mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 04:43:17 +03:00
WIP - Start work on updating project diagnostics view
This commit is contained in:
parent
a888620e5f
commit
2c3efdea8c
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1404,13 +1404,16 @@ name = "diagnostics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"client",
|
||||
"collections",
|
||||
"editor",
|
||||
"gpui",
|
||||
"language",
|
||||
"postage",
|
||||
"project",
|
||||
"serde_json",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
|
@ -13,12 +13,15 @@ editor = { path = "../editor" }
|
||||
language = { path = "../language" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
util = { path = "../util" }
|
||||
workspace = { path = "../workspace" }
|
||||
postage = { version = "0.4", features = ["futures-traits"] }
|
||||
|
||||
[dev-dependencies]
|
||||
unindent = "0.1"
|
||||
client = { path = "../client", features = ["test-support"] }
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
|
@ -12,6 +12,7 @@ use language::{Bias, Buffer, Point};
|
||||
use postage::watch;
|
||||
use project::Project;
|
||||
use std::ops::Range;
|
||||
use util::TryFutureExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
action!(Toggle);
|
||||
@ -65,11 +66,49 @@ impl View for ProjectDiagnosticsEditor {
|
||||
|
||||
impl ProjectDiagnosticsEditor {
|
||||
fn new(
|
||||
replica_id: u16,
|
||||
project: ModelHandle<Project>,
|
||||
settings: watch::Receiver<workspace::Settings>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let excerpts = cx.add_model(|_| MultiBuffer::new(replica_id));
|
||||
let project_paths = project
|
||||
.read(cx)
|
||||
.diagnostic_summaries(cx)
|
||||
.map(|e| e.0)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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?;
|
||||
this.update(&mut cx, |view, cx| view.populate_excerpts(buffer, cx))
|
||||
}
|
||||
Result::<_, anyhow::Error>::Ok(())
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.subscribe(&project, |_, project, event, cx| {
|
||||
if let project::Event::DiagnosticsUpdated(project_path) = event {
|
||||
let project_path = project_path.clone();
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
let buffer = project
|
||||
.update(&mut cx, |project, cx| project.open_buffer(project_path, cx))
|
||||
.await?;
|
||||
this.update(&mut cx, |view, cx| view.populate_excerpts(buffer, cx));
|
||||
Ok(())
|
||||
}
|
||||
.log_err()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let excerpts = cx.add_model(|cx| MultiBuffer::new(project.read(cx).replica_id()));
|
||||
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));
|
||||
@ -82,6 +121,11 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn text(&self, cx: &AppContext) -> String {
|
||||
self.editor.read(cx).text(cx)
|
||||
}
|
||||
|
||||
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);
|
||||
@ -193,6 +237,7 @@ impl ProjectDiagnosticsEditor {
|
||||
cx,
|
||||
);
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,27 +250,7 @@ impl workspace::Item for ProjectDiagnostics {
|
||||
cx: &mut ViewContext<Self::View>,
|
||||
) -> Self::View {
|
||||
let project = handle.read(cx).project.clone();
|
||||
let project_paths = project
|
||||
.read(cx)
|
||||
.diagnostic_summaries(cx)
|
||||
.map(|e| e.0)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cx.spawn(|view, 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?;
|
||||
view.update(&mut cx, |view, cx| view.populate_excerpts(buffer, cx))
|
||||
}
|
||||
Result::<_, anyhow::Error>::Ok(())
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
ProjectDiagnosticsEditor::new(project.read(cx).replica_id(), settings, cx)
|
||||
ProjectDiagnosticsEditor::new(project, settings, cx)
|
||||
}
|
||||
|
||||
fn project_path(&self) -> Option<project::ProjectPath> {
|
||||
@ -282,35 +307,68 @@ impl workspace::ItemView for ProjectDiagnosticsEditor {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
|
||||
use client::{http::ServerResponse, test::FakeHttpClient, Client, UserStore};
|
||||
use gpui::TestAppContext;
|
||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry, PointUtf16};
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use unindent::Unindent as _;
|
||||
use workspace::WorkspaceParams;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_diagnostics(cx: &mut MutableAppContext) {
|
||||
let settings = WorkspaceParams::test(cx).settings;
|
||||
let view = cx.add_view(Default::default(), |cx| {
|
||||
ProjectDiagnosticsEditor::new(0, settings, cx)
|
||||
async fn test_diagnostics(mut cx: TestAppContext) {
|
||||
let settings = cx.update(WorkspaceParams::test).settings;
|
||||
let http_client = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) });
|
||||
let client = Client::new();
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
let fs = Arc::new(FakeFs::new());
|
||||
|
||||
let project = cx.update(|cx| {
|
||||
Project::local(
|
||||
client.clone(),
|
||||
user_store,
|
||||
Arc::new(LanguageRegistry::new()),
|
||||
fs.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let text = "
|
||||
fn main() {
|
||||
let x = vec![];
|
||||
let y = vec![];
|
||||
a(x);
|
||||
b(y);
|
||||
// comment 1
|
||||
// comment 2
|
||||
c(y);
|
||||
d(x);
|
||||
}
|
||||
"
|
||||
.unindent();
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
json!({
|
||||
"a.rs": "
|
||||
const a: i32 = 'a';
|
||||
".unindent(),
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, text, cx);
|
||||
buffer
|
||||
.update_diagnostics(
|
||||
"main.rs": "
|
||||
fn main() {
|
||||
let x = vec![];
|
||||
let y = vec![];
|
||||
a(x);
|
||||
b(y);
|
||||
// comment 1
|
||||
// comment 2
|
||||
c(y);
|
||||
d(x);
|
||||
}
|
||||
"
|
||||
.unindent(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let worktree = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.add_local_worktree("/test", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
worktree.update(&mut cx, |worktree, cx| {
|
||||
worktree
|
||||
.update_diagnostic_entries(
|
||||
Arc::from("/test/main.rs".as_ref()),
|
||||
None,
|
||||
vec![
|
||||
DiagnosticEntry {
|
||||
@ -381,11 +439,16 @@ mod tests {
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
buffer
|
||||
});
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
view.populate_excerpts(buffer, cx);
|
||||
let view = cx.add_view(Default::default(), |cx| {
|
||||
ProjectDiagnosticsEditor::new(project.clone(), settings, cx)
|
||||
});
|
||||
|
||||
view.condition(&mut cx, |view, cx| view.text(cx).contains("fn main()"))
|
||||
.await;
|
||||
|
||||
view.update(&mut cx, |view, cx| {
|
||||
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||
|
||||
assert_eq!(
|
||||
@ -423,5 +486,71 @@ mod tests {
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
worktree.update(&mut cx, |worktree, cx| {
|
||||
worktree
|
||||
.update_diagnostic_entries(
|
||||
Arc::from("/test/a.rs".as_ref()),
|
||||
None,
|
||||
vec![DiagnosticEntry {
|
||||
range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15),
|
||||
diagnostic: Diagnostic {
|
||||
message: "mismatched types\nexpected `usize`, found `char`".to_string(),
|
||||
severity: DiagnosticSeverity::ERROR,
|
||||
is_primary: true,
|
||||
group_id: 0,
|
||||
..Default::default()
|
||||
},
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
view.condition(&mut cx, |view, cx| view.text(cx).contains("const a"))
|
||||
.await;
|
||||
|
||||
view.update(&mut cx, |view, cx| {
|
||||
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||
|
||||
assert_eq!(
|
||||
editor.text(),
|
||||
concat!(
|
||||
// a.rs
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
"const a: i32 = 'a';\n",
|
||||
// main.rs, diagnostic group 1
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
" let x = vec![];\n",
|
||||
" let y = vec![];\n",
|
||||
"\n", // supporting diagnostic
|
||||
" a(x);\n",
|
||||
" b(y);\n",
|
||||
"\n", // supporting diagnostic
|
||||
" // comment 1\n",
|
||||
" // comment 2\n",
|
||||
" c(y);\n",
|
||||
"\n", // supporting diagnostic
|
||||
" d(x);\n",
|
||||
// main.rs, diagnostic group 2
|
||||
"\n", // primary message
|
||||
"\n", // filename
|
||||
"fn main() {\n",
|
||||
" let x = vec![];\n",
|
||||
"\n", // supporting diagnostic
|
||||
" let y = vec![];\n",
|
||||
" a(x);\n",
|
||||
"\n", // supporting diagnostic
|
||||
" b(y);\n",
|
||||
"\n", // context ellipsis
|
||||
" c(y);\n",
|
||||
" d(x);\n",
|
||||
"\n", // supporting diagnostic
|
||||
"}"
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -56,9 +56,11 @@ pub struct Collaborator {
|
||||
pub replica_id: ReplicaId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
ActiveEntryChanged(Option<ProjectEntry>),
|
||||
WorktreeRemoved(usize),
|
||||
DiagnosticsUpdated(ProjectPath),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
@ -473,6 +475,15 @@ impl Project {
|
||||
|
||||
fn add_worktree(&mut self, worktree: ModelHandle<Worktree>, cx: &mut ModelContext<Self>) {
|
||||
cx.observe(&worktree, |_, _, cx| cx.notify()).detach();
|
||||
cx.subscribe(&worktree, |_, worktree, event, cx| match event {
|
||||
worktree::Event::DiagnosticsUpdated(path) => {
|
||||
cx.emit(Event::DiagnosticsUpdated(ProjectPath {
|
||||
worktree_id: worktree.id(),
|
||||
path: path.clone(),
|
||||
}));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
self.worktrees.push(worktree);
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -64,8 +64,9 @@ pub enum Worktree {
|
||||
Remote(RemoteWorktree),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Closed,
|
||||
DiagnosticsUpdated(Arc<Path>),
|
||||
}
|
||||
|
||||
impl Entity for Worktree {
|
||||
@ -671,7 +672,7 @@ impl Worktree {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_diagnostics(
|
||||
pub fn update_diagnostics(
|
||||
&mut self,
|
||||
mut params: lsp::PublishDiagnosticsParams,
|
||||
cx: &mut ModelContext<Worktree>,
|
||||
@ -736,17 +737,28 @@ impl Worktree {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.update_diagnostic_entries(worktree_path, params.version, diagnostics, cx)
|
||||
}
|
||||
|
||||
pub fn update_diagnostic_entries(
|
||||
&mut self,
|
||||
path: Arc<Path>,
|
||||
version: Option<i32>,
|
||||
diagnostics: Vec<DiagnosticEntry<PointUtf16>>,
|
||||
cx: &mut ModelContext<Worktree>,
|
||||
) -> Result<()> {
|
||||
let this = self.as_local_mut().unwrap();
|
||||
for buffer in this.open_buffers.values() {
|
||||
if let Some(buffer) = buffer.upgrade(cx) {
|
||||
if buffer
|
||||
.read(cx)
|
||||
.file()
|
||||
.map_or(false, |file| *file.path() == worktree_path)
|
||||
.map_or(false, |file| *file.path() == path)
|
||||
{
|
||||
let (remote_id, operation) = buffer.update(cx, |buffer, cx| {
|
||||
(
|
||||
buffer.remote_id(),
|
||||
buffer.update_diagnostics(params.version, diagnostics.clone(), cx),
|
||||
buffer.update_diagnostics(version, diagnostics.clone(), cx),
|
||||
)
|
||||
});
|
||||
self.send_buffer_update(remote_id, operation?, cx);
|
||||
@ -757,8 +769,9 @@ impl Worktree {
|
||||
|
||||
let this = self.as_local_mut().unwrap();
|
||||
this.diagnostic_summaries
|
||||
.insert(worktree_path.clone(), DiagnosticSummary::new(&diagnostics));
|
||||
this.diagnostics.insert(worktree_path.clone(), diagnostics);
|
||||
.insert(path.clone(), DiagnosticSummary::new(&diagnostics));
|
||||
this.diagnostics.insert(path.clone(), diagnostics);
|
||||
cx.emit(Event::DiagnosticsUpdated(path.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user