Render basic diagnostic messages in project diagnostics view

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo 2021-12-14 17:00:57 -07:00
parent ad05c0cc7a
commit e1a2897d53
4 changed files with 145 additions and 98 deletions

View file

@ -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<MultiBuffer>,
project: ModelHandle<Project>,
}
struct ProjectDiagnosticsEditor {
editor: ViewHandle<Editor>,
excerpts: ModelHandle<MultiBuffer>,
}
impl ProjectDiagnostics {
fn new(project: ModelHandle<Project>, cx: &mut ModelContext<Self>) -> Self {
fn new(project: ModelHandle<Project>) -> 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<Self>) -> ElementBox {
ChildView::new(self.editor.id()).boxed()
}
}
impl ProjectDiagnosticsEditor {
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
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<Self>,
settings: watch::Receiver<workspace::Settings>,
cx: &mut ViewContext<Self::View>,
) -> 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::<Vec<_>>();
sorted_diagnostic_groups.sort_by_key(|group| group.0);
for diagnostic in snapshot.all_diagnostics::<Point>() {
for entry in snapshot.all_diagnostics::<Point>() {
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<Self>) -> ElementBox {
ChildView::new(self.editor.id()).boxed()
}
}
impl ProjectDiagnosticsEditor {
fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
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<Self>,
settings: watch::Receiver<workspace::Settings>,
cx: &mut ViewContext<Self::View>,
) -> 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<project::ProjectPath> {
@ -148,22 +162,22 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
"Project Diagnostics".to_string()
}
fn project_path(&self, cx: &AppContext) -> Option<project::ProjectPath> {
fn project_path(&self, _: &AppContext) -> Option<project::ProjectPath> {
None
}
fn save(
&mut self,
cx: &mut ViewContext<Self>,
_: &mut ViewContext<Self>,
) -> anyhow::Result<gpui::Task<anyhow::Result<()>>> {
todo!()
}
fn save_as(
&mut self,
worktree: ModelHandle<project::Worktree>,
path: &std::path::Path,
cx: &mut ViewContext<Self>,
_: ModelHandle<project::Worktree>,
_: &std::path::Path,
_: &mut ViewContext<Self>,
) -> gpui::Task<anyhow::Result<()>> {
todo!()
}

View file

@ -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<u32>,
) -> impl 'a + Iterator<Item = (Range<u32>, 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<T: ToOffset>(&self, offset: T) -> bool {
self.folds_snapshot.intersects_fold(offset)
}

View file

@ -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();
}

View file

@ -631,6 +631,9 @@ impl EditorElement {
line_layouts: &[text_layout::Line],
cx: &mut LayoutContext,
) -> Vec<(u32, ElementBox)> {
let mut blocks = Vec::new();
blocks.extend(
snapshot
.blocks_in_range(rows.clone())
.map(|(start_row, block)| {
@ -657,8 +660,26 @@ impl EditorElement {
cx,
);
(start_row, element)
})
.collect()
}),
);
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
}
}