fix(rome_lsp): handle changes to workspace settings
MODIFY
Cargo.lock
Cargo.lock
@@ -477,6 +477,7 @@ checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
"futures-channel",
"futures-core",
+ "futures-executor",
"futures-io",
"futures-sink",
"futures-task",
@@ -499,6 +500,17 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
+[[package]]
+name = "futures-executor"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
[[package]]
name = "futures-io"
version = "0.3.19"
@@ -1357,6 +1369,7 @@ name = "rome_lsp"
version = "0.1.0"
dependencies = [
"anyhow",
+ "futures",
"indexmap",
"lspower",
"parking_lot 0.12.0",

MODIFY
crates/rome_lsp/Cargo.toml
crates/rome_lsp/Cargo.toml
@@ -22,3 +22,4 @@ tracing = { version = "0.1.31", features = ["max_level_trace", "release_max_leve
tracing-tree = "0.2.0"
tracing-subscriber = "0.3.5"
parking_lot = "0.12.0"
+futures = "0.3"

MODIFY
crates/rome_lsp/src/server.rs
crates/rome_lsp/src/server.rs
-use lspower::jsonrpc::Error as LspError;
use lspower::jsonrpc::Result as LspResult;
use lspower::lsp::*;
use lspower::{Client, LanguageServer, LspService, Server};
use rome_analyze::AnalysisServer;
use std::sync::Arc;
use tokio::io::{Stdin, Stdout};
-use tracing::{error, info, trace};
+use tracing::{error, info};
use crate::capabilities::server_capabilities;
-use crate::config::CONFIGURATION_SECTION;
use crate::handlers;
use crate::handlers::formatting::{
to_format_options, FormatOnTypeParams, FormatParams, FormatRangeParams,
@@ -34,17 +32,6 @@ impl LanguageServer for LSPServer {
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
info!("Starting Rome Language Server...");
- if let Some(value) = params.initialization_options {
- self.session
- .config
- .write()
- .set_workspace_settings(value)
- .map_err(|err| {
- error!("Cannot set workspace settings: {}", err);
- LspError::internal_error()
- })?;
- }
-
self.session
.client_capabilities
.write()
@@ -59,30 +46,25 @@ impl LanguageServer for LSPServer {
}
async fn initialized(&self, _: InitializedParams) {
- let item = ConfigurationItem {
- scope_uri: None,
- section: Some(String::from(CONFIGURATION_SECTION)),
+ self.session.fetch_client_configuration().await;
+
+ let msg = format!("Server initialized with PID: {}", std::process::id());
+ self.client.log_message(MessageType::INFO, msg).await;
+
+ if self.session.can_register_did_change_configuration() {
+ let registration = Registration {
+ id: "workspace/didChangeConfiguration".to_string(),
+ method: "workspace/didChangeConfiguration".to_string(),
+ register_options: None,
};
- let items = vec![item];
- let configurations = self.client.configuration(items).await;
- if let Ok(configurations) = configurations {
- configurations.into_iter().next().and_then(|configuration| {
- self.session
- .config
- .write()
- .set_workspace_settings(configuration)
- .map_err(|err| {
- error!("Cannot set workspace settings: {}", err);
- })
- .ok()
- });
- } else {
- trace!("Cannot read configuration from the client");
+ if let Err(e) = self.client.register_capability(vec![registration]).await {
+ error!("Error registering didChangeConfiguration capability: {}", e);
+ }
}
- let msg = format!("Server initialized with PID: {}", std::process::id());
- self.client.log_message(MessageType::INFO, msg).await;
+ // Diagnostics are disabled by default, so update them after fetching workspace config
+ self.session.update_all_diagnostics().await;
}
async fn shutdown(&self) -> LspResult<()> {
@@ -176,15 +158,13 @@ impl LanguageServer for LSPServer {
Ok(edits)
}
- async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
- let result = self
- .session
- .config
- .write()
- .set_workspace_settings(params.settings);
+ async fn did_change_configuration(&self, _params: DidChangeConfigurationParams) {
+ let diags_enabled_prev = self.session.diagnostics_enabled();
+
+ self.session.fetch_client_configuration().await;
- if let Err(err) = result {
- error!("Cannot set workspace settings: {}", err);
+ if diags_enabled_prev != self.session.diagnostics_enabled() {
+ self.session.update_all_diagnostics().await;
}
}

MODIFY
crates/rome_lsp/src/session.rs
crates/rome_lsp/src/session.rs
use crate::config::Config;
+use crate::config::CONFIGURATION_SECTION;
+
use crate::{documents::Document, handlers, url_interner::UrlInterner};
+use futures::stream::futures_unordered::FuturesUnordered;
+use futures::StreamExt;
use lspower::jsonrpc::Error as LspError;
use lspower::lsp;
use parking_lot::RwLock;
use rome_analyze::{AnalysisServer, FileId};
use std::{collections::HashMap, error::Error, fmt::Display};
+use tracing::{error, trace};
/// Represents the state of an LSP server session.
pub(crate) struct Session {
@@ -104,12 +109,11 @@ impl Session {
/// them to the client. Called from [`handlers::text_document`] when a file's
/// contents changes.
pub(crate) async fn update_diagnostics(&self, url: lsp::Url) -> anyhow::Result<()> {
- let workspace_settings = self.config.read().get_workspace_settings();
- if !workspace_settings.analysis.enable_diagnostics {
- return Ok(());
- }
let doc = self.document(&url)?;
+ let workspace_settings = self.config.read().get_workspace_settings();
+
+ let diagnostics = if workspace_settings.analysis.enable_diagnostics {
let file_id = doc.file_id();
let mut analysis_server = AnalysisServer::default();
analysis_server.set_file_text(file_id, doc.text);
@@ -118,7 +122,11 @@ impl Session {
handlers::analysis::diagnostics(analysis_server, file_id)
});
- let diagnostics = handle.await??;
+ handle.await??
+ } else {
+ // Sending empty vector clears published diagnostics
+ vec![]
+ };
let version = self.document_version(&url)?;
@@ -129,4 +137,65 @@ impl Session {
}
Ok(())
}
+
+ /// Updates diagnostics for every [`Document`] in this [`Session`]
+ pub(crate) async fn update_all_diagnostics(&self) {
+ let mut futures: FuturesUnordered<_> = self
+ .documents
+ .read()
+ .keys()
+ .cloned()
+ .map(|url| self.update_diagnostics(url))
+ .collect();
+
+ while let Some(result) = futures.next().await {
+ if let Err(e) = result {
+ error!("Error while updating diagnostics: {}", e);
+ }
+ }
+ }
+
+ /// True if the client supports dynamic registration of "workspace/didChangeConfiguration" requests
+ pub(crate) fn can_register_did_change_configuration(&self) -> bool {
+ self.client_capabilities
+ .read()
+ .as_ref()
+ .and_then(|c| c.workspace.as_ref())
+ .and_then(|c| c.did_change_configuration)
+ .and_then(|c| c.dynamic_registration)
+ == Some(true)
+ }
+
+ /// Checks `analysis.enable_diagnostics` in this session's workspace settings`
+ pub(crate) fn diagnostics_enabled(&self) -> bool {
+ self.config
+ .read()
+ .get_workspace_settings()
+ .analysis
+ .enable_diagnostics
+ }
+
+ /// Requests "workspace/configuration" from client and updates Session config
+ pub(crate) async fn fetch_client_configuration(&self) {
+ let item = lsp::ConfigurationItem {
+ scope_uri: None,
+ section: Some(String::from(CONFIGURATION_SECTION)),
+ };
+ let items = vec![item];
+ let configurations = self.client.configuration(items).await;
+
+ if let Ok(configurations) = configurations {
+ configurations.into_iter().next().and_then(|configuration| {
+ self.config
+ .write()
+ .set_workspace_settings(configuration)
+ .map_err(|err| {
+ error!("Cannot set workspace settings: {}", err);
+ })
+ .ok()
+ });
+ } else {
+ trace!("Cannot read configuration from the client");
+ }
+ }
}

fix(rome_lsp): handle changes to workspace settings (#2250)