1use std::sync::Arc;
2
3use collections::HashMap;
4use editor::{Editor, ExcerptProperties, MultiBuffer};
5use gpui::{
6 action, elements::*, keymap::Binding, AppContext, Entity, ModelContext, ModelHandle,
7 MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
8};
9use language::Point;
10use postage::watch;
11use project::Project;
12use workspace::Workspace;
13
14action!(Toggle);
15
16pub fn init(cx: &mut MutableAppContext) {
17 cx.add_bindings([Binding::new("alt-shift-D", Toggle, None)]);
18 cx.add_action(ProjectDiagnosticsEditor::toggle);
19}
20
21struct ProjectDiagnostics {
22 excerpts: ModelHandle<MultiBuffer>,
23 project: ModelHandle<Project>,
24}
25
26struct ProjectDiagnosticsEditor {
27 editor: ViewHandle<Editor>,
28}
29
30impl ProjectDiagnostics {
31 fn new(project: ModelHandle<Project>, cx: &mut ModelContext<Self>) -> Self {
32 let project_paths = project
33 .read(cx)
34 .diagnostic_summaries(cx)
35 .map(|e| e.0)
36 .collect::<Vec<_>>();
37
38 cx.spawn(|this, mut cx| {
39 let project = project.clone();
40 async move {
41 for project_path in project_paths {
42 let buffer = project
43 .update(&mut cx, |project, cx| project.open_buffer(project_path, cx))
44 .await?;
45 let snapshot = buffer.read_with(&cx, |b, _| b.snapshot());
46
47 let mut grouped_diagnostics = HashMap::default();
48 for entry in snapshot.all_diagnostics() {
49 let mut group = grouped_diagnostics
50 .entry(entry.diagnostic.group_id)
51 .or_insert((Point::zero(), Vec::new()));
52 if entry.diagnostic.is_primary {
53 group.0 = entry.range.start;
54 }
55 group.1.push(entry);
56 }
57 let mut sorted_diagnostic_groups =
58 grouped_diagnostics.into_values().collect::<Vec<_>>();
59 sorted_diagnostic_groups.sort_by_key(|group| group.0);
60
61 for diagnostic in snapshot.all_diagnostics::<Point>() {
62 this.update(&mut cx, |this, cx| {
63 this.excerpts.update(cx, |excerpts, cx| {
64 excerpts.push_excerpt(
65 ExcerptProperties {
66 buffer: &buffer,
67 range: diagnostic.range,
68 header_height: 1,
69 render_header: Some(Arc::new({
70 let message = diagnostic.diagnostic.message.clone();
71 move |_| {
72 Text::new(message.clone(), Default::default())
73 .boxed()
74 }
75 })),
76 },
77 cx,
78 );
79 cx.notify();
80 });
81 })
82 }
83 }
84 Result::Ok::<_, anyhow::Error>(())
85 }
86 })
87 .detach();
88
89 Self {
90 excerpts: cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id(cx))),
91 project,
92 }
93 }
94}
95
96impl Entity for ProjectDiagnostics {
97 type Event = ();
98}
99
100impl Entity for ProjectDiagnosticsEditor {
101 type Event = ();
102}
103
104impl View for ProjectDiagnosticsEditor {
105 fn ui_name() -> &'static str {
106 "ProjectDiagnosticsEditor"
107 }
108
109 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
110 ChildView::new(self.editor.id()).boxed()
111 }
112}
113
114impl ProjectDiagnosticsEditor {
115 fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
116 let diagnostics =
117 cx.add_model(|cx| ProjectDiagnostics::new(workspace.project().clone(), cx));
118 workspace.add_item(diagnostics, cx);
119 }
120}
121
122impl workspace::Item for ProjectDiagnostics {
123 type View = ProjectDiagnosticsEditor;
124
125 fn build_view(
126 handle: ModelHandle<Self>,
127 settings: watch::Receiver<workspace::Settings>,
128 cx: &mut ViewContext<Self::View>,
129 ) -> Self::View {
130 let excerpts = handle.read(cx).excerpts.clone();
131 let editor = cx.add_view(|cx| {
132 Editor::for_buffer(
133 excerpts.clone(),
134 editor::settings_builder(excerpts.downgrade(), settings),
135 cx,
136 )
137 });
138 ProjectDiagnosticsEditor { editor }
139 }
140
141 fn project_path(&self) -> Option<project::ProjectPath> {
142 None
143 }
144}
145
146impl workspace::ItemView for ProjectDiagnosticsEditor {
147 fn title(&self, _: &AppContext) -> String {
148 "Project Diagnostics".to_string()
149 }
150
151 fn project_path(&self, cx: &AppContext) -> Option<project::ProjectPath> {
152 None
153 }
154
155 fn save(
156 &mut self,
157 cx: &mut ViewContext<Self>,
158 ) -> anyhow::Result<gpui::Task<anyhow::Result<()>>> {
159 todo!()
160 }
161
162 fn save_as(
163 &mut self,
164 worktree: ModelHandle<project::Worktree>,
165 path: &std::path::Path,
166 cx: &mut ViewContext<Self>,
167 ) -> gpui::Task<anyhow::Result<()>> {
168 todo!()
169 }
170}