From e1a2897d534d9e3c9e5c1748e259aa8d30355a08 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 14 Dec 2021 17:00:57 -0700 Subject: [PATCH] Render basic diagnostic messages in project diagnostics view Co-Authored-By: Max Brunsfeld --- crates/diagnostics/src/diagnostics.rs | 146 ++++++++++++++------------ crates/editor/src/display_map.rs | 19 +++- crates/editor/src/editor.rs | 5 - crates/editor/src/element.rs | 73 ++++++++----- 4 files changed, 145 insertions(+), 98 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 58efa50c5e..59f3acd368 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use collections::HashMap; -use editor::{Editor, ExcerptProperties, MultiBuffer}; +use editor::{diagnostic_style, Editor, ExcerptProperties, MultiBuffer}; use gpui::{ - action, elements::*, keymap::Binding, AppContext, Entity, ModelContext, ModelHandle, - MutableAppContext, RenderContext, View, ViewContext, ViewHandle, + action, elements::*, keymap::Binding, AppContext, Entity, ModelHandle, MutableAppContext, + RenderContext, View, ViewContext, ViewHandle, }; use language::Point; use postage::watch; @@ -19,16 +19,63 @@ pub fn init(cx: &mut MutableAppContext) { } struct ProjectDiagnostics { - excerpts: ModelHandle, project: ModelHandle, } struct ProjectDiagnosticsEditor { editor: ViewHandle, + excerpts: ModelHandle, } impl ProjectDiagnostics { - fn new(project: ModelHandle, cx: &mut ModelContext) -> Self { + fn new(project: ModelHandle) -> Self { + Self { project } + } +} + +impl Entity for ProjectDiagnostics { + type Event = (); +} + +impl Entity for ProjectDiagnosticsEditor { + type Event = (); +} + +impl View for ProjectDiagnosticsEditor { + fn ui_name() -> &'static str { + "ProjectDiagnosticsEditor" + } + + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + ChildView::new(self.editor.id()).boxed() + } +} + +impl ProjectDiagnosticsEditor { + fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { + let diagnostics = cx.add_model(|_| ProjectDiagnostics::new(workspace.project().clone())); + workspace.add_item(diagnostics, cx); + } +} + +impl workspace::Item for ProjectDiagnostics { + type View = ProjectDiagnosticsEditor; + + fn build_view( + handle: ModelHandle, + settings: watch::Receiver, + cx: &mut ViewContext, + ) -> Self::View { + let project = handle.read(cx).project.clone(); + let excerpts = cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id(cx))); + let editor = cx.add_view(|cx| { + Editor::for_buffer( + excerpts.clone(), + editor::settings_builder(excerpts.downgrade(), settings.clone()), + cx, + ) + }); + let project_paths = project .read(cx) .diagnostic_summaries(cx) @@ -58,19 +105,35 @@ impl ProjectDiagnostics { grouped_diagnostics.into_values().collect::>(); sorted_diagnostic_groups.sort_by_key(|group| group.0); - for diagnostic in snapshot.all_diagnostics::() { + for entry in snapshot.all_diagnostics::() { this.update(&mut cx, |this, cx| { this.excerpts.update(cx, |excerpts, cx| { excerpts.push_excerpt( ExcerptProperties { buffer: &buffer, - range: diagnostic.range, - header_height: 1, + range: entry.range, + header_height: entry + .diagnostic + .message + .matches('\n') + .count() + as u8 + + 1, render_header: Some(Arc::new({ - let message = diagnostic.diagnostic.message.clone(); + let message = entry.diagnostic.message.clone(); + let settings = settings.clone(); + move |_| { - Text::new(message.clone(), Default::default()) - .boxed() + let editor_style = &settings.borrow().theme.editor; + let mut text_style = editor_style.text.clone(); + text_style.color = diagnostic_style( + entry.diagnostic.severity, + true, + &editor_style, + ) + .text; + + Text::new(message.clone(), text_style).boxed() } })), }, @@ -86,56 +149,7 @@ impl ProjectDiagnostics { }) .detach(); - Self { - excerpts: cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id(cx))), - project, - } - } -} - -impl Entity for ProjectDiagnostics { - type Event = (); -} - -impl Entity for ProjectDiagnosticsEditor { - type Event = (); -} - -impl View for ProjectDiagnosticsEditor { - fn ui_name() -> &'static str { - "ProjectDiagnosticsEditor" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - ChildView::new(self.editor.id()).boxed() - } -} - -impl ProjectDiagnosticsEditor { - fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { - let diagnostics = - cx.add_model(|cx| ProjectDiagnostics::new(workspace.project().clone(), cx)); - workspace.add_item(diagnostics, cx); - } -} - -impl workspace::Item for ProjectDiagnostics { - type View = ProjectDiagnosticsEditor; - - fn build_view( - handle: ModelHandle, - settings: watch::Receiver, - cx: &mut ViewContext, - ) -> Self::View { - let excerpts = handle.read(cx).excerpts.clone(); - let editor = cx.add_view(|cx| { - Editor::for_buffer( - excerpts.clone(), - editor::settings_builder(excerpts.downgrade(), settings), - cx, - ) - }); - ProjectDiagnosticsEditor { editor } + ProjectDiagnosticsEditor { editor, excerpts } } fn project_path(&self) -> Option { @@ -148,22 +162,22 @@ impl workspace::ItemView for ProjectDiagnosticsEditor { "Project Diagnostics".to_string() } - fn project_path(&self, cx: &AppContext) -> Option { + fn project_path(&self, _: &AppContext) -> Option { None } fn save( &mut self, - cx: &mut ViewContext, + _: &mut ViewContext, ) -> anyhow::Result>> { todo!() } fn save_as( &mut self, - worktree: ModelHandle, - path: &std::path::Path, - cx: &mut ViewContext, + _: ModelHandle, + _: &std::path::Path, + _: &mut ViewContext, ) -> gpui::Task> { todo!() } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 1f420681a1..838b136f75 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -3,7 +3,9 @@ mod fold_map; mod tab_map; mod wrap_map; -use crate::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint}; +use crate::{ + multi_buffer::RenderHeaderFn, Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, +}; use block_map::{BlockMap, BlockPoint}; use fold_map::{FoldMap, ToFoldPoint as _}; use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle}; @@ -327,6 +329,21 @@ impl DisplaySnapshot { self.blocks_snapshot.blocks_in_range(rows) } + pub fn excerpt_headers_in_range<'a>( + &'a self, + rows: Range, + ) -> impl 'a + Iterator, RenderHeaderFn)> { + let start_row = DisplayPoint::new(rows.start, 0).to_point(self).row; + let end_row = DisplayPoint::new(rows.end, 0).to_point(self).row; + self.buffer_snapshot + .excerpt_headers_in_range(start_row..end_row) + .map(move |(rows, render)| { + let start_row = Point::new(rows.start, 0).to_display_point(self).row(); + let end_row = Point::new(rows.end, 0).to_display_point(self).row(); + (start_row..end_row, render) + }) + } + pub fn intersects_fold(&self, offset: T) -> bool { self.folds_snapshot.intersects_fold(offset) } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 5a578115d5..a82b02435d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -590,11 +590,6 @@ impl Editor { scroll_position.y() - self.scroll_top_anchor.to_display_point(&map).row() as f32, ); - debug_assert_eq!( - compute_scroll_position(&map, self.scroll_position, &self.scroll_top_anchor), - scroll_position - ); - cx.notify(); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 3572d839b6..768b591003 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -631,34 +631,55 @@ impl EditorElement { line_layouts: &[text_layout::Line], cx: &mut LayoutContext, ) -> Vec<(u32, ElementBox)> { - snapshot - .blocks_in_range(rows.clone()) - .map(|(start_row, block)| { - let anchor_row = block - .position() - .to_point(&snapshot.buffer_snapshot) - .to_display_point(snapshot) - .row(); + let mut blocks = Vec::new(); - let anchor_x = if rows.contains(&anchor_row) { - line_layouts[(anchor_row - rows.start) as usize] - .x_for_index(block.column() as usize) - } else { - layout_line(anchor_row, snapshot, style, cx.text_layout_cache) - .x_for_index(block.column() as usize) - }; + blocks.extend( + snapshot + .blocks_in_range(rows.clone()) + .map(|(start_row, block)| { + let anchor_row = block + .position() + .to_point(&snapshot.buffer_snapshot) + .to_display_point(snapshot) + .row(); - let mut element = block.render(&BlockContext { cx, anchor_x }); - element.layout( - SizeConstraint { - min: Vector2F::zero(), - max: vec2f(text_width, block.height() as f32 * line_height), - }, - cx, - ); - (start_row, element) - }) - .collect() + let anchor_x = if rows.contains(&anchor_row) { + line_layouts[(anchor_row - rows.start) as usize] + .x_for_index(block.column() as usize) + } else { + layout_line(anchor_row, snapshot, style, cx.text_layout_cache) + .x_for_index(block.column() as usize) + }; + + let mut element = block.render(&BlockContext { cx, anchor_x }); + element.layout( + SizeConstraint { + min: Vector2F::zero(), + max: vec2f(text_width, block.height() as f32 * line_height), + }, + cx, + ); + (start_row, element) + }), + ); + + blocks.extend( + snapshot + .excerpt_headers_in_range(rows.clone()) + .map(|(rows, render)| { + let mut element = render(cx); + element.layout( + SizeConstraint { + min: Vector2F::zero(), + max: vec2f(text_width, rows.len() as f32 * line_height), + }, + cx, + ); + (rows.start, element) + }), + ); + + blocks } }