Cargo.lock 🔗
@@ -3391,13 +3391,11 @@ dependencies = [
"ctor",
"editor",
"env_logger",
- "feature_flags",
"futures 0.3.28",
"gpui",
"language",
"log",
"lsp",
- "multi_buffer",
"pretty_assertions",
"project",
"rand 0.8.5",
Antonio Scandurra , Nathan , and Max created
Release Notes:
- N/A
---------
Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Max <max@zed.dev>
Cargo.lock | 2
crates/assistant/src/assistant_panel.rs | 26
crates/assistant/src/inline_assistant.rs | 72
crates/diagnostics/Cargo.toml | 2
crates/diagnostics/src/diagnostics.rs | 9
crates/diagnostics/src/grouped_diagnostics.rs | 1403 ---------------------
crates/diagnostics/src/toolbar_controls.rs | 85
crates/editor/src/display_map.rs | 61
crates/editor/src/display_map/block_map.rs | 96
crates/editor/src/editor.rs | 40
crates/editor/src/element.rs | 148 +
crates/editor/src/hunk_diff.rs | 14
crates/editor/src/scroll.rs | 4
crates/editor/src/scroll/autoscroll.rs | 4
crates/feature_flags/src/feature_flags.rs | 5
crates/gpui/src/window.rs | 6
crates/repl/src/outputs.rs | 37
crates/repl/src/session.rs | 24
crates/repl/src/stdio.rs | 4
19 files changed, 266 insertions(+), 1,776 deletions(-)
@@ -3391,13 +3391,11 @@ dependencies = [
"ctor",
"editor",
"env_logger",
- "feature_flags",
"futures 0.3.28",
"gpui",
"language",
"log",
"lsp",
- "multi_buffer",
"pretty_assertions",
"project",
"rand 0.8.5",
@@ -1917,18 +1917,20 @@ impl ContextEditor {
cx.update(|cx| {
for suggestion in suggestion_group.suggestions {
let description = suggestion.description.unwrap_or_else(|| "Delete".into());
+
let range = {
- let buffer = editor.read(cx).buffer().read(cx).read(cx);
- let (&excerpt_id, _, _) = buffer.as_singleton().unwrap();
- buffer
+ let multibuffer = editor.read(cx).buffer().read(cx).read(cx);
+ let (&excerpt_id, _, _) = multibuffer.as_singleton().unwrap();
+ multibuffer
.anchor_in_excerpt(excerpt_id, suggestion.range.start)
.unwrap()
- ..buffer
+ ..multibuffer
.anchor_in_excerpt(excerpt_id, suggestion.range.end)
.unwrap()
};
+
InlineAssistant::update_global(cx, |assistant, cx| {
- assist_ids.push(assistant.suggest_assist(
+ let suggestion_id = assistant.suggest_assist(
&editor,
range,
description,
@@ -1936,16 +1938,20 @@ impl ContextEditor {
Some(workspace.clone()),
assistant_panel.upgrade().as_ref(),
cx,
- ));
+ );
+ assist_ids.push(suggestion_id);
});
}
// Scroll the editor to the suggested assist
editor.update(cx, |editor, cx| {
- let anchor = {
- let buffer = editor.buffer().read(cx).read(cx);
- let (&excerpt_id, _, _) = buffer.as_singleton().unwrap();
- buffer
+ let multibuffer = editor.buffer().read(cx).snapshot(cx);
+ let (&excerpt_id, _, buffer) = multibuffer.as_singleton().unwrap();
+ let anchor = if suggestion_group.context_range.start.to_offset(buffer) == 0
+ {
+ Anchor::min()
+ } else {
+ multibuffer
.anchor_in_excerpt(excerpt_id, suggestion_group.context_range.start)
.unwrap()
};
@@ -331,11 +331,16 @@ impl InlineAssistant {
prompt_editor: &View<PromptEditor>,
cx: &mut WindowContext,
) -> [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![
BlockProperties {
style: BlockStyle::Sticky,
position: range.start,
- height: prompt_editor.read(cx).height_in_lines,
+ height: prompt_editor_height,
render: build_assist_editor_renderer(prompt_editor),
disposition: BlockDisposition::Above,
},
@@ -446,9 +451,6 @@ impl InlineAssistant {
PromptEditorEvent::DismissRequested => {
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(
&mut self,
assist_group_id: InlineAssistGroupId,
@@ -1029,8 +1004,8 @@ impl InlineAssistant {
editor
});
- let height = deleted_lines_editor
- .update(cx, |editor, cx| editor.max_point(cx).row().0 as u8 + 1);
+ let height =
+ deleted_lines_editor.update(cx, |editor, cx| editor.max_point(cx).row().0 + 1);
new_blocks.push(BlockProperties {
position: new_row,
height,
@@ -1194,13 +1169,11 @@ enum PromptEditorEvent {
ConfirmRequested,
CancelRequested,
DismissRequested,
- Resized { height_in_lines: u8 },
}
struct PromptEditor {
id: InlineAssistId,
fs: Arc<dyn Fs>,
- height_in_lines: u8,
editor: View<Editor>,
edited_since_done: bool,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -1307,9 +1280,8 @@ impl Render for PromptEditor {
.bg(cx.theme().colors().editor_background)
.border_y_1()
.border_color(cx.theme().status().info_border)
- .py_1p5()
- .h_full()
- .w_full()
+ .size_full()
+ .py(cx.line_height() / 2.)
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::move_up))
@@ -1427,7 +1399,6 @@ impl PromptEditor {
let mut this = Self {
id,
- height_in_lines: 1,
editor: prompt_editor,
edited_since_done: false,
gutter_dimensions,
@@ -1443,7 +1414,6 @@ impl PromptEditor {
_token_count_subscriptions: token_count_subscriptions,
workspace,
};
- this.count_lines(cx);
this.count_tokens(cx);
this.subscribe_to_editor(cx);
this
@@ -1451,8 +1421,6 @@ impl PromptEditor {
fn subscribe_to_editor(&mut self, cx: &mut ViewContext<Self>) {
self.editor_subscriptions.clear();
- self.editor_subscriptions
- .push(cx.observe(&self.editor, Self::handle_prompt_editor_changed));
self.editor_subscriptions
.push(cx.subscribe(&self.editor, Self::handle_prompt_editor_events));
}
@@ -1487,22 +1455,6 @@ impl PromptEditor {
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(
&mut self,
_: 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(
&mut self,
_: View<Editor>,
@@ -18,13 +18,11 @@ collections.workspace = true
ctor.workspace = true
editor.workspace = true
env_logger.workspace = true
-feature_flags.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true
-multi_buffer.workspace = true
project.workspace = true
rand.workspace = true
schemars.workspace = true
@@ -4,7 +4,6 @@ mod toolbar_controls;
#[cfg(test)]
mod diagnostics_tests;
-pub(crate) mod grouped_diagnostics;
use anyhow::Result;
use collections::{BTreeSet, HashSet};
@@ -15,7 +14,6 @@ use editor::{
scroll::Autoscroll,
Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
};
-use feature_flags::FeatureFlagAppExt;
use futures::{
channel::mpsc::{self, UnboundedSender},
StreamExt as _,
@@ -54,9 +52,6 @@ pub fn init(cx: &mut AppContext) {
ProjectDiagnosticsSettings::register(cx);
cx.observe_new_views(ProjectDiagnosticsEditor::register)
.detach();
- if !cx.has_flag::<feature_flags::GroupedDiagnostics>() {
- grouped_diagnostics::init(cx);
- }
}
struct ProjectDiagnosticsEditor {
@@ -469,7 +464,7 @@ impl ProjectDiagnosticsEditor {
group_state.block_count += 1;
blocks_to_add.push(BlockProperties {
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,
render: diagnostic_block_renderer(
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();
h_flex()
.id(DIAGNOSTIC_HEADER)
- .py_2()
+ .h(2. * cx.line_height())
.pl_10()
.pr_5()
.w_full()
@@ -1,1403 +0,0 @@
-use anyhow::Result;
-use collections::{BTreeMap, BTreeSet, HashMap, HashSet};
-use editor::{
- diagnostic_block_renderer,
- display_map::{
- BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, CustomBlockId,
- RenderBlock,
- },
- scroll::Autoscroll,
- Bias, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToPoint,
-};
-use futures::{
- channel::mpsc::{self, UnboundedSender},
- StreamExt as _,
-};
-use gpui::{
- actions, div, AnyElement, AnyView, AppContext, Context, EventEmitter, FocusHandle,
- FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render, SharedString,
- Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
-};
-use language::{
- Buffer, BufferSnapshot, DiagnosticEntry, DiagnosticSeverity, OffsetRangeExt, ToOffset,
- ToPoint as _,
-};
-use lsp::LanguageServerId;
-use multi_buffer::{build_excerpt_ranges, ExpandExcerptDirection, MultiBufferRow};
-use project::{DiagnosticSummary, Project, ProjectPath};
-use settings::Settings;
-use std::{
- any::{Any, TypeId},
- cmp::Ordering,
- ops::Range,
- sync::{
- atomic::{self, AtomicBool},
- Arc,
- },
-};
-use theme::ActiveTheme;
-use ui::{h_flex, prelude::*, Icon, IconName, Label};
-use util::{debug_panic, ResultExt};
-use workspace::{
- item::{BreadcrumbText, Item, ItemEvent, ItemHandle, TabContentParams},
- ItemNavHistory, ToolbarItemLocation, Workspace,
-};
-
-use crate::project_diagnostics_settings::ProjectDiagnosticsSettings;
-actions!(grouped_diagnostics, [Deploy, ToggleWarnings]);
-
-pub fn init(cx: &mut AppContext) {
- cx.observe_new_views(GroupedDiagnosticsEditor::register)
- .detach();
-}
-
-pub struct GroupedDiagnosticsEditor {
- pub project: Model<Project>,
- workspace: WeakView<Workspace>,
- focus_handle: FocusHandle,
- editor: View<Editor>,
- summary: DiagnosticSummary,
- excerpts: Model<MultiBuffer>,
- path_states: Vec<PathState>,
- pub paths_to_update: BTreeSet<(ProjectPath, LanguageServerId)>,
- pub include_warnings: bool,
- context: u32,
- pub update_paths_tx: UnboundedSender<(ProjectPath, Option<LanguageServerId>)>,
- _update_excerpts_task: Task<Result<()>>,
- _subscription: Subscription,
-}
-
-struct PathState {
- path: ProjectPath,
- first_excerpt_id: Option<ExcerptId>,
- last_excerpt_id: Option<ExcerptId>,
- diagnostics: Vec<(DiagnosticData, CustomBlockId)>,
-}
-
-#[derive(Debug, Clone)]
-struct DiagnosticData {
- language_server_id: LanguageServerId,
- is_primary: bool,
- entry: DiagnosticEntry<language::Anchor>,
-}
-
-impl DiagnosticData {
- fn diagnostic_entries_equal(&self, other: &DiagnosticData) -> bool {
- self.language_server_id == other.language_server_id
- && self.is_primary == other.is_primary
- && self.entry.range == other.entry.range
- && equal_without_group_ids(&self.entry.diagnostic, &other.entry.diagnostic)
- }
-}
-
-// `group_id` can differ between LSP server diagnostics output,
-// hence ignore it when checking diagnostics for updates.
-fn equal_without_group_ids(a: &language::Diagnostic, b: &language::Diagnostic) -> bool {
- a.source == b.source
- && a.code == b.code
- && a.severity == b.severity
- && a.message == b.message
- && a.is_primary == b.is_primary
- && a.is_disk_based == b.is_disk_based
- && a.is_unnecessary == b.is_unnecessary
-}
-
-impl EventEmitter<EditorEvent> for GroupedDiagnosticsEditor {}
-
-impl Render for GroupedDiagnosticsEditor {
- fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
- let child = if self.path_states.is_empty() {
- div()
- .bg(cx.theme().colors().editor_background)
- .flex()
- .items_center()
- .justify_center()
- .size_full()
- .child(Label::new("No problems in workspace"))
- } else {
- div().size_full().child(self.editor.clone())
- };
-
- div()
- .track_focus(&self.focus_handle)
- .when(self.path_states.is_empty(), |el| {
- el.key_context("EmptyPane")
- })
- .size_full()
- .on_action(cx.listener(Self::toggle_warnings))
- .child(child)
- }
-}
-
-impl GroupedDiagnosticsEditor {
- fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
- workspace.register_action(Self::deploy);
- }
-
- fn new_with_context(
- context: u32,
- project_handle: Model<Project>,
- workspace: WeakView<Workspace>,
- cx: &mut ViewContext<Self>,
- ) -> Self {
- let project_event_subscription =
- cx.subscribe(&project_handle, |this, project, event, cx| match event {
- project::Event::DiskBasedDiagnosticsStarted { .. } => {
- cx.notify();
- }
- project::Event::DiskBasedDiagnosticsFinished { language_server_id } => {
- log::debug!("disk based diagnostics finished for server {language_server_id}");
- this.enqueue_update_stale_excerpts(Some(*language_server_id));
- }
- project::Event::DiagnosticsUpdated {
- language_server_id,
- path,
- } => {
- this.paths_to_update
- .insert((path.clone(), *language_server_id));
- this.summary = project.read(cx).diagnostic_summary(false, cx);
- cx.emit(EditorEvent::TitleChanged);
-
- if this.editor.focus_handle(cx).contains_focused(cx) || this.focus_handle.contains_focused(cx) {
- log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. recording change");
- } else {
- log::debug!("diagnostics updated for server {language_server_id}, path {path:?}. updating excerpts");
- this.enqueue_update_stale_excerpts(Some(*language_server_id));
- }
- }
- _ => {}
- });
-
- let focus_handle = cx.focus_handle();
- cx.on_focus_in(&focus_handle, |this, cx| this.focus_in(cx))
- .detach();
- cx.on_focus_out(&focus_handle, |this, _event, cx| this.focus_out(cx))
- .detach();
-
- let excerpts = cx.new_model(|cx| {
- MultiBuffer::new(
- project_handle.read(cx).replica_id(),
- project_handle.read(cx).capability(),
- )
- });
- let editor = cx.new_view(|cx| {
- let mut editor =
- Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), false, cx);
- editor.set_vertical_scroll_margin(5, cx);
- editor
- });
- cx.subscribe(&editor, |this, _editor, event: &EditorEvent, cx| {
- cx.emit(event.clone());
- match event {
- EditorEvent::Focused => {
- if this.path_states.is_empty() {
- cx.focus(&this.focus_handle);
- }
- }
- EditorEvent::Blurred => this.enqueue_update_stale_excerpts(None),
- _ => {}
- }
- })
- .detach();
-
- let (update_excerpts_tx, mut update_excerpts_rx) = mpsc::unbounded();
-
- let project = project_handle.read(cx);
- let mut this = Self {
- project: project_handle.clone(),
- context,
- summary: project.diagnostic_summary(false, cx),
- workspace,
- excerpts,
- focus_handle,
- editor,
- path_states: Vec::new(),
- paths_to_update: BTreeSet::new(),
- include_warnings: ProjectDiagnosticsSettings::get_global(cx).include_warnings,
- update_paths_tx: update_excerpts_tx,
- _update_excerpts_task: cx.spawn(move |this, mut cx| async move {
- while let Some((path, language_server_id)) = update_excerpts_rx.next().await {
- if let Some(buffer) = project_handle
- .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))?
- .await
- .log_err()
- {
- this.update(&mut cx, |this, cx| {
- this.update_excerpts(path, language_server_id, buffer, cx);
- })?;
- }
- }
- anyhow::Ok(())
- }),
- _subscription: project_event_subscription,
- };
- this.enqueue_update_all_excerpts(cx);
- this
- }
-
- fn new(
- project_handle: Model<Project>,
- workspace: WeakView<Workspace>,
- cx: &mut ViewContext<Self>,
- ) -> Self {
- Self::new_with_context(
- editor::DEFAULT_MULTIBUFFER_CONTEXT,
- project_handle,
- workspace,
- cx,
- )
- }
-
- fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
- if let Some(existing) = workspace.item_of_type::<GroupedDiagnosticsEditor>(cx) {
- workspace.activate_item(&existing, true, true, cx);
- } else {
- let workspace_handle = cx.view().downgrade();
- let diagnostics = cx.new_view(|cx| {
- GroupedDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
- });
- workspace.add_item_to_active_pane(Box::new(diagnostics), None, true, cx);
- }
- }
-
- pub fn toggle_warnings(&mut self, _: &ToggleWarnings, cx: &mut ViewContext<Self>) {
- self.include_warnings = !self.include_warnings;
- self.enqueue_update_all_excerpts(cx);
- cx.notify();
- }
-
- fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
- if self.focus_handle.is_focused(cx) && !self.path_states.is_empty() {
- self.editor.focus_handle(cx).focus(cx)
- }
- }
-
- fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
- if !self.focus_handle.is_focused(cx) && !self.editor.focus_handle(cx).is_focused(cx) {
- self.enqueue_update_stale_excerpts(None);
- }
- }
-
- /// Enqueue an update of all excerpts. Updates all paths that either
- /// currently have diagnostics or are currently present in this view.
- fn enqueue_update_all_excerpts(&mut self, cx: &mut ViewContext<Self>) {
- self.project.update(cx, |project, cx| {
- let mut paths = project
- .diagnostic_summaries(false, cx)
- .map(|(path, _, _)| path)
- .collect::<BTreeSet<_>>();
- paths.extend(self.path_states.iter().map(|state| state.path.clone()));
- for path in paths {
- self.update_paths_tx.unbounded_send((path, None)).unwrap();
- }
- });
- }
-
- /// Enqueue an update of the excerpts for any path whose diagnostics are known
- /// to have changed. If a language server id is passed, then only the excerpts for
- /// that language server's diagnostics will be updated. Otherwise, all stale excerpts
- /// will be refreshed.
- pub fn enqueue_update_stale_excerpts(&mut self, language_server_id: Option<LanguageServerId>) {
- for (path, server_id) in &self.paths_to_update {
- if language_server_id.map_or(true, |id| id == *server_id) {
- self.update_paths_tx
- .unbounded_send((path.clone(), Some(*server_id)))
- .unwrap();
- }
- }
- }
-
- fn update_excerpts(
- &mut self,
- path_to_update: ProjectPath,
- server_to_update: Option<LanguageServerId>,
- buffer: Model<Buffer>,
- cx: &mut ViewContext<Self>,
- ) {
- self.paths_to_update.retain(|(path, server_id)| {
- *path != path_to_update
- || server_to_update.map_or(false, |to_update| *server_id != to_update)
- });
-
- // TODO change selections as in the old panel, to the next primary diagnostics
- // TODO make [shift-]f8 to work, jump to the next block group
- let _was_empty = self.path_states.is_empty();
- let path_ix = match self.path_states.binary_search_by(|probe| {
- project::compare_paths((&probe.path.path, true), (&path_to_update.path, true))
- }) {
- Ok(ix) => ix,
- Err(ix) => {
- self.path_states.insert(
- ix,
- PathState {
- path: path_to_update.clone(),
- diagnostics: Vec::new(),
- last_excerpt_id: None,
- first_excerpt_id: None,
- },
- );
- ix
- }
- };
-
- let max_severity = if self.include_warnings {
- DiagnosticSeverity::WARNING
- } else {
- DiagnosticSeverity::ERROR
- };
-
- let excerpt_borders = self.excerpt_borders_for_path(path_ix);
- let path_state = &mut self.path_states[path_ix];
- let buffer_snapshot = buffer.read(cx).snapshot();
-
- let mut path_update = PathUpdate::new(
- excerpt_borders,
- &buffer_snapshot,
- server_to_update,
- max_severity,
- path_state,
- );
- path_update.prepare_excerpt_data(
- self.context,
- self.excerpts.read(cx).snapshot(cx),
- buffer.read(cx).snapshot(),
- path_state.diagnostics.iter(),
- );
- self.excerpts.update(cx, |multi_buffer, cx| {
- path_update.apply_excerpt_changes(
- path_state,
- self.context,
- buffer_snapshot,
- multi_buffer,
- buffer,
- cx,
- );
- });
-
- let new_multi_buffer_snapshot = self.excerpts.read(cx).snapshot(cx);
- let blocks_to_insert =
- path_update.prepare_blocks_to_insert(self.editor.clone(), new_multi_buffer_snapshot);
-
- let new_block_ids = self.editor.update(cx, |editor, cx| {
- editor.remove_blocks(std::mem::take(&mut path_update.blocks_to_remove), None, cx);
- editor.insert_blocks(blocks_to_insert, Some(Autoscroll::fit()), cx)
- });
- path_state.diagnostics = path_update.new_blocks(new_block_ids);
-
- if self.path_states.is_empty() {
- if self.editor.focus_handle(cx).is_focused(cx) {
- cx.focus(&self.focus_handle);
- }
- } else if self.focus_handle.is_focused(cx) {
- let focus_handle = self.editor.focus_handle(cx);
- cx.focus(&focus_handle);
- }
-
- #[cfg(test)]
- self.check_invariants(cx);
-
- cx.notify();
- }
-
- fn excerpt_borders_for_path(&self, path_ix: usize) -> (Option<ExcerptId>, Option<ExcerptId>) {
- let previous_path_state_ix =
- Some(path_ix.saturating_sub(1)).filter(|&previous_path_ix| previous_path_ix != path_ix);
- let next_path_state_ix = path_ix + 1;
- let start = previous_path_state_ix.and_then(|i| {
- self.path_states[..=i]
- .iter()
- .rev()
- .find_map(|state| state.last_excerpt_id)
- });
- let end = self.path_states[next_path_state_ix..]
- .iter()
- .find_map(|state| state.first_excerpt_id);
- (start, end)
- }
-
- #[cfg(test)]
- fn check_invariants(&self, cx: &mut ViewContext<Self>) {
- let mut excerpts = Vec::new();
- for (id, buffer, _) in self.excerpts.read(cx).snapshot(cx).excerpts() {
- if let Some(file) = buffer.file() {
- excerpts.push((id, file.path().clone()));
- }
- }
-
- let mut prev_path = None;
- for (_, path) in &excerpts {
- if let Some(prev_path) = prev_path {
- if path < prev_path {
- panic!("excerpts are not sorted by path {:?}", excerpts);
- }
- }
- prev_path = Some(path);
- }
- }
-}
-
-impl FocusableView for GroupedDiagnosticsEditor {
- fn focus_handle(&self, _: &AppContext) -> FocusHandle {
- self.focus_handle.clone()
- }
-}
-
-impl Item for GroupedDiagnosticsEditor {
- type Event = EditorEvent;
-
- fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
- Editor::to_item_events(event, f)
- }
-
- fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
- self.editor.update(cx, |editor, cx| editor.deactivated(cx));
- }
-
- fn navigate(&mut self, data: Box<dyn Any>, cx: &mut ViewContext<Self>) -> bool {
- self.editor
- .update(cx, |editor, cx| editor.navigate(data, cx))
- }
-
- fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
- Some("Project Diagnostics".into())
- }
-
- fn tab_content(&self, params: TabContentParams, _: &WindowContext) -> AnyElement {
- if self.summary.error_count == 0 && self.summary.warning_count == 0 {
- Label::new("No problems")
- .color(params.text_color())
- .into_any_element()
- } else {
- h_flex()
- .gap_1()
- .when(self.summary.error_count > 0, |then| {
- then.child(
- h_flex()
- .gap_1()
- .child(Icon::new(IconName::XCircle).color(Color::Error))
- .child(
- Label::new(self.summary.error_count.to_string())
- .color(params.text_color()),
- ),
- )
- })
- .when(self.summary.warning_count > 0, |then| {
- then.child(
- h_flex()
- .gap_1()
- .child(Icon::new(IconName::ExclamationTriangle).color(Color::Warning))
- .child(
- Label::new(self.summary.warning_count.to_string())
- .color(params.text_color()),
- ),
- )
- })
- .into_any_element()
- }
- }
-
- fn telemetry_event_text(&self) -> Option<&'static str> {
- Some("project diagnostics")
- }
-
- fn for_each_project_item(
- &self,
- cx: &AppContext,
- f: &mut dyn FnMut(gpui::EntityId, &dyn project::Item),
- ) {
- self.editor.for_each_project_item(cx, f)
- }
-
- fn is_singleton(&self, _: &AppContext) -> bool {
- false
- }
-
- fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
- self.editor.update(cx, |editor, _| {
- editor.set_nav_history(Some(nav_history));
- });
- }
-
- fn clone_on_split(
- &self,
- _workspace_id: Option<workspace::WorkspaceId>,
- cx: &mut ViewContext<Self>,
- ) -> Option<View<Self>>
- where
- Self: Sized,
- {
- Some(cx.new_view(|cx| {
- GroupedDiagnosticsEditor::new(self.project.clone(), self.workspace.clone(), cx)
- }))
- }
-
- fn is_dirty(&self, cx: &AppContext) -> bool {
- self.excerpts.read(cx).is_dirty(cx)
- }
-
- fn has_conflict(&self, cx: &AppContext) -> bool {
- self.excerpts.read(cx).has_conflict(cx)
- }
-
- fn can_save(&self, _: &AppContext) -> bool {
- true
- }
-
- fn save(
- &mut self,
- format: bool,
- project: Model<Project>,
- cx: &mut ViewContext<Self>,
- ) -> Task<Result<()>> {
- self.editor.save(format, project, cx)
- }
-
- fn save_as(
- &mut self,
- _: Model<Project>,
- _: ProjectPath,
- _: &mut ViewContext<Self>,
- ) -> Task<Result<()>> {
- unreachable!()
- }
-
- fn reload(&mut self, project: Model<Project>, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
- self.editor.reload(project, cx)
- }
-
- fn act_as_type<'a>(
- &'a self,
- type_id: TypeId,
- self_handle: &'a View<Self>,
- _: &'a AppContext,
- ) -> Option<AnyView> {
- if type_id == TypeId::of::<Self>() {
- Some(self_handle.to_any())
- } else if type_id == TypeId::of::<Editor>() {
- Some(self.editor.to_any())
- } else {
- None
- }
- }
-
- fn breadcrumb_location(&self) -> ToolbarItemLocation {
- ToolbarItemLocation::PrimaryLeft
- }
-
- fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
- self.editor.breadcrumbs(theme, cx)
- }
-
- fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
- self.editor
- .update(cx, |editor, cx| editor.added_to_workspace(workspace, cx));
- }
-}
-
-fn compare_data_locations(
- old: &DiagnosticData,
- new: &DiagnosticData,
- snapshot: &BufferSnapshot,
-) -> Ordering {
- compare_diagnostics(&old.entry, &new.entry, snapshot)
- .then_with(|| old.language_server_id.cmp(&new.language_server_id))
-}
-
-fn compare_diagnostics(
- old: &DiagnosticEntry<language::Anchor>,
- new: &DiagnosticEntry<language::Anchor>,
- snapshot: &BufferSnapshot,
-) -> Ordering {
- compare_diagnostic_ranges(&old.range, &new.range, snapshot)
- .then_with(|| old.diagnostic.message.cmp(&new.diagnostic.message))
-}
-
-fn compare_diagnostic_ranges(
- old: &Range<language::Anchor>,
- new: &Range<language::Anchor>,
- snapshot: &BufferSnapshot,
-) -> Ordering {
- // The diagnostics may point to a previously open Buffer for this file.
- if !old.start.is_valid(snapshot) || !new.start.is_valid(snapshot) {
- return Ordering::Greater;
- }
-
- old.start
- .to_offset(snapshot)
- .cmp(&new.start.to_offset(snapshot))
- .then_with(|| {
- old.end
- .to_offset(snapshot)
- .cmp(&new.end.to_offset(snapshot))
- })
-}
-
-// TODO wrong? What to do here instead?
-fn compare_diagnostic_range_edges(
- old: &Range<language::Anchor>,
- new: &Range<language::Anchor>,
- snapshot: &BufferSnapshot,
-) -> (Ordering, Ordering) {
- // The diagnostics may point to a previously open Buffer for this file.
- let start_cmp = match (old.start.is_valid(snapshot), new.start.is_valid(snapshot)) {
- (false, false) => old.start.offset.cmp(&new.start.offset),
- (false, true) => Ordering::Greater,
- (true, false) => Ordering::Less,
- (true, true) => old.start.cmp(&new.start, snapshot),
- };
-
- let end_cmp = old
- .end
- .to_offset(snapshot)
- .cmp(&new.end.to_offset(snapshot));
- (start_cmp, end_cmp)
-}
-
-#[derive(Debug)]
-struct PathUpdate {
- path_excerpts_borders: (Option<ExcerptId>, Option<ExcerptId>),
- latest_excerpt_id: ExcerptId,
- new_diagnostics: Vec<(DiagnosticData, Option<CustomBlockId>)>,
- diagnostics_by_row_label: BTreeMap<MultiBufferRow, (editor::Anchor, Vec<usize>)>,
- blocks_to_remove: HashSet<CustomBlockId>,
- unchanged_blocks: HashMap<usize, CustomBlockId>,
- excerpts_with_new_diagnostics: HashSet<ExcerptId>,
- excerpts_to_remove: Vec<ExcerptId>,
- excerpt_expands: HashMap<(ExpandExcerptDirection, u32), Vec<ExcerptId>>,
- excerpts_to_add: HashMap<ExcerptId, Vec<Range<language::Anchor>>>,
- first_excerpt_id: Option<ExcerptId>,
- last_excerpt_id: Option<ExcerptId>,
-}
-
-impl PathUpdate {
- fn new(
- path_excerpts_borders: (Option<ExcerptId>, Option<ExcerptId>),
- buffer_snapshot: &BufferSnapshot,
- server_to_update: Option<LanguageServerId>,
- max_severity: DiagnosticSeverity,
- path_state: &PathState,
- ) -> Self {
- let mut blocks_to_remove = HashSet::default();
- let mut removed_groups = HashSet::default();
- let mut new_diagnostics = path_state
- .diagnostics
- .iter()
- .filter(|(diagnostic_data, _)| {
- server_to_update.map_or(true, |server_id| {
- diagnostic_data.language_server_id != server_id
- })
- })
- .filter(|(diagnostic_data, block_id)| {
- let diagnostic = &diagnostic_data.entry.diagnostic;
- let retain = !diagnostic.is_primary || diagnostic.severity <= max_severity;
- if !retain {
- removed_groups.insert(diagnostic.group_id);
- blocks_to_remove.insert(*block_id);
- }
- retain
- })
- .map(|(diagnostic, block_id)| (diagnostic.clone(), Some(*block_id)))
- .collect::<Vec<_>>();
- new_diagnostics.retain(|(diagnostic_data, block_id)| {
- let retain = !removed_groups.contains(&diagnostic_data.entry.diagnostic.group_id);
- if !retain {
- if let Some(block_id) = block_id {
- blocks_to_remove.insert(*block_id);
- }
- }
- retain
- });
- for (server_id, group) in buffer_snapshot
- .diagnostic_groups(server_to_update)
- .into_iter()
- .filter(|(_, group)| {
- group.entries[group.primary_ix].diagnostic.severity <= max_severity
- })
- {
- for (diagnostic_index, diagnostic) in group.entries.iter().enumerate() {
- let new_data = DiagnosticData {
- language_server_id: server_id,
- is_primary: diagnostic_index == group.primary_ix,
- entry: diagnostic.clone(),
- };
- let (Ok(i) | Err(i)) = new_diagnostics.binary_search_by(|probe| {
- compare_data_locations(&probe.0, &new_data, &buffer_snapshot)
- });
- new_diagnostics.insert(i, (new_data, None));
- }
- }
-
- let latest_excerpt_id = path_excerpts_borders.0.unwrap_or_else(|| ExcerptId::min());
- Self {
- latest_excerpt_id,
- path_excerpts_borders,
- new_diagnostics,
- blocks_to_remove,
- diagnostics_by_row_label: BTreeMap::new(),
- excerpts_to_remove: Vec::new(),
- excerpts_with_new_diagnostics: HashSet::default(),
- unchanged_blocks: HashMap::default(),
- excerpts_to_add: HashMap::default(),
- excerpt_expands: HashMap::default(),
- first_excerpt_id: None,
- last_excerpt_id: None,
- }
- }
-
- fn prepare_excerpt_data<'a>(
- &'a mut self,
- context: u32,
- multi_buffer_snapshot: MultiBufferSnapshot,
- buffer_snapshot: BufferSnapshot,
- current_diagnostics: impl Iterator<Item = &'a (DiagnosticData, CustomBlockId)> + 'a,
- ) {
- let mut current_diagnostics = current_diagnostics.fuse().peekable();
- let mut excerpts_to_expand =
- HashMap::<ExcerptId, HashMap<ExpandExcerptDirection, u32>>::default();
- let mut current_excerpts = path_state_excerpts(
- self.path_excerpts_borders.0,
- self.path_excerpts_borders.1,
- &multi_buffer_snapshot,
- )
- .fuse()
- .peekable();
-
- for (diagnostic_index, (new_diagnostic, existing_block)) in
- self.new_diagnostics.iter().enumerate()
- {
- if let Some(existing_block) = existing_block {
- self.unchanged_blocks
- .insert(diagnostic_index, *existing_block);
- }
-
- loop {
- match current_excerpts.peek() {
- None => {
- let excerpt_ranges = self
- .excerpts_to_add
- .entry(self.latest_excerpt_id)
- .or_default();
- let new_range = new_diagnostic.entry.range.clone();
- let (Ok(i) | Err(i)) = excerpt_ranges.binary_search_by(|probe| {
- compare_diagnostic_ranges(probe, &new_range, &buffer_snapshot)
- });
- excerpt_ranges.insert(i, new_range);
- break;
- }
- Some((current_excerpt_id, _, current_excerpt_range)) => {
- match compare_diagnostic_range_edges(
- ¤t_excerpt_range.context,
- &new_diagnostic.entry.range,
- &buffer_snapshot,
- ) {
- /*
- new_s new_e
- ----[---->><<----]--
- cur_s cur_e
- */
- (
- Ordering::Less | Ordering::Equal,
- Ordering::Greater | Ordering::Equal,
- ) => {
- self.excerpts_with_new_diagnostics
- .insert(*current_excerpt_id);
- if self.first_excerpt_id.is_none() {
- self.first_excerpt_id = Some(*current_excerpt_id);
- }
- self.last_excerpt_id = Some(*current_excerpt_id);
- break;
- }
- /*
- cur_s cur_e
- ---->>>>>[--]<<<<<--
- new_s new_e
- */
- (
- Ordering::Greater | Ordering::Equal,
- Ordering::Less | Ordering::Equal,
- ) => {
- let expand_up = current_excerpt_range
- .context
- .start
- .to_point(&buffer_snapshot)
- .row
- .saturating_sub(
- new_diagnostic
- .entry
- .range
- .start
- .to_point(&buffer_snapshot)
- .row,
- );
- let expand_down = new_diagnostic
- .entry
- .range
- .end
- .to_point(&buffer_snapshot)
- .row
- .saturating_sub(
- current_excerpt_range
- .context
- .end
- .to_point(&buffer_snapshot)
- .row,
- );
- let expand_value = excerpts_to_expand
- .entry(*current_excerpt_id)
- .or_default()
- .entry(ExpandExcerptDirection::UpAndDown)
- .or_default();
- *expand_value = (*expand_value).max(expand_up).max(expand_down);
- self.excerpts_with_new_diagnostics
- .insert(*current_excerpt_id);
- if self.first_excerpt_id.is_none() {
- self.first_excerpt_id = Some(*current_excerpt_id);
- }
- self.last_excerpt_id = Some(*current_excerpt_id);
- break;
- }
- /*
- new_s new_e
- > <
- ----[---->>>]<<<<<--
- cur_s cur_e
-
- or
- new_s new_e
- > <
- ----[----]-->>><<<--
- cur_s cur_e
- */
- (Ordering::Less, Ordering::Less) => {
- if current_excerpt_range
- .context
- .end
- .cmp(&new_diagnostic.entry.range.start, &buffer_snapshot)
- .is_ge()
- {
- let expand_down = new_diagnostic
- .entry
- .range
- .end
- .to_point(&buffer_snapshot)
- .row
- .saturating_sub(
- current_excerpt_range
- .context
- .end
- .to_point(&buffer_snapshot)
- .row,
- );
- let expand_value = excerpts_to_expand
- .entry(*current_excerpt_id)
- .or_default()
- .entry(ExpandExcerptDirection::Down)
- .or_default();
- *expand_value = (*expand_value).max(expand_down);
- self.excerpts_with_new_diagnostics
- .insert(*current_excerpt_id);
- if self.first_excerpt_id.is_none() {
- self.first_excerpt_id = Some(*current_excerpt_id);
- }
- self.last_excerpt_id = Some(*current_excerpt_id);
- break;
- } else if !self
- .excerpts_with_new_diagnostics
- .contains(current_excerpt_id)
- {
- self.excerpts_to_remove.push(*current_excerpt_id);
- }
- }
- /*
- cur_s cur_e
- ---->>>>>[<<<<----]--
- > <
- new_s new_e
-
- or
- cur_s cur_e
- ---->>><<<--[----]--
- > <
- new_s new_e
- */
- (Ordering::Greater, Ordering::Greater) => {
- if current_excerpt_range
- .context
- .start
- .cmp(&new_diagnostic.entry.range.end, &buffer_snapshot)
- .is_le()
- {
- let expand_up = current_excerpt_range
- .context
- .start
- .to_point(&buffer_snapshot)
- .row
- .saturating_sub(
- new_diagnostic
- .entry
- .range
- .start
- .to_point(&buffer_snapshot)
- .row,
- );
- let expand_value = excerpts_to_expand
- .entry(*current_excerpt_id)
- .or_default()
- .entry(ExpandExcerptDirection::Up)
- .or_default();
- *expand_value = (*expand_value).max(expand_up);
- self.excerpts_with_new_diagnostics
- .insert(*current_excerpt_id);
- if self.first_excerpt_id.is_none() {
- self.first_excerpt_id = Some(*current_excerpt_id);
- }
- self.last_excerpt_id = Some(*current_excerpt_id);
- break;
- } else {
- let excerpt_ranges = self
- .excerpts_to_add
- .entry(self.latest_excerpt_id)
- .or_default();
- let new_range = new_diagnostic.entry.range.clone();
- let (Ok(i) | Err(i)) =
- excerpt_ranges.binary_search_by(|probe| {
- compare_diagnostic_ranges(
- probe,
- &new_range,
- &buffer_snapshot,
- )
- });
- excerpt_ranges.insert(i, new_range);
- break;
- }
- }
- }
- if let Some((next_id, ..)) = current_excerpts.next() {
- self.latest_excerpt_id = next_id;
- }
- }
- }
- }
-
- loop {
- match current_diagnostics.peek() {
- None => break,
- Some((current_diagnostic, current_block)) => {
- match compare_data_locations(
- current_diagnostic,
- new_diagnostic,
- &buffer_snapshot,
- ) {
- Ordering::Less => {
- self.blocks_to_remove.insert(*current_block);
- }
- Ordering::Equal => {
- if current_diagnostic.diagnostic_entries_equal(&new_diagnostic) {
- self.unchanged_blocks
- .insert(diagnostic_index, *current_block);
- } else {
- self.blocks_to_remove.insert(*current_block);
- }
- let _ = current_diagnostics.next();
- break;
- }
- Ordering::Greater => break,
- }
- let _ = current_diagnostics.next();
- }
- }
- }
- }
-
- self.excerpts_to_remove.retain(|excerpt_id| {
- !self.excerpts_with_new_diagnostics.contains(excerpt_id)
- && !excerpts_to_expand.contains_key(excerpt_id)
- });
- self.excerpts_to_remove.extend(
- current_excerpts
- .filter(|(excerpt_id, ..)| {
- !self.excerpts_with_new_diagnostics.contains(excerpt_id)
- && !excerpts_to_expand.contains_key(excerpt_id)
- })
- .map(|(excerpt_id, ..)| excerpt_id),
- );
- let mut excerpt_expands = HashMap::default();
- for (excerpt_id, directions) in excerpts_to_expand {
- let excerpt_expand = if directions.len() > 1 {
- Some((
- ExpandExcerptDirection::UpAndDown,
- directions
- .values()
- .max()
- .copied()
- .unwrap_or_default()
- .max(context),
- ))
- } else {
- directions
- .into_iter()
- .next()
- .map(|(direction, expand)| (direction, expand.max(context)))
- };
- if let Some(expand) = excerpt_expand {
- excerpt_expands
- .entry(expand)
- .or_insert_with(|| Vec::new())
- .push(excerpt_id);
- }
- }
- self.blocks_to_remove
- .extend(current_diagnostics.map(|(_, block_id)| block_id));
- }
-
- fn apply_excerpt_changes(
- &mut self,
- path_state: &mut PathState,
- context: u32,
- buffer_snapshot: BufferSnapshot,
- multi_buffer: &mut MultiBuffer,
- buffer: Model<Buffer>,
- cx: &mut gpui::ModelContext<MultiBuffer>,
- ) {
- let max_point = buffer_snapshot.max_point();
- for (after_excerpt_id, ranges) in std::mem::take(&mut self.excerpts_to_add) {
- let ranges = ranges
- .into_iter()
- .map(|range| {
- let mut extended_point_range = range.to_point(&buffer_snapshot);
- extended_point_range.start.row =
- extended_point_range.start.row.saturating_sub(context);
- extended_point_range.start.column = 0;
- extended_point_range.end.row =
- (extended_point_range.end.row + context).min(max_point.row);
- extended_point_range.end.column = u32::MAX;
- let extended_start =
- buffer_snapshot.clip_point(extended_point_range.start, Bias::Left);
- let extended_end =
- buffer_snapshot.clip_point(extended_point_range.end, Bias::Right);
- extended_start..extended_end
- })
- .collect::<Vec<_>>();
- let (joined_ranges, _) = build_excerpt_ranges(&buffer_snapshot, &ranges, context);
- let excerpts = multi_buffer.insert_excerpts_after(
- after_excerpt_id,
- buffer.clone(),
- joined_ranges,
- cx,
- );
- if self.first_excerpt_id.is_none() {
- self.first_excerpt_id = excerpts.first().copied();
- }
- self.last_excerpt_id = excerpts.last().copied();
- }
- for ((direction, line_count), excerpts) in std::mem::take(&mut self.excerpt_expands) {
- multi_buffer.expand_excerpts(excerpts, line_count, direction, cx);
- }
- multi_buffer.remove_excerpts(std::mem::take(&mut self.excerpts_to_remove), cx);
- path_state.first_excerpt_id = self.first_excerpt_id;
- path_state.last_excerpt_id = self.last_excerpt_id;
- }
-
- fn prepare_blocks_to_insert(
- &mut self,
- editor: View<Editor>,
- multi_buffer_snapshot: MultiBufferSnapshot,
- ) -> Vec<BlockProperties<editor::Anchor>> {
- let mut updated_excerpts = path_state_excerpts(
- self.path_excerpts_borders.0,
- self.path_excerpts_borders.1,
- &multi_buffer_snapshot,
- )
- .fuse()
- .peekable();
- let mut used_labels = BTreeMap::new();
- self.diagnostics_by_row_label = self.new_diagnostics.iter().enumerate().fold(
- BTreeMap::new(),
- |mut diagnostics_by_row_label, (diagnostic_index, (diagnostic, existing_block))| {
- let new_diagnostic = &diagnostic.entry;
- let block_position = new_diagnostic.range.start;
- let excerpt_id = loop {
- match updated_excerpts.peek() {
- None => break None,
- Some((excerpt_id, excerpt_buffer_snapshot, excerpt_range)) => {
- let excerpt_range = &excerpt_range.context;
- match block_position.cmp(&excerpt_range.start, excerpt_buffer_snapshot)
- {
- Ordering::Less => break None,
- Ordering::Equal | Ordering::Greater => match block_position
- .cmp(&excerpt_range.end, excerpt_buffer_snapshot)
- {
- Ordering::Equal | Ordering::Less => break Some(*excerpt_id),
- Ordering::Greater => {
- let _ = updated_excerpts.next();
- }
- },
- }
- }
- }
- };
-
- let Some(position_in_multi_buffer) = excerpt_id.and_then(|excerpt_id| {
- multi_buffer_snapshot.anchor_in_excerpt(excerpt_id, block_position)
- }) else {
- return diagnostics_by_row_label;
- };
-
- let multi_buffer_row = MultiBufferRow(
- position_in_multi_buffer
- .to_point(&multi_buffer_snapshot)
- .row,
- );
-
- let grouped_diagnostics = &mut diagnostics_by_row_label
- .entry(multi_buffer_row)
- .or_insert_with(|| (position_in_multi_buffer, Vec::new()))
- .1;
- let new_label = used_labels
- .entry(multi_buffer_row)
- .or_insert_with(|| HashSet::default())
- .insert((
- new_diagnostic.diagnostic.source.as_deref(),
- new_diagnostic.diagnostic.message.as_str(),
- ));
-
- if !new_label || !grouped_diagnostics.is_empty() {
- if let Some(existing_block) = existing_block {
- self.blocks_to_remove.insert(*existing_block);
- }
- if let Some(block_id) = self.unchanged_blocks.remove(&diagnostic_index) {
- self.blocks_to_remove.insert(block_id);
- }
- }
- if new_label {
- let (Ok(i) | Err(i)) = grouped_diagnostics.binary_search_by(|&probe| {
- let a = &self.new_diagnostics[probe].0.entry.diagnostic;
- let b = &self.new_diagnostics[diagnostic_index].0.entry.diagnostic;
- a.group_id
- .cmp(&b.group_id)
- .then_with(|| a.is_primary.cmp(&b.is_primary).reverse())
- .then_with(|| a.severity.cmp(&b.severity))
- });
- grouped_diagnostics.insert(i, diagnostic_index);
- }
-
- diagnostics_by_row_label
- },
- );
-
- self.diagnostics_by_row_label
- .values()
- .filter_map(|(earliest_in_row_position, diagnostics_at_line)| {
- let earliest_in_row_position = *earliest_in_row_position;
- match diagnostics_at_line.len() {
- 0 => None,
- len => {
- if len == 1 {
- let i = diagnostics_at_line.first().copied()?;
- if self.unchanged_blocks.contains_key(&i) {
- return None;
- }
- }
- let lines_in_first_message = diagnostic_text_lines(
- &self
- .new_diagnostics
- .get(diagnostics_at_line.first().copied()?)?
- .0
- .entry
- .diagnostic,
- );
- let folded_block_height = lines_in_first_message.clamp(1, 2);
- let diagnostics_to_render = Arc::new(
- diagnostics_at_line
- .iter()
- .filter_map(|&index| self.new_diagnostics.get(index))
- .map(|(diagnostic_data, _)| {
- diagnostic_data.entry.diagnostic.clone()
- })
- .collect::<Vec<_>>(),
- );
- Some(BlockProperties {
- position: earliest_in_row_position,
- height: folded_block_height,
- style: BlockStyle::Sticky,
- render: render_same_line_diagnostics(
- Arc::new(AtomicBool::new(false)),
- diagnostics_to_render,
- editor.clone(),
- folded_block_height,
- ),
- disposition: BlockDisposition::Above,
- })
- }
- }
- })
- .collect()
- }
-
- fn new_blocks(
- mut self,
- new_block_ids: Vec<CustomBlockId>,
- ) -> Vec<(DiagnosticData, CustomBlockId)> {
- let mut new_block_ids = new_block_ids.into_iter().fuse();
- for (_, (_, grouped_diagnostics)) in self.diagnostics_by_row_label {
- let mut created_block_id = None;
- match grouped_diagnostics.len() {
- 0 => {
- debug_panic!("Unexpected empty diagnostics group");
- continue;
- }
- 1 => {
- let index = grouped_diagnostics[0];
- if let Some(&block_id) = self.unchanged_blocks.get(&index) {
- self.new_diagnostics[index].1 = Some(block_id);
- } else {
- let Some(block_id) =
- created_block_id.get_or_insert_with(|| new_block_ids.next())
- else {
- debug_panic!("Expected a new block for each new diagnostic");
- continue;
- };
- self.new_diagnostics[index].1 = Some(*block_id);
- }
- }
- _ => {
- let Some(block_id) =
- created_block_id.get_or_insert_with(|| new_block_ids.next())
- else {
- debug_panic!("Expected a new block for each new diagnostic group");
- continue;
- };
- for i in grouped_diagnostics {
- self.new_diagnostics[i].1 = Some(*block_id);
- }
- }
- }
- }
-
- self.new_diagnostics
- .into_iter()
- .filter_map(|(diagnostic, block_id)| Some((diagnostic, block_id?)))
- .collect()
- }
-}
-
-fn render_same_line_diagnostics(
- expanded: Arc<AtomicBool>,
- diagnostics: Arc<Vec<language::Diagnostic>>,
- editor_handle: View<Editor>,
- folded_block_height: u8,
-) -> RenderBlock {
- Box::new(move |cx: &mut BlockContext| {
- let block_id = match cx.block_id {
- BlockId::Custom(block_id) => block_id,
- _ => {
- debug_panic!("Expected a block id for the diagnostics block");
- return div().into_any_element();
- }
- };
- let Some(first_diagnostic) = diagnostics.first() else {
- debug_panic!("Expected at least one diagnostic");
- return div().into_any_element();
- };
- let button_expanded = expanded.clone();
- let expanded = expanded.load(atomic::Ordering::Acquire);
- let expand_label = if expanded { '-' } else { '+' };
- let first_diagnostics_height = diagnostic_text_lines(first_diagnostic);
- let extra_diagnostics = diagnostics.len() - 1;
- let toggle_expand_label =
- if folded_block_height == first_diagnostics_height && extra_diagnostics == 0 {
- None
- } else if extra_diagnostics > 0 {
- Some(format!("{expand_label}{extra_diagnostics}"))
- } else {
- Some(expand_label.to_string())
- };
-
- let expanded_block_height = diagnostics
- .iter()
- .map(|diagnostic| diagnostic_text_lines(diagnostic))
- .sum::<u8>();
- let editor_handle = editor_handle.clone();
- let parent = h_flex()
- .items_start()
- .child(v_flex().size_full().map(|parent| {
- if let Some(label) = toggle_expand_label {
- parent.child(Button::new(cx.block_id, label).on_click({
- let diagnostics = Arc::clone(&diagnostics);
- move |_, cx| {
- let new_expanded = !expanded;
- button_expanded.store(new_expanded, atomic::Ordering::Release);
- let new_size = if new_expanded {
- expanded_block_height
- } else {
- folded_block_height
- };
- editor_handle.update(cx, |editor, cx| {
- editor.replace_blocks(
- HashMap::from_iter(Some((
- block_id,
- (
- Some(new_size),
- render_same_line_diagnostics(
- Arc::clone(&button_expanded),
- Arc::clone(&diagnostics),
- editor_handle.clone(),
- folded_block_height,
- ),
- ),
- ))),
- None,
- cx,
- )
- });
- }
- }))
- } else {
- parent.child(
- h_flex()
- .size(IconSize::default().rems())
- .invisible()
- .flex_none(),
- )
- }
- }));
- let max_message_rows = if expanded {
- None
- } else {
- Some(folded_block_height)
- };
- let mut renderer =
- diagnostic_block_renderer(first_diagnostic.clone(), max_message_rows, false, true);
- let mut diagnostics_element = v_flex();
- diagnostics_element = diagnostics_element.child(renderer(cx));
- if expanded {
- for diagnostic in diagnostics.iter().skip(1) {
- let mut renderer = diagnostic_block_renderer(diagnostic.clone(), None, false, true);
- diagnostics_element = diagnostics_element.child(renderer(cx));
- }
- }
- parent.child(diagnostics_element).into_any_element()
- })
-}
-
-fn diagnostic_text_lines(diagnostic: &language::Diagnostic) -> u8 {
- diagnostic.message.matches('\n').count() as u8 + 1
-}
-
-fn path_state_excerpts(
- after_excerpt_id: Option<ExcerptId>,
- before_excerpt_id: Option<ExcerptId>,
- multi_buffer_snapshot: &editor::MultiBufferSnapshot,
-) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<language::Anchor>)> {
- multi_buffer_snapshot
- .excerpts()
- .skip_while(move |&(excerpt_id, ..)| match after_excerpt_id {
- Some(after_excerpt_id) => after_excerpt_id != excerpt_id,
- None => false,
- })
- .filter(move |&(excerpt_id, ..)| after_excerpt_id != Some(excerpt_id))
- .take_while(move |&(excerpt_id, ..)| match before_excerpt_id {
- Some(before_excerpt_id) => before_excerpt_id != excerpt_id,
- None => true,
- })
-}
@@ -1,12 +1,11 @@
-use crate::{grouped_diagnostics::GroupedDiagnosticsEditor, ProjectDiagnosticsEditor};
-use futures::future::Either;
+use crate::ProjectDiagnosticsEditor;
use gpui::{EventEmitter, ParentElement, Render, View, ViewContext, WeakView};
use ui::prelude::*;
use ui::{IconButton, IconName, Tooltip};
use workspace::{item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
pub struct ToolbarControls {
- editor: Option<Either<WeakView<ProjectDiagnosticsEditor>, WeakView<GroupedDiagnosticsEditor>>>,
+ editor: Option<WeakView<ProjectDiagnosticsEditor>>,
}
impl Render for ToolbarControls {
@@ -16,32 +15,16 @@ impl Render for ToolbarControls {
let mut is_updating = false;
if let Some(editor) = self.editor() {
- match editor {
- Either::Left(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();
- }
- 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 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 {
@@ -59,18 +42,9 @@ impl Render for ToolbarControls {
.tooltip(move |cx| Tooltip::text("Update excerpts", cx))
.on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor() {
- match editor {
- Either::Left(editor) => {
- editor.update(cx, |editor, _| {
- editor.enqueue_update_stale_excerpts(None);
- });
- }
- Either::Right(editor) => {
- editor.update(cx, |editor, _| {
- editor.enqueue_update_stale_excerpts(None);
- });
- }
- }
+ 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))
.on_click(cx.listener(|this, _, cx| {
if let Some(editor) = this.editor() {
- match editor {
- Either::Left(editor) => {
- 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);
- });
- }
- }
+ editor.update(cx, |editor, cx| {
+ editor.toggle_warnings(&Default::default(), cx);
+ });
}
})),
)
@@ -108,10 +73,7 @@ impl ToolbarItemView for ToolbarControls {
) -> ToolbarItemLocation {
if let Some(pane_item) = active_pane_item.as_ref() {
if let Some(editor) = pane_item.downcast::<ProjectDiagnosticsEditor>() {
- self.editor = Some(Either::Left(editor.downgrade()));
- ToolbarItemLocation::PrimaryRight
- } else if let Some(editor) = pane_item.downcast::<GroupedDiagnosticsEditor>() {
- self.editor = Some(Either::Right(editor.downgrade()));
+ self.editor = Some(editor.downgrade());
ToolbarItemLocation::PrimaryRight
} else {
ToolbarItemLocation::Hidden
@@ -127,12 +89,7 @@ impl ToolbarControls {
ToolbarControls { editor: None }
}
- fn editor(
- &self,
- ) -> 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()?),
- })
+ fn editor(&self) -> Option<View<ProjectDiagnosticsEditor>> {
+ self.editor.as_ref()?.upgrade()
}
}
@@ -120,9 +120,9 @@ impl DisplayMap {
font_size: Pixels,
wrap_width: Option<Pixels>,
show_excerpt_controls: bool,
- buffer_header_height: u8,
- excerpt_header_height: u8,
- excerpt_footer_height: u8,
+ buffer_header_height: u32,
+ excerpt_header_height: u32,
+ excerpt_footer_height: u32,
fold_placeholder: FoldPlaceholder,
cx: &mut ModelContext<Self>,
) -> Self {
@@ -286,44 +286,11 @@ impl DisplayMap {
block_map.insert(blocks)
}
- pub fn replace_blocks(
+ pub fn resize_blocks(
&mut self,
- heights_and_renderers: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
+ heights: HashMap<CustomBlockId, u32>,
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 edits = self.buffer_subscription.consume().into_inner();
let tab_size = Self::tab_size(&self.buffer, cx);
@@ -334,7 +301,11 @@ impl DisplayMap {
.wrap_map
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
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>) {
@@ -1051,6 +1022,18 @@ impl DisplaySnapshot {
let type_id = TypeId::of::<Tag>();
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)]
@@ -35,9 +35,9 @@ pub struct BlockMap {
custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
transforms: RefCell<SumTree<Transform>>,
show_excerpt_controls: bool,
- buffer_header_height: u8,
- excerpt_header_height: u8,
- excerpt_footer_height: u8,
+ buffer_header_height: u32,
+ excerpt_header_height: u32,
+ excerpt_footer_height: u32,
}
pub struct BlockMapReader<'a> {
@@ -52,6 +52,9 @@ pub struct BlockSnapshot {
wrap_snapshot: WrapSnapshot,
transforms: SumTree<Transform>,
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)]
@@ -77,15 +80,15 @@ pub type RenderBlock = Box<dyn Send + FnMut(&mut BlockContext) -> AnyElement>;
pub struct CustomBlock {
id: CustomBlockId,
position: Anchor,
- height: u8,
+ height: u32,
style: BlockStyle,
- render: Mutex<RenderBlock>,
+ render: Arc<Mutex<RenderBlock>>,
disposition: BlockDisposition,
}
pub struct BlockProperties<P> {
pub position: P,
- pub height: u8,
+ pub height: u32,
pub style: BlockStyle,
pub render: RenderBlock,
pub disposition: BlockDisposition,
@@ -189,14 +192,14 @@ pub enum Block {
id: ExcerptId,
buffer: BufferSnapshot,
range: ExcerptRange<text::Anchor>,
- height: u8,
+ height: u32,
starts_new_buffer: bool,
show_excerpt_controls: bool,
},
ExcerptFooter {
id: ExcerptId,
disposition: BlockDisposition,
- height: u8,
+ height: u32,
},
}
@@ -231,7 +234,7 @@ impl Block {
}
}
- pub fn height(&self) -> u8 {
+ pub fn height(&self) -> u32 {
match self {
Block::Custom(block) => block.height,
Block::ExcerptHeader { height, .. } => *height,
@@ -301,9 +304,9 @@ impl BlockMap {
pub fn new(
wrap_snapshot: WrapSnapshot,
show_excerpt_controls: bool,
- buffer_header_height: u8,
- excerpt_header_height: u8,
- excerpt_footer_height: u8,
+ buffer_header_height: u32,
+ excerpt_header_height: u32,
+ excerpt_footer_height: u32,
) -> Self {
let row_count = wrap_snapshot.max_point().row() + 1;
let map = Self {
@@ -336,6 +339,9 @@ impl BlockMap {
wrap_snapshot,
transforms: self.transforms.borrow().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;
}
- 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 {
if let Some(render) = renderers.remove(&block.id) {
*block.render.lock() = render;
@@ -565,9 +571,9 @@ impl BlockMap {
pub fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
show_excerpt_controls: bool,
- excerpt_footer_height: u8,
- buffer_header_height: u8,
- excerpt_header_height: u8,
+ excerpt_footer_height: u32,
+ buffer_header_height: u32,
+ excerpt_header_height: u32,
buffer: &'b multi_buffer::MultiBufferSnapshot,
range: R,
wrap_snapshot: &'c WrapSnapshot,
@@ -793,7 +799,7 @@ impl<'a> BlockMapWriter<'a> {
id,
position,
height: block.height,
- render: Mutex::new(block.render),
+ render: Arc::new(Mutex::new(block.render)),
disposition: block.disposition,
style: block.style,
});
@@ -810,24 +816,21 @@ impl<'a> BlockMapWriter<'a> {
ids
}
- pub fn replace(
- &mut self,
- mut heights_and_renderers: HashMap<CustomBlockId, (u8, RenderBlock)>,
- ) {
+ pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
let buffer = wrap_snapshot.buffer_snapshot();
let mut edits = Patch::default();
let mut last_block_buffer_row = None;
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 {
let new_block = CustomBlock {
id: block.id,
position: block.position,
height: new_height,
style: block.style,
- render: Mutex::new(render),
+ render: block.render.clone(),
disposition: block.disposition,
};
let new_block = Arc::new(new_block);
@@ -1174,7 +1177,7 @@ impl Transform {
Self {
summary: TransformSummary {
input_rows: 0,
- output_rows: block.height() as u32,
+ output_rows: block.height(),
},
block: Some(block),
}
@@ -1445,7 +1448,7 @@ mod tests {
.blocks_in_range(0..8)
.map(|(start_row, block)| {
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<_>>();
@@ -1697,10 +1700,9 @@ mod tests {
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
- let mut hash_map = HashMap::default();
- let render: RenderBlock = Box::new(|_| div().into_any());
- hash_map.insert(block_ids[0], (2_u8, render));
- block_map_writer.replace(hash_map);
+ let mut new_heights = HashMap::default();
+ new_heights.insert(block_ids[0], 2);
+ block_map_writer.resize(new_heights);
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");
}
@@ -1708,10 +1710,9 @@ mod tests {
{
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
- let mut hash_map = HashMap::default();
- let render: RenderBlock = Box::new(|_| div().into_any());
- hash_map.insert(block_ids[0], (1_u8, render));
- block_map_writer.replace(hash_map);
+ let mut new_heights = HashMap::default();
+ new_heights.insert(block_ids[0], 1);
+ block_map_writer.resize(new_heights);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
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 hash_map = HashMap::default();
- let render: RenderBlock = Box::new(|_| div().into_any());
- hash_map.insert(block_ids[0], (0_u8, render));
- block_map_writer.replace(hash_map);
+ let mut new_heights = HashMap::default();
+ new_heights.insert(block_ids[0], 0);
+ block_map_writer.resize(new_heights);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
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 hash_map = HashMap::default();
- let render: RenderBlock = Box::new(|_| div().into_any());
- hash_map.insert(block_ids[0], (3_u8, render));
- block_map_writer.replace(hash_map);
+ let mut new_heights = HashMap::default();
+ new_heights.insert(block_ids[0], 3);
+ block_map_writer.resize(new_heights);
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");
@@ -1744,10 +1743,9 @@ mod tests {
{
let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
- let mut hash_map = HashMap::default();
- let render: RenderBlock = Box::new(|_| div().into_any());
- hash_map.insert(block_ids[0], (3_u8, render));
- block_map_writer.replace(hash_map);
+ let mut new_heights = HashMap::default();
+ new_heights.insert(block_ids[0], 3);
+ block_map_writer.resize(new_heights);
let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
// Same height as before, should remain the same
@@ -2185,17 +2183,17 @@ mod tests {
#[derive(Debug, Eq, PartialEq)]
enum ExpectedBlock {
ExcerptHeader {
- height: u8,
+ height: u32,
starts_new_buffer: bool,
},
ExcerptFooter {
- height: u8,
+ height: u32,
disposition: BlockDisposition,
},
Custom {
disposition: BlockDisposition,
id: CustomBlockId,
- height: u8,
+ height: u32,
},
}
@@ -2214,7 +2212,7 @@ mod tests {
}
impl ExpectedBlock {
- fn height(&self) -> u8 {
+ fn height(&self) -> u32 {
match self {
ExpectedBlock::ExcerptHeader { height, .. } => *height,
ExpectedBlock::Custom { height, .. } => *height,
@@ -160,9 +160,9 @@ use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
use crate::hover_links::find_url;
use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
-pub const FILE_HEADER_HEIGHT: u8 = 1;
-pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
-pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
+pub const FILE_HEADER_HEIGHT: u32 = 1;
+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;
@@ -558,7 +558,7 @@ pub struct Editor {
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
tasks_update_task: Option<Task<()>>,
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
- file_header_size: u8,
+ file_header_size: u32,
breadcrumb_header: Option<String>,
focused_block: Option<FocusedBlock>,
}
@@ -9805,14 +9805,11 @@ impl Editor {
for (block_id, diagnostic) in &active_diagnostics.blocks {
new_styles.insert(
*block_id,
- (
- None,
- diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
- ),
+ diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
);
}
- self.display_map.update(cx, |display_map, cx| {
- display_map.replace_blocks(new_styles, cx)
+ self.display_map.update(cx, |display_map, _cx| {
+ display_map.replace_blocks(new_styles)
});
}
}
@@ -9855,7 +9852,7 @@ impl Editor {
.insert_blocks(
diagnostic_group.iter().map(|entry| {
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 {
style: BlockStyle::Fixed,
position: buffer.anchor_after(entry.range.start),
@@ -10170,16 +10167,31 @@ impl Editor {
blocks
}
+ pub(crate) fn resize_blocks(
+ &mut self,
+ heights: HashMap<CustomBlockId, u32>,
+ autoscroll: Option<Autoscroll>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ self.display_map
+ .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
+ if let Some(autoscroll) = autoscroll {
+ self.request_autoscroll(autoscroll, cx);
+ }
+ }
+
pub fn replace_blocks(
&mut self,
- blocks: HashMap<CustomBlockId, (Option<u8>, RenderBlock)>,
+ renderers: HashMap<CustomBlockId, RenderBlock>,
autoscroll: Option<Autoscroll>,
cx: &mut ViewContext<Self>,
) {
self.display_map
- .update(cx, |display_map, cx| display_map.replace_blocks(blocks, cx));
+ .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
if let Some(autoscroll) = autoscroll {
self.request_autoscroll(autoscroll, cx);
+ } else {
+ cx.notify();
}
}
@@ -11755,7 +11767,7 @@ impl Editor {
})
}
- pub fn file_header_size(&self) -> u8 {
+ pub fn file_header_size(&self) -> u32 {
self.file_header_size
}
@@ -17,14 +17,14 @@ use crate::{
hunk_diff::ExpandedHunk,
hunk_status,
items::BufferSearchHighlights,
- mouse_context_menu::MenuPosition,
- mouse_context_menu::{self, MouseContextMenu},
+ mouse_context_menu::{self, MenuPosition, MouseContextMenu},
scroll::scroll_amount::ScrollAmount,
- BlockId, CodeActionsMenu, CursorShape, DisplayPoint, DisplayRow, DocumentHighlightRead,
- DocumentHighlightWrite, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
- ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HoveredCursor,
- HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, RangeToAnchorExt, RowExt,
- RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
+ BlockId, CodeActionsMenu, CursorShape, CustomBlockId, DisplayPoint, DisplayRow,
+ DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
+ EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
+ HalfPageUp, HoveredCursor, HoveredHunk, LineDown, LineUp, OpenExcerpts, PageDown, PageUp,
+ Point, RangeToAnchorExt, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
+ CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
};
use client::ParticipantIndex;
use collections::{BTreeMap, HashMap};
@@ -1929,7 +1929,7 @@ impl EditorElement {
fn render_block(
&self,
block: &Block,
- available_space: Size<AvailableSpace>,
+ available_width: AvailableSpace,
block_id: BlockId,
block_row_start: DisplayRow,
snapshot: &EditorSnapshot,
@@ -1941,6 +1941,7 @@ impl EditorElement {
em_width: Pixels,
text_hitbox: &Hitbox,
scroll_width: &mut Pixels,
+ resized_blocks: &mut HashMap<CustomBlockId, u32>,
cx: &mut WindowContext,
) -> (AnyElement, Size<Pixels>) {
let mut element = match block {
@@ -2021,7 +2022,7 @@ impl EditorElement {
};
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
.scroll_anchor
.scroll_position(&snapshot.display_snapshot)
@@ -2054,12 +2055,13 @@ impl EditorElement {
v_flex()
.id(("path excerpt header", EntityId::from(block_id)))
- .size_full()
- .p(header_padding)
+ .w_full()
+ .px(header_padding)
.child(
h_flex()
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.667)))
.id("path header block")
+ .h(2. * cx.line_height())
.pl(gpui::px(12.))
.pr(gpui::px(8.))
.rounded_md()
@@ -2112,6 +2114,7 @@ impl EditorElement {
.children(show_excerpt_controls.then(|| {
h_flex()
.flex_basis(Length::Definite(DefiniteLength::Fraction(0.333)))
+ .h(1. * cx.line_height())
.pt_1()
.justify_end()
.flex_none()
@@ -2157,7 +2160,8 @@ impl EditorElement {
} else {
v_flex()
.id(("excerpt header", EntityId::from(block_id)))
- .size_full()
+ .w_full()
+ .h(snapshot.excerpt_header_height() as f32 * cx.line_height())
.child(
div()
.flex()
@@ -2309,7 +2313,8 @@ impl EditorElement {
Block::ExcerptFooter { id, .. } => {
let element = v_flex()
.id(("excerpt footer", EntityId::from(block_id)))
- .size_full()
+ .w_full()
+ .h(snapshot.excerpt_footer_height() as f32 * cx.line_height())
.child(
h_flex()
.justify_end()
@@ -2357,8 +2362,24 @@ impl EditorElement {
}
};
- let size = element.layout_as_root(available_space, cx);
- (element, size)
+ // Discover the element's content height, then round up to the nearest multiple of line height.
+ 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)]
@@ -2375,7 +2396,7 @@ impl EditorElement {
line_height: Pixels,
line_layouts: &[LineWithInvisibles],
cx: &mut WindowContext,
- ) -> Vec<BlockLayout> {
+ ) -> Result<Vec<BlockLayout>, HashMap<CustomBlockId, u32>> {
let (fixed_blocks, non_fixed_blocks) = snapshot
.blocks_in_range(rows.clone())
.partition::<Vec<_>, _>(|(_, block)| block.style() == BlockStyle::Fixed);
@@ -2385,11 +2406,9 @@ impl EditorElement {
.update(cx, |editor, _| editor.take_focused_block());
let mut fixed_block_max_width = Pixels::ZERO;
let mut blocks = Vec::new();
+ let mut resized_blocks = HashMap::default();
+
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();
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(
block,
- available_space,
+ AvailableSpace::MinContent,
block_id,
row,
snapshot,
@@ -2410,6 +2429,7 @@ impl EditorElement {
em_width,
text_hitbox,
scroll_width,
+ &mut resized_blocks,
cx,
);
fixed_block_max_width = fixed_block_max_width.max(element_size.width + em_width);
@@ -2417,7 +2437,7 @@ impl EditorElement {
id: block_id,
row: Some(row),
element,
- available_space,
+ available_space: size(AvailableSpace::MinContent, element_size.height.into()),
style: BlockStyle::Fixed,
});
}
@@ -2432,19 +2452,15 @@ impl EditorElement {
.max(gutter_dimensions.width + *scroll_width),
BlockStyle::Fixed => unreachable!(),
};
- let available_space = size(
- AvailableSpace::Definite(width),
- AvailableSpace::Definite(block.height() as f32 * line_height),
- );
let block_id = block.id();
if focused_block.as_ref().map_or(false, |b| b.id == block_id) {
focused_block = None;
}
- let (element, _) = self.render_block(
+ let (element, element_size) = self.render_block(
block,
- available_space,
+ width.into(),
block_id,
row,
snapshot,
@@ -2456,13 +2472,15 @@ impl EditorElement {
em_width,
text_hitbox,
scroll_width,
+ &mut resized_blocks,
cx,
);
+
blocks.push(BlockLayout {
id: block_id,
row: Some(row),
element,
- available_space,
+ available_space: size(width.into(), element_size.height.into()),
style,
});
}
@@ -2483,14 +2501,10 @@ impl EditorElement {
),
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,
- available_space,
+ width,
focused_block.id,
rows.end,
snapshot,
@@ -2502,6 +2516,7 @@ impl EditorElement {
em_width,
text_hitbox,
scroll_width,
+ &mut resized_blocks,
cx,
);
@@ -2509,7 +2524,7 @@ impl EditorElement {
id: block.id(),
row: None,
element,
- available_space,
+ available_space: size(width, element_size.height.into()),
style,
});
}
@@ -2517,10 +2532,16 @@ impl EditorElement {
}
}
- *scroll_width = (*scroll_width).max(fixed_block_max_width - gutter_dimensions.width);
- blocks
+ if resized_blocks.is_empty() {
+ *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(
&self,
blocks: &mut Vec<BlockLayout>,
@@ -4938,21 +4959,27 @@ impl Element for EditorElement {
editor.gutter_dimensions = gutter_dimensions;
editor.set_visible_line_count(bounds.size.height / line_height, cx);
- 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 {
+ if matches!(editor.mode, EditorMode::AutoHeight { .. }) {
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_horizontally = false;
self.editor.update(cx, |editor, cx| {
+ autoscroll_request = editor.autoscroll_request();
autoscroll_containing_element =
- editor.autoscroll_requested() || editor.has_pending_selection();
+ autoscroll_request.is_some() || editor.has_pending_selection();
autoscroll_horizontally =
editor.autoscroll_vertically(bounds, line_height, max_scroll_top, cx);
snapshot = editor.snapshot(cx);
@@ -5116,7 +5145,7 @@ impl Element for EditorElement {
let mut scroll_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(
start_row..end_row,
&snapshot,
@@ -5131,6 +5160,15 @@ impl Element for EditorElement {
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 =
MultiBufferRow(start_anchor.to_point(&snapshot.buffer_snapshot).row);
@@ -6430,7 +6468,7 @@ mod tests {
disposition: BlockDisposition::Above,
height: 3,
position: Anchor::min(),
- render: Box::new(|_| div().into_any()),
+ render: Box::new(|cx| div().h(3. * cx.line_height()).into_any()),
}],
None,
cx,
@@ -364,7 +364,7 @@ impl Editor {
.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;
- line_count as u8
+ line_count
})?;
Some((diff_base_buffer, deleted_text_lines))
} else {
@@ -422,7 +422,7 @@ impl Editor {
fn insert_deleted_text_block(
&mut self,
diff_base_buffer: Model<Buffer>,
- deleted_text_height: u8,
+ deleted_text_height: u32,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Self>,
) -> Option<CustomBlockId> {
@@ -431,10 +431,11 @@ impl Editor {
editor_with_deleted_text(diff_base_buffer, deleted_hunk_color, hunk, cx);
let editor = cx.view().clone();
let hunk = hunk.clone();
+ let height = editor_height.max(deleted_text_height);
let mut new_block_ids = self.insert_blocks(
Some(BlockProperties {
position: hunk.multi_buffer_range.start,
- height: editor_height.max(deleted_text_height),
+ height,
style: BlockStyle::Flex,
disposition: BlockDisposition::Above,
render: Box::new(move |cx| {
@@ -474,7 +475,8 @@ impl Editor {
h_flex()
.id("gutter with editor")
.bg(deleted_hunk_color)
- .size_full()
+ .h(height as f32 * cx.line_height())
+ .w_full()
.child(
h_flex()
.id("gutter")
@@ -783,7 +785,7 @@ fn editor_with_deleted_text(
deleted_color: Hsla,
hunk: &HoveredHunk,
cx: &mut ViewContext<'_, Editor>,
-) -> (u8, View<Editor>) {
+) -> (u32, View<Editor>) {
let parent_editor = cx.view().downgrade();
let editor = cx.new_view(|cx| {
let multi_buffer =
@@ -885,7 +887,7 @@ fn editor_with_deleted_text(
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)
}
@@ -307,8 +307,8 @@ impl ScrollManager {
self.show_scrollbars
}
- pub fn autoscroll_requested(&self) -> bool {
- self.autoscroll_request.is_some()
+ pub fn autoscroll_request(&self) -> Option<Autoscroll> {
+ self.autoscroll_request.map(|(autoscroll, _)| autoscroll)
}
pub fn is_dragging_scrollbar(&self) -> bool {
@@ -61,8 +61,8 @@ impl AutoscrollStrategy {
}
impl Editor {
- pub fn autoscroll_requested(&self) -> bool {
- self.scroll_manager.autoscroll_requested()
+ pub fn autoscroll_request(&self) -> Option<Autoscroll> {
+ self.scroll_manager.autoscroll_request()
}
pub fn autoscroll_vertically(
@@ -43,11 +43,6 @@ impl FeatureFlag for LanguageModels {
const NAME: &'static str = "language-models";
}
-pub struct GroupedDiagnostics {}
-impl FeatureFlag for GroupedDiagnostics {
- const NAME: &'static str = "grouped-diagnostics";
-}
-
pub struct ZedPro {}
impl FeatureFlag for ZedPro {
const NAME: &'static str = "zed-pro";
@@ -1366,11 +1366,7 @@ impl<'a> WindowContext<'a> {
/// The line height associated with the current text style.
pub fn line_height(&self) -> Pixels {
- let rem_size = self.rem_size();
- let text_style = self.text_style();
- text_style
- .line_height
- .to_pixels(text_style.font_size, rem_size)
+ self.text_style().line_height_in_pixels(self.rem_size())
}
/// Call to prevent the default action of an event. Currently only used to prevent
@@ -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
/// how many lines they will take up in the editor.
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
@@ -88,15 +88,9 @@ impl 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 lines = self.height as f32 / line_height.0;
-
- if lines > u8::MAX as f32 {
- return u8::MAX;
- }
- lines as u8
+ (self.height as f32 / line_height.0) as usize
}
}
@@ -257,7 +251,7 @@ impl 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 {
// Rows + header
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;
- num_lines.ceil() as u8
+ num_lines.ceil() as usize
}
}
@@ -303,12 +297,9 @@ impl ErrorView {
}
impl LineHeight for ErrorView {
- fn num_lines(&self, cx: &mut WindowContext) -> u8 {
- let mut height: u8 = 1; // Start at 1 to account for the y padding
- height = height.saturating_add(self.ename.lines().count() as u8);
- height = height.saturating_add(self.evalue.lines().count() as u8);
- height = height.saturating_add(self.traceback.num_lines(cx));
- height
+ fn num_lines(&self, cx: &mut WindowContext) -> usize {
+ // Start at 1 to account for the y padding
+ 1 + self.ename.lines().count() + self.evalue.lines().count() + self.traceback.num_lines(cx)
}
}
@@ -357,12 +348,12 @@ impl OutputType {
impl LineHeight for OutputType {
/// Calculates the expected number of lines
- fn num_lines(&self, cx: &mut WindowContext) -> u8 {
+ fn num_lines(&self, cx: &mut WindowContext) -> usize {
match self {
Self::Plain(stdio) => stdio.num_lines(cx),
Self::Stream(stdio) => stdio.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::ErrorOutput(error_view) => error_view.num_lines(cx),
Self::ClearOutputWaitMarker => 0,
@@ -572,7 +563,7 @@ impl Render 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() {
return 1; // For the status message if outputs are not there
}
@@ -581,9 +572,7 @@ impl LineHeight for ExecutionView {
.outputs
.iter()
.map(|output| output.num_lines(cx))
- .fold(0_u8, |acc, additional_height| {
- acc.saturating_add(additional_height)
- })
+ .sum::<usize>()
.max(1);
let num_lines = match self.status {
@@ -597,7 +586,7 @@ impl LineHeight for 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))
}
}
@@ -42,12 +42,10 @@ pub struct Session {
}
struct EditorBlock {
- editor: WeakView<Editor>,
code_range: Range<Anchor>,
invalidation_anchor: Anchor,
block_id: CustomBlockId,
execution_view: View<ExecutionView>,
- on_close: CloseBlockFn,
}
type CloseBlockFn =
@@ -84,7 +82,7 @@ impl EditorBlock {
let invalidation_anchor = buffer.read(cx).read(cx).anchor_before(next_row_start);
let block = BlockProperties {
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,
render: Self::create_output_area_renderer(execution_view.clone(), on_close.clone()),
disposition: BlockDisposition::Below,
@@ -95,12 +93,10 @@ impl EditorBlock {
})?;
anyhow::Ok(Self {
- editor,
code_range,
invalidation_anchor,
block_id,
execution_view,
- on_close,
})
}
@@ -108,24 +104,6 @@ impl EditorBlock {
self.execution_view.update(cx, |execution_view, 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(
@@ -96,8 +96,8 @@ impl TerminalOutput {
}
impl LineHeight for TerminalOutput {
- fn num_lines(&self, _cx: &mut WindowContext) -> u8 {
- self.handler.buffer.lines().count().max(1) as u8
+ fn num_lines(&self, _cx: &mut WindowContext) -> usize {
+ self.handler.buffer.lines().count().max(1)
}
}