Repository: helix-editor/helix
File: helix-term/src/handlers/diagnostics.rs (Rust)

Code before:
        if event
            .doc
            .has_language_server_with_feature(LanguageServerFeature::PullDiagnostics)
        {
            let document_id = event.doc.id();
            job::dispatch_blocking(move |editor, _| {
                let Some(doc) = editor.document(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);
                }
            })
        }

        Ok(())
    });
}

#[derive(Debug)]
pub(super) struct PullDiagnosticsHandler {
    document_id: Option<DocumentId>,
}

impl PullDiagnosticsHandler {
    pub fn new() -> PullDiagnosticsHandler {
        PullDiagnosticsHandler { document_id: None }
    }
}

impl helix_event::AsyncHook for PullDiagnosticsHandler {
    type Event = PullDiagnosticsEvent;

    fn handle_event(
        &mut self,
        event: Self::Event,
        _: Option<tokio::time::Instant>,
    ) -> Option<tokio::time::Instant> {
        self.document_id = Some(event.document_id);
        Some(Instant::now() + Duration::from_millis(120))
    }

    fn finish_debounce(&mut self) {
        let document_id = self.document_id;
        job::dispatch_blocking(move |editor, _| {
            let Some(document_id) = document_id else {
                return;

Review comment (suggestion):
It should be possible for multiple documents to change while the changes are debounced. (For example the language server might send edits for multiple documents for an LSP rename request.) Instead of tracking one document I believe we need a HashSet/BTreeSet of document IDs to request

Code after:
            // Cancel the ongoing request, if present.
            event.doc.pull_diagnostic_controller.cancel();
            let document_id = event.doc.id();
            send_blocking(&tx, PullDiagnosticsEvent { document_id });

            let inter_file_dependencies_language_servers = event
                .doc
                .language_servers_with_feature(LanguageServerFeature::PullDiagnostics)
                .filter(|language_server| {
                    language_server
                        .capabilities()
                        .diagnostic_provider
                        .as_ref()
                        .is_some_and(|diagnostic_provider| match diagnostic_provider {
                            lsp::DiagnosticServerCapabilities::Options(options) => {
                                options.inter_file_dependencies
                            }

                            lsp::DiagnosticServerCapabilities::RegistrationOptions(options) => {
                                options.diagnostic_options.inter_file_dependencies
                            }
                        })
                })
                .map(|language_server| language_server.id())
                .collect();

            send_blocking(
                &tx_all_documents,
                PullAllDocumentsDiagnosticsEvent {
                    language_servers: inter_file_dependencies_language_servers,
                },
            );
        }
        Ok(())
    });

    register_hook!(move |event: &mut DocumentDidOpen<'_>| {
        request_document_diagnostics(event.editor, event.doc);

        Ok(())
    });

    register_hook!(move |event: &mut LanguageServerInitialized<'_>| {
        let doc_ids: Vec<_> = event.editor.documents.keys().copied().collect();

        for doc_id in doc_ids {
            request_document_diagnostics(event.editor, doc_id);
        }

        Ok(())
    });