use std::{cmp, sync::Arc}; use editor::{ diagnostic_block_renderer, diagnostic_style, display_map::{BlockDisposition, BlockProperties}, Anchor, Editor, ExcerptProperties, MultiBuffer, }; use gpui::{ action, elements::*, keymap::Binding, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use language::Point; use postage::watch; use project::Project; use workspace::Workspace; action!(Toggle); pub fn init(cx: &mut MutableAppContext) { cx.add_bindings([Binding::new("alt-shift-D", Toggle, None)]); cx.add_action(ProjectDiagnosticsEditor::toggle); } struct ProjectDiagnostics { project: ModelHandle, } struct ProjectDiagnosticsEditor { editor: ViewHandle, excerpts: ModelHandle, } impl ProjectDiagnostics { 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 build_settings = editor::settings_builder(excerpts.downgrade(), settings.clone()); let editor = cx.add_view(|cx| Editor::for_buffer(excerpts.clone(), build_settings.clone(), cx)); let project_paths = project .read(cx) .diagnostic_summaries(cx) .map(|e| e.0) .collect::>(); cx.spawn(|this, mut cx| { let project = project.clone(); async move { for project_path in project_paths { let buffer = project .update(&mut cx, |project, cx| project.open_buffer(project_path, cx)) .await?; let snapshot = buffer.read_with(&cx, |b, _| b.snapshot()); this.update(&mut cx, |this, cx| { let mut blocks = Vec::new(); this.excerpts.update(cx, |excerpts, excerpts_cx| { for group in snapshot.diagnostic_groups::() { let excerpt_start = cmp::min( group.primary.range.start.row, group .supporting .first() .map_or(u32::MAX, |entry| entry.range.start.row), ); let excerpt_end = cmp::max( group.primary.range.end.row, group .supporting .last() .map_or(0, |entry| entry.range.end.row), ); let primary_diagnostic = group.primary.diagnostic; let excerpt_id = excerpts.push_excerpt( ExcerptProperties { buffer: &buffer, range: Point::new(excerpt_start, 0) ..Point::new( excerpt_end, snapshot.line_len(excerpt_end), ), header_height: primary_diagnostic .message .matches('\n') .count() as u8 + 1, render_header: Some(Arc::new({ let settings = settings.clone(); move |_| { let editor_style = &settings.borrow().theme.editor; let mut text_style = editor_style.text.clone(); text_style.color = diagnostic_style( primary_diagnostic.severity, true, &editor_style, ) .text; Text::new( primary_diagnostic.message.clone(), text_style, ) .boxed() } })), }, excerpts_cx, ); for entry in group.supporting { let buffer_anchor = snapshot.anchor_before(entry.range.start); blocks.push(BlockProperties { position: Anchor::new(excerpt_id.clone(), buffer_anchor), height: entry.diagnostic.message.matches('\n').count() as u8 + 1, render: diagnostic_block_renderer( entry.diagnostic, true, build_settings.clone(), ), disposition: BlockDisposition::Below, }); } } }); this.editor.update(cx, |editor, cx| { editor.insert_blocks(blocks, cx); }); }) } Result::Ok::<_, anyhow::Error>(()) } }) .detach(); ProjectDiagnosticsEditor { editor, excerpts } } fn project_path(&self) -> Option { None } } impl workspace::ItemView for ProjectDiagnosticsEditor { fn title(&self, _: &AppContext) -> String { "Project Diagnostics".to_string() } fn project_path(&self, _: &AppContext) -> Option { None } fn save( &mut self, _: &mut ViewContext, ) -> anyhow::Result>> { todo!() } fn save_as( &mut self, _: ModelHandle, _: &std::path::Path, _: &mut ViewContext, ) -> gpui::Task> { todo!() } }