summaryrefslogtreecommitdiff
path: root/cli/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp')
-rw-r--r--cli/lsp/client.rs34
-rw-r--r--cli/lsp/diagnostics.rs134
-rw-r--r--cli/lsp/language_server.rs27
-rw-r--r--cli/lsp/lsp_custom.rs18
-rw-r--r--cli/lsp/mod.rs18
5 files changed, 210 insertions, 21 deletions
diff --git a/cli/lsp/client.rs b/cli/lsp/client.rs
index 4923a4585..5f1a7fcef 100644
--- a/cli/lsp/client.rs
+++ b/cli/lsp/client.rs
@@ -62,6 +62,20 @@ impl Client {
});
}
+ /// This notification is sent to the client during internal testing
+ /// purposes only in order to let the test client know when the latest
+ /// diagnostics have been published.
+ pub fn send_diagnostic_batch_notification(
+ &self,
+ params: lsp_custom::DiagnosticBatchNotificationParams,
+ ) {
+ // do on a task in case the caller currently is in the lsp lock
+ let client = self.0.clone();
+ spawn(async move {
+ client.send_diagnostic_batch_notification(params).await;
+ });
+ }
+
pub fn send_test_notification(&self, params: TestingNotification) {
// do on a task in case the caller currently is in the lsp lock
let client = self.0.clone();
@@ -160,6 +174,10 @@ trait ClientTrait: Send + Sync {
&self,
params: lsp_custom::RegistryStateNotificationParams,
);
+ async fn send_diagnostic_batch_notification(
+ &self,
+ params: lsp_custom::DiagnosticBatchNotificationParams,
+ );
async fn send_test_notification(&self, params: TestingNotification);
async fn specifier_configurations(
&self,
@@ -197,6 +215,16 @@ impl ClientTrait for TowerClient {
.await
}
+ async fn send_diagnostic_batch_notification(
+ &self,
+ params: lsp_custom::DiagnosticBatchNotificationParams,
+ ) {
+ self
+ .0
+ .send_notification::<lsp_custom::DiagnosticBatchNotification>(params)
+ .await
+ }
+
async fn send_test_notification(&self, notification: TestingNotification) {
match notification {
TestingNotification::Module(params) => {
@@ -311,6 +339,12 @@ impl ClientTrait for ReplClient {
) {
}
+ async fn send_diagnostic_batch_notification(
+ &self,
+ _params: lsp_custom::DiagnosticBatchNotificationParams,
+ ) {
+ }
+
async fn send_test_notification(&self, _params: TestingNotification) {}
async fn specifier_configurations(
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 1a57ad03b..a5e9d7bf8 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -16,6 +16,7 @@ use super::tsc::TsServer;
use crate::args::LintOptions;
use crate::graph_util;
use crate::graph_util::enhanced_resolution_error_message;
+use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams;
use crate::tools::lint::get_configured_rules;
use deno_ast::MediaType;
@@ -37,6 +38,7 @@ use deno_runtime::tokio_util::create_basic_runtime;
use deno_semver::npm::NpmPackageReqReference;
use log::error;
use std::collections::HashMap;
+use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::thread;
use tokio::sync::mpsc;
@@ -45,8 +47,13 @@ use tokio::time::Duration;
use tokio_util::sync::CancellationToken;
use tower_lsp::lsp_types as lsp;
-pub type SnapshotForDiagnostics =
- (Arc<StateSnapshot>, Arc<ConfigSnapshot>, LintOptions);
+#[derive(Debug)]
+pub struct DiagnosticServerUpdateMessage {
+ pub snapshot: Arc<StateSnapshot>,
+ pub config: Arc<ConfigSnapshot>,
+ pub lint_options: LintOptions,
+}
+
pub type DiagnosticRecord =
(ModuleSpecifier, Option<i32>, Vec<lsp::Diagnostic>);
pub type DiagnosticVec = Vec<DiagnosticRecord>;
@@ -145,13 +152,55 @@ impl TsDiagnosticsStore {
}
}
+pub fn should_send_diagnostic_batch_index_notifications() -> bool {
+ crate::args::has_flag_env_var(
+ "DENO_DONT_USE_INTERNAL_LSP_DIAGNOSTIC_SYNC_FLAG",
+ )
+}
+
+#[derive(Clone, Debug)]
+struct DiagnosticBatchCounter(Option<Arc<AtomicUsize>>);
+
+impl Default for DiagnosticBatchCounter {
+ fn default() -> Self {
+ if should_send_diagnostic_batch_index_notifications() {
+ Self(Some(Default::default()))
+ } else {
+ Self(None)
+ }
+ }
+}
+
+impl DiagnosticBatchCounter {
+ pub fn inc(&self) -> Option<usize> {
+ self
+ .0
+ .as_ref()
+ .map(|value| value.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1)
+ }
+
+ pub fn get(&self) -> Option<usize> {
+ self
+ .0
+ .as_ref()
+ .map(|value| value.load(std::sync::atomic::Ordering::SeqCst))
+ }
+}
+
+#[derive(Debug)]
+struct ChannelMessage {
+ message: DiagnosticServerUpdateMessage,
+ batch_index: Option<usize>,
+}
+
#[derive(Debug)]
pub struct DiagnosticsServer {
- channel: Option<mpsc::UnboundedSender<SnapshotForDiagnostics>>,
+ channel: Option<mpsc::UnboundedSender<ChannelMessage>>,
ts_diagnostics: TsDiagnosticsStore,
client: Client,
performance: Arc<Performance>,
ts_server: Arc<TsServer>,
+ batch_counter: DiagnosticBatchCounter,
}
impl DiagnosticsServer {
@@ -166,6 +215,7 @@ impl DiagnosticsServer {
client,
performance,
ts_server,
+ batch_counter: Default::default(),
}
}
@@ -187,7 +237,7 @@ impl DiagnosticsServer {
#[allow(unused_must_use)]
pub fn start(&mut self) {
- let (tx, mut rx) = mpsc::unbounded_channel::<SnapshotForDiagnostics>();
+ let (tx, mut rx) = mpsc::unbounded_channel::<ChannelMessage>();
self.channel = Some(tx);
let client = self.client.clone();
let performance = self.performance.clone();
@@ -208,7 +258,17 @@ impl DiagnosticsServer {
match rx.recv().await {
// channel has closed
None => break,
- Some((snapshot, config, lint_options)) => {
+ Some(message) => {
+ let ChannelMessage {
+ message:
+ DiagnosticServerUpdateMessage {
+ snapshot,
+ config,
+ lint_options,
+ },
+ batch_index,
+ } = message;
+
// cancel the previous run
token.cancel();
token = CancellationToken::new();
@@ -255,6 +315,7 @@ impl DiagnosticsServer {
})
.unwrap_or_default();
+ let messages_len = diagnostics.len();
if !token.is_cancelled() {
ts_diagnostics_store.update(&diagnostics);
diagnostics_publisher.publish(diagnostics, &token).await;
@@ -263,6 +324,17 @@ impl DiagnosticsServer {
performance.measure(mark);
}
}
+
+ if let Some(batch_index) = batch_index {
+ diagnostics_publisher
+ .client
+ .send_diagnostic_batch_notification(
+ DiagnosticBatchNotificationParams {
+ batch_index,
+ messages_len,
+ },
+ );
+ }
}
}));
@@ -286,10 +358,24 @@ impl DiagnosticsServer {
)
.await;
- diagnostics_publisher.publish(diagnostics, &token).await;
-
+ let messages_len = diagnostics.len();
if !token.is_cancelled() {
- performance.measure(mark);
+ diagnostics_publisher.publish(diagnostics, &token).await;
+
+ if !token.is_cancelled() {
+ performance.measure(mark);
+ }
+ }
+
+ if let Some(batch_index) = batch_index {
+ diagnostics_publisher
+ .client
+ .send_diagnostic_batch_notification(
+ DiagnosticBatchNotificationParams {
+ batch_index,
+ messages_len,
+ },
+ );
}
}
}));
@@ -315,10 +401,24 @@ impl DiagnosticsServer {
)
.await;
- diagnostics_publisher.publish(diagnostics, &token).await;
-
+ let messages_len = diagnostics.len();
if !token.is_cancelled() {
- performance.measure(mark);
+ diagnostics_publisher.publish(diagnostics, &token).await;
+
+ if !token.is_cancelled() {
+ performance.measure(mark);
+ }
+ }
+
+ if let Some(batch_index) = batch_index {
+ diagnostics_publisher
+ .client
+ .send_diagnostic_batch_notification(
+ DiagnosticBatchNotificationParams {
+ batch_index,
+ messages_len,
+ },
+ );
}
}
}));
@@ -329,15 +429,23 @@ impl DiagnosticsServer {
});
}
+ pub fn latest_batch_index(&self) -> Option<usize> {
+ self.batch_counter.get()
+ }
+
pub fn update(
&self,
- message: SnapshotForDiagnostics,
+ message: DiagnosticServerUpdateMessage,
) -> Result<(), AnyError> {
// todo(dsherret): instead of queuing up messages, it would be better to
// instead only store the latest message (ex. maybe using a
// tokio::sync::watch::channel)
if let Some(tx) = &self.channel {
- tx.send(message).map_err(|err| err.into())
+ tx.send(ChannelMessage {
+ message,
+ batch_index: self.batch_counter.inc(),
+ })
+ .map_err(|err| err.into())
} else {
Err(anyhow!("diagnostics server not started"))
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 9a2b067c6..7c4191c82 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -46,6 +46,7 @@ use super::completions;
use super::config::Config;
use super::config::SETTINGS_SECTION;
use super::diagnostics;
+use super::diagnostics::DiagnosticServerUpdateMessage;
use super::diagnostics::DiagnosticsServer;
use super::documents::to_hover_text;
use super::documents::to_lsp_range;
@@ -342,6 +343,22 @@ impl LanguageServer {
}
}
+ /// This request is only used by the lsp integration tests to
+ /// coordinate the tests receiving the latest diagnostics.
+ pub async fn latest_diagnostic_batch_index_request(
+ &self,
+ ) -> LspResult<Option<Value>> {
+ Ok(
+ self
+ .0
+ .read()
+ .await
+ .diagnostics_server
+ .latest_batch_index()
+ .map(|v| v.into()),
+ )
+ }
+
pub async fn performance_request(&self) -> LspResult<Option<Value>> {
Ok(Some(self.0.read().await.get_performance()))
}
@@ -2932,11 +2949,11 @@ impl Inner {
}
fn send_diagnostics_update(&self) {
- let snapshot = (
- self.snapshot(),
- self.config.snapshot(),
- self.lint_options.clone(),
- );
+ let snapshot = DiagnosticServerUpdateMessage {
+ snapshot: self.snapshot(),
+ config: self.config.snapshot(),
+ lint_options: self.lint_options.clone(),
+ };
if let Err(err) = self.diagnostics_server.update(snapshot) {
error!("Cannot update diagnostics: {}", err);
}
diff --git a/cli/lsp/lsp_custom.rs b/cli/lsp/lsp_custom.rs
index 70a245a66..24c4bc131 100644
--- a/cli/lsp/lsp_custom.rs
+++ b/cli/lsp/lsp_custom.rs
@@ -10,6 +10,8 @@ pub const TASK_REQUEST: &str = "deno/task";
pub const RELOAD_IMPORT_REGISTRIES_REQUEST: &str =
"deno/reloadImportRegistries";
pub const VIRTUAL_TEXT_DOCUMENT: &str = "deno/virtualTextDocument";
+pub const LATEST_DIAGNOSTIC_BATCH_INDEX: &str =
+ "deno/internalLatestDiagnosticBatchIndex";
// While lsp_types supports inlay hints currently, tower_lsp does not.
pub const INLAY_HINT: &str = "textDocument/inlayHint";
@@ -44,3 +46,19 @@ impl lsp::notification::Notification for RegistryStateNotification {
pub struct VirtualTextDocumentParams {
pub text_document: lsp::TextDocumentIdentifier,
}
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct DiagnosticBatchNotificationParams {
+ pub batch_index: usize,
+ pub messages_len: usize,
+}
+
+/// This notification is only sent for testing purposes
+/// in order to know what the latest diagnostics are.
+pub enum DiagnosticBatchNotification {}
+
+impl lsp::notification::Notification for DiagnosticBatchNotification {
+ type Params = DiagnosticBatchNotificationParams;
+
+ const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
+}
diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs
index 0d5552519..d13c90089 100644
--- a/cli/lsp/mod.rs
+++ b/cli/lsp/mod.rs
@@ -8,6 +8,8 @@ use crate::lsp::language_server::LanguageServer;
pub use repl::ReplCompletionItem;
pub use repl::ReplLanguageServer;
+use self::diagnostics::should_send_diagnostic_batch_index_notifications;
+
mod analysis;
mod cache;
mod capabilities;
@@ -36,7 +38,7 @@ pub async fn start() -> Result<(), AnyError> {
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
- let (service, socket) = LspService::build(|client| {
+ let builder = LspService::build(|client| {
language_server::LanguageServer::new(client::Client::from_tower(client))
})
.custom_method(lsp_custom::CACHE_REQUEST, LanguageServer::cache_request)
@@ -58,8 +60,18 @@ pub async fn start() -> Result<(), AnyError> {
lsp_custom::VIRTUAL_TEXT_DOCUMENT,
LanguageServer::virtual_text_document,
)
- .custom_method(lsp_custom::INLAY_HINT, LanguageServer::inlay_hint)
- .finish();
+ .custom_method(lsp_custom::INLAY_HINT, LanguageServer::inlay_hint);
+
+ let builder = if should_send_diagnostic_batch_index_notifications() {
+ builder.custom_method(
+ lsp_custom::LATEST_DIAGNOSTIC_BATCH_INDEX,
+ LanguageServer::latest_diagnostic_batch_index_request,
+ )
+ } else {
+ builder
+ };
+
+ let (service, socket) = builder.finish();
Server::new(stdin, stdout, socket).serve(service).await;