From 4177ba68dc9d7013b67d72e6ac3092a4d94abc93 Mon Sep 17 00:00:00 2001 From: Sofus Addington Date: Wed, 14 Aug 2024 22:22:55 +0200 Subject: [PATCH] Pull diagnostics on open --- helix-term/src/events.rs | 5 +- helix-term/src/handlers.rs | 11 +- helix-term/src/handlers/diagnostics.rs | 206 ++++++++++++++++--------- helix-view/src/editor.rs | 6 + helix-view/src/events.rs | 1 + helix-view/src/handlers.rs | 3 +- helix-view/src/handlers/lsp.rs | 9 +- 7 files changed, 164 insertions(+), 77 deletions(-) diff --git a/helix-term/src/events.rs b/helix-term/src/events.rs index 415213c2245e5..95e0b34e20c79 100644 --- a/helix-term/src/events.rs +++ b/helix-term/src/events.rs @@ -1,6 +1,8 @@ use helix_event::{events, register_event}; use helix_view::document::Mode; -use helix_view::events::{DiagnosticsDidChange, DocumentDidChange, SelectionDidChange}; +use helix_view::events::{ + DiagnosticsDidChange, DocumentDidChange, DocumentDidOpen, SelectionDidChange, +}; use crate::commands; use crate::keymap::MappableCommand; @@ -16,6 +18,7 @@ pub fn register() { register_event::(); register_event::(); register_event::(); + register_event::(); register_event::(); register_event::(); } diff --git a/helix-term/src/handlers.rs b/helix-term/src/handlers.rs index b32b9e2ffdb02..b3fd7601f78eb 100644 --- a/helix-term/src/handlers.rs +++ b/helix-term/src/handlers.rs @@ -8,12 +8,14 @@ use crate::config::Config; use crate::events; use crate::handlers::auto_save::AutoSaveHandler; use crate::handlers::completion::CompletionHandler; -use crate::handlers::diagnostics::PullDiagnosticsHandler; +use crate::handlers::diagnostics::PullDiagnosticsForLanguageServersHandler; use crate::handlers::signature_help::SignatureHelpHandler; pub use completion::trigger_auto_completion; pub use helix_view::handlers::Handlers; +use self::diagnostics::PullDiagnosticsForDocumentsHandler; + mod auto_save; pub mod completion; mod diagnostics; @@ -25,13 +27,16 @@ pub fn setup(config: Arc>) -> Handlers { let completions = CompletionHandler::new(config).spawn(); let signature_hints = SignatureHelpHandler::new().spawn(); let auto_save = AutoSaveHandler::new().spawn(); - let pull_diagnostics = PullDiagnosticsHandler::new().spawn(); + let pull_diagnostics_for_language_servers = + PullDiagnosticsForLanguageServersHandler::new().spawn(); + let pull_diagnostics_for_documents = PullDiagnosticsForDocumentsHandler::new().spawn(); let handlers = Handlers { completions, signature_hints, auto_save, - pull_diagnostics, + pull_diagnostics_for_language_servers, + pull_diagnostics_for_documents, }; completion::register_hooks(&handlers); diff --git a/helix-term/src/handlers/diagnostics.rs b/helix-term/src/handlers/diagnostics.rs index 9db591fbde740..2663da8e62b83 100644 --- a/helix-term/src/handlers/diagnostics.rs +++ b/helix-term/src/handlers/diagnostics.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::time::Duration; use helix_core::syntax::LanguageServerFeature; @@ -7,11 +7,13 @@ use helix_event::{register_hook, send_blocking}; use helix_lsp::lsp::{self, Diagnostic}; use helix_lsp::LanguageServerId; use helix_view::document::Mode; -use helix_view::events::{DiagnosticsDidChange, DocumentDidChange}; +use helix_view::events::{DiagnosticsDidChange, DocumentDidChange, DocumentDidOpen}; use helix_view::handlers::diagnostics::DiagnosticEvent; -use helix_view::handlers::lsp::PullDiagnosticsEvent; +use helix_view::handlers::lsp::{ + PullDiagnosticsForDocumentsEvent, PullDiagnosticsForLanguageServersEvent, +}; use helix_view::handlers::Handlers; -use helix_view::Editor; +use helix_view::{DocumentId, Editor}; use tokio::time::Instant; use crate::events::OnModeSwitch; @@ -33,7 +35,7 @@ pub(super) fn register_hooks(handlers: &Handlers) { Ok(()) }); - let tx = handlers.pull_diagnostics.clone(); + let tx = handlers.pull_diagnostics_for_language_servers.clone(); register_hook!(move |event: &mut DocumentDidChange<'_>| { if event .doc @@ -47,32 +49,58 @@ pub(super) fn register_hooks(handlers: &Handlers) { send_blocking( &tx, - PullDiagnosticsEvent { + PullDiagnosticsForLanguageServersEvent { language_server_ids, }, ); } Ok(()) }); -} -const TIMEOUT: u64 = 120; + let tx = handlers.pull_diagnostics_for_documents.clone(); + register_hook!(move |event: &mut DocumentDidOpen<'_>| { + if event + .doc + .has_language_server_with_feature(LanguageServerFeature::PullDiagnostics) + { + send_blocking( + &tx, + PullDiagnosticsForDocumentsEvent { + document_id: event.doc.id(), + }, + ); + } + + Ok(()) + }); +} #[derive(Debug)] -pub(super) struct PullDiagnosticsHandler { - language_server_ids: Vec, +pub(super) struct PullDiagnosticsForLanguageServersHandler { + language_server_ids: HashSet, } -impl PullDiagnosticsHandler { - pub fn new() -> PullDiagnosticsHandler { - PullDiagnosticsHandler { - language_server_ids: vec![], +impl PullDiagnosticsForLanguageServersHandler { + pub fn new() -> PullDiagnosticsForLanguageServersHandler { + PullDiagnosticsForLanguageServersHandler { + language_server_ids: [].into(), } } } +pub(super) struct PullDiagnosticsForDocumentsHandler { + document_ids: HashSet, +} -impl helix_event::AsyncHook for PullDiagnosticsHandler { - type Event = PullDiagnosticsEvent; +impl PullDiagnosticsForDocumentsHandler { + pub fn new() -> PullDiagnosticsForDocumentsHandler { + PullDiagnosticsForDocumentsHandler { + document_ids: [].into(), + } + } +} + +impl helix_event::AsyncHook for PullDiagnosticsForLanguageServersHandler { + type Event = PullDiagnosticsForLanguageServersEvent; fn handle_event( &mut self, @@ -80,84 +108,120 @@ impl helix_event::AsyncHook for PullDiagnosticsHandler { _: Option, ) -> Option { self.language_server_ids = event.language_server_ids; - Some(Instant::now() + Duration::from_millis(TIMEOUT)) + Some(Instant::now() + Duration::from_millis(120)) } fn finish_debounce(&mut self) { let language_servers = self.language_server_ids.clone(); job::dispatch_blocking(move |editor, _| { - pull_diagnostic_for_document( - editor, - language_servers, - editor.documents().map(|x| x.id()).collect(), - ) + pull_diagnostic_for_language_servers(editor, language_servers) }) } } -fn pull_diagnostic_for_document( +impl helix_event::AsyncHook for PullDiagnosticsForDocumentsHandler { + type Event = PullDiagnosticsForDocumentsEvent; + + fn handle_event( + &mut self, + event: Self::Event, + _: Option, + ) -> Option { + self.document_ids.insert(event.document_id); + Some(Instant::now() + Duration::from_millis(50)) + } + + fn finish_debounce(&mut self) { + let document_ids = self.document_ids.clone(); + job::dispatch_blocking(move |editor, _| { + pull_diagnostics_for_documents(editor, document_ids) + }) + } +} + +fn pull_diagnostics_for_documents(editor: &mut Editor, document_ids: HashSet) { + for document_id in document_ids { + let Some(doc) = editor.document_mut(document_id) else { + return; + }; + + let language_servers = + doc.language_servers_with_feature(LanguageServerFeature::PullDiagnostics); + + for language_server in language_servers { + pull_diagnostics_for_document(doc, language_server); + } + } +} + +fn pull_diagnostic_for_language_servers( editor: &mut Editor, - language_server_ids: Vec, - document_ids: Vec, + language_server_ids: HashSet, ) { - for document_id in document_ids.clone() { - let doc = doc_mut!(editor, &document_id); + let document_ids: Vec<_> = editor.documents().map(|x| x.id()).collect(); + + for document_id in document_ids { + let Some(doc) = editor.document_mut(document_id) else { + return; + }; + let language_servers = doc .language_servers() .filter(|x| language_server_ids.contains(&x.id())); for language_server in language_servers { - let Some(future) = language_server - .text_document_diagnostic(doc.identifier(), doc.previous_diagnostic_id.clone()) - else { - return; - }; - - let Some(uri) = doc.uri() else { - return; - }; - - let server_id = language_server.id(); - - tokio::spawn(async move { - match future.await { - Ok(res) => { - job::dispatch(move |editor, _| { - log::error!("{}", res); - - let parsed_response: Option = - match serde_json::from_value(res) { - Ok(result) => Some(result), - Err(_) => None, - }; - - let Some(response) = parsed_response else { - return; - }; - - show_pull_diagnostics(editor, response, server_id, uri, &document_id) - }) - .await - } - Err(err) => log::error!("signature help request failed: {err}"), - } - }); + pull_diagnostics_for_document(doc, language_server); } } } -fn show_pull_diagnostics( +fn pull_diagnostics_for_document(doc: &helix_view::Document, language_server: &helix_lsp::Client) { + let Some(future) = language_server + .text_document_diagnostic(doc.identifier(), doc.previous_diagnostic_id.clone()) + else { + return; + }; + + let Some(uri) = doc.uri() else { + return; + }; + + let server_id = language_server.id(); + let document_id = doc.id(); + + tokio::spawn(async move { + match future.await { + Ok(res) => { + job::dispatch(move |editor, _| { + let response = match serde_json::from_value(res) { + Ok(result) => result, + Err(_) => return, + }; + + handle_pull_diagnostics_response(editor, response, server_id, uri, document_id) + }) + .await + } + Err(err) => log::error!("Pull diagnostic request failed: {err}"), + } + }); +} + +fn handle_pull_diagnostics_response( editor: &mut Editor, response: lsp::DocumentDiagnosticReport, server_id: LanguageServerId, uri: Uri, - document_id: &helix_view::DocumentId, + document_id: helix_view::DocumentId, ) { - let doc = doc_mut!(editor, document_id); + let Some(doc) = editor.document_mut(document_id) else { + return; + }; + match response { lsp::DocumentDiagnosticReport::Full(report) => { // Original file diagnostic - parse_diagnostic( + add_diagnostics_to_editor( editor, uri, report.full_document_diagnostic_report.items, @@ -187,7 +251,7 @@ fn show_pull_diagnostics( } } -fn parse_diagnostic( +fn add_diagnostics_to_editor( editor: &mut Editor, uri: Uri, report: Vec, @@ -202,7 +266,7 @@ fn parse_diagnostic( fn handle_document_diagnostic_report_kind( editor: &mut Editor, - document_id: &helix_view::DocumentId, + document_id: helix_view::DocumentId, report: Option>, server_id: LanguageServerId, ) { @@ -213,10 +277,12 @@ fn handle_document_diagnostic_report_kind( return; }; - parse_diagnostic(editor, uri, report.items, report.result_id, server_id); + add_diagnostics_to_editor(editor, uri, report.items, report.result_id, server_id); } lsp::DocumentDiagnosticReportKind::Unchanged(report) => { - let doc = doc_mut!(editor, &document_id); + let Some(doc) = editor.document_mut(document_id) else { + return; + }; doc.previous_diagnostic_id = Some(report.result_id); } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 691c762b59b55..5549391651b87 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -3,6 +3,7 @@ use crate::{ document::{ DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint, }, + events::DocumentDidOpen, graphics::{CursorKind, Rect}, handlers::Handlers, info::Info, @@ -1721,6 +1722,11 @@ impl Editor { }; self.switch(id, action); + + if let Some(doc) = self.document_mut(id) { + helix_event::dispatch(DocumentDidOpen { doc }); + }; + Ok(id) } diff --git a/helix-view/src/events.rs b/helix-view/src/events.rs index 881412495464b..0d4b1c21c7225 100644 --- a/helix-view/src/events.rs +++ b/helix-view/src/events.rs @@ -5,6 +5,7 @@ use crate::{Document, DocumentId, Editor, ViewId}; events! { DocumentDidChange<'a> { doc: &'a mut Document, view: ViewId, old_text: &'a Rope } + DocumentDidOpen<'a> { doc: &'a mut Document} SelectionDidChange<'a> { doc: &'a mut Document, view: ViewId } DiagnosticsDidChange<'a> { editor: &'a mut Editor, doc: DocumentId } } diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index 519ccca8abd1f..8f1ef4088e164 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -19,7 +19,8 @@ pub struct Handlers { pub completions: Sender, pub signature_hints: Sender, pub auto_save: Sender, - pub pull_diagnostics: Sender, + pub pull_diagnostics_for_language_servers: Sender, + pub pull_diagnostics_for_documents: Sender, } impl Handlers { diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index ff540fd0f6e4b..bd0aba74fe14a 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::fmt::Display; use crate::editor::Action; @@ -47,8 +48,12 @@ pub enum SignatureHelpEvent { RequestComplete { open: bool }, } -pub struct PullDiagnosticsEvent { - pub language_server_ids: Vec, +pub struct PullDiagnosticsForLanguageServersEvent { + pub language_server_ids: HashSet, +} + +pub struct PullDiagnosticsForDocumentsEvent { + pub document_id: DocumentId, } #[derive(Debug)]