From 373012ee1ccf6cc9d2c00b450544e8e46e00eb59 Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Thu, 6 Mar 2025 11:54:15 +0100 Subject: [PATCH] fix(core): fix workspace scanner in LSP --- crates/biome_cli/src/commands/mod.rs | 1 + crates/biome_lsp/src/server.rs | 1 - crates/biome_lsp/src/server.tests.rs | 2 ++ crates/biome_lsp/src/session.rs | 28 +++++++++------ crates/biome_service/src/workspace.rs | 3 ++ crates/biome_service/src/workspace.tests.rs | 3 ++ crates/biome_service/src/workspace/server.rs | 36 ++++++++++++++++--- .../src/workspace/server.tests.rs | 1 + crates/biome_service/src/workspace/watcher.rs | 1 + 9 files changed, 60 insertions(+), 16 deletions(-) diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs index 910fcb6c3787..13ddc1f750cf 100644 --- a/crates/biome_cli/src/commands/mod.rs +++ b/crates/biome_cli/src/commands/mod.rs @@ -800,6 +800,7 @@ pub(crate) trait CommandRunner: Sized { project_key, path: Some(project_path), watch: cli_options.use_server, + force: false, // TODO: Maybe we'll want a CLI flag for this. })?; for diagnostic in result.diagnostics { if diagnostic.severity() >= Severity::Error { diff --git a/crates/biome_lsp/src/server.rs b/crates/biome_lsp/src/server.rs index 9851e3580b83..2cfa78da0ab6 100644 --- a/crates/biome_lsp/src/server.rs +++ b/crates/biome_lsp/src/server.rs @@ -260,7 +260,6 @@ impl LanguageServer for LSPServer { params.workspace_folders, ); - // let init = InitializeResult { capabilities: server_capabilities, server_info: Some(ServerInfo { diff --git a/crates/biome_lsp/src/server.tests.rs b/crates/biome_lsp/src/server.tests.rs index e9898b88e824..bc6446ca59be 100644 --- a/crates/biome_lsp/src/server.tests.rs +++ b/crates/biome_lsp/src/server.tests.rs @@ -3042,6 +3042,7 @@ export function bar() { project_key, path: None, watch: true, + force: false, }, ) .await? @@ -3253,6 +3254,7 @@ export function bar() { project_key, path: None, watch: true, + force: false, }, ) .await? diff --git a/crates/biome_lsp/src/session.rs b/crates/biome_lsp/src/session.rs index 8cc30ca898be..552486eb036d 100644 --- a/crates/biome_lsp/src/session.rs +++ b/crates/biome_lsp/src/session.rs @@ -34,6 +34,7 @@ use std::sync::Arc; use std::sync::RwLock; use std::sync::atomic::Ordering; use std::sync::atomic::{AtomicBool, AtomicU8}; +use tokio::spawn; use tokio::sync::Notify; use tokio::sync::OnceCell; use tokio::sync::watch; @@ -572,31 +573,36 @@ impl Session { project_path: BiomePath, ) { let session = self.clone(); - let scan_project = async move || { + let scan_project = move || { let result = session .workspace .scan_project_folder(ScanProjectFolderParams { project_key, path: Some(project_path), watch: true, + force: false, }); match result { Ok(result) => { - for diagnostic in result.diagnostics { - let message = PrintDescription(&diagnostic).to_string(); + spawn(async move { + for diagnostic in result.diagnostics { + let message = PrintDescription(&diagnostic).to_string(); + session + .client + .log_message(MessageType::ERROR, message) + .await; + } + }); + } + Err(err) => { + let message = PrintDescription(&err).to_string(); + spawn(async move { session .client .log_message(MessageType::ERROR, message) .await; - } - } - Err(err) => { - let message = PrintDescription(&err).to_string(); - session - .client - .log_message(MessageType::ERROR, message) - .await; + }); } } }; diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index 25e3e0eab6c0..a6dfb88fe73c 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -1029,6 +1029,9 @@ pub struct ScanProjectFolderParams { /// /// Does nothing if the watcher is already watching this path. pub watch: bool, + + /// Forces scanning of the folder, even if it is already being watched. + pub force: bool, } #[derive(Debug, serde::Serialize, serde::Deserialize)] diff --git a/crates/biome_service/src/workspace.tests.rs b/crates/biome_service/src/workspace.tests.rs index ba81c85054c4..c09f3f8a43da 100644 --- a/crates/biome_service/src/workspace.tests.rs +++ b/crates/biome_service/src/workspace.tests.rs @@ -326,6 +326,7 @@ fn files_loaded_by_the_scanner_are_only_unloaded_when_the_project_is_unregistere project_key, path: None, watch: false, + force: false, }) .unwrap(); @@ -414,6 +415,7 @@ fn too_large_files_are_tracked_but_not_parsed() { project_key, path: None, watch: false, + force: false, }) .unwrap(); @@ -470,6 +472,7 @@ fn plugins_are_loaded_and_used_during_analysis() { project_key, path: None, watch: false, + force: false, }) .unwrap(); diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 9211f00489de..3b9eed179829 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -1,6 +1,7 @@ use std::panic::RefUnwindSafe; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use std::time::Duration; use append_only_vec::AppendOnlyVec; use biome_analyze::AnalyzerPluginVec; @@ -26,7 +27,7 @@ use biome_project_layout::ProjectLayout; use biome_rowan::NodeCache; use camino::{Utf8Path, Utf8PathBuf}; use crossbeam::channel::Sender; -use papaya::{Compute, HashMap, Operation}; +use papaya::{Compute, HashMap, HashSet, Operation}; use rustc_hash::{FxBuildHasher, FxHashMap}; use tokio::sync::watch; use tracing::{error, info, instrument, warn}; @@ -109,6 +110,9 @@ pub struct WorkspaceServer { /// Channel sender for instructions to the [crate::WorkspaceWatcher]. watcher_tx: Sender, + /// Set containing all the watched folders. + watched_folders: HashSet, + /// Channel sender for sending notifications of service data updates. pub(super) notification_tx: watch::Sender, } @@ -142,6 +146,7 @@ impl WorkspaceServer { node_cache: Default::default(), fs, watcher_tx, + watched_folders: Default::default(), notification_tx, } } @@ -819,7 +824,23 @@ impl Workspace for WorkspaceServer { .or_else(|| self.projects.get_project_path(params.project_key)) .ok_or_else(WorkspaceError::no_project)?; + let should_scan = params.force + || !self + .watched_folders + .pin() + .iter() + .any(|watched_folder| path.starts_with(watched_folder)); + if !should_scan { + // No need to scan folders that are already being watched. + return Ok(ScanProjectFolderResult { + diagnostics: Vec::new(), + duration: Duration::from_millis(0), + }); + } + if params.watch { + self.watched_folders.pin().insert(path.clone()); + let _ = self .watcher_tx .try_send(WatcherInstruction::WatchFolder(path.clone())); @@ -839,9 +860,16 @@ impl Workspace for WorkspaceServer { .get_project_path(params.project_key) .ok_or_else(WorkspaceError::no_project)?; - let _ = self - .watcher_tx - .try_send(WatcherInstruction::UnwatchFolder(project_path.clone())); + self.watched_folders.pin().retain(|watched_folder| { + if watched_folder.starts_with(&project_path) { + let _ = self + .watcher_tx + .try_send(WatcherInstruction::UnwatchFolder(watched_folder.clone())); + false + } else { + true + } + }); // Limit the scope of the pin and the lock inside. { diff --git a/crates/biome_service/src/workspace/server.tests.rs b/crates/biome_service/src/workspace/server.tests.rs index 9f1b4b4adbb5..cad9d3b3639c 100644 --- a/crates/biome_service/src/workspace/server.tests.rs +++ b/crates/biome_service/src/workspace/server.tests.rs @@ -28,6 +28,7 @@ fn commonjs_file_rejects_import_statement() { project_key, path: Some(BiomePath::new("/")), watch: false, + force: false, }) .unwrap(); diff --git a/crates/biome_service/src/workspace/watcher.rs b/crates/biome_service/src/workspace/watcher.rs index 8716b03e600c..ca443bc9b96a 100644 --- a/crates/biome_service/src/workspace/watcher.rs +++ b/crates/biome_service/src/workspace/watcher.rs @@ -93,6 +93,7 @@ impl WorkspaceServer { project_key, path: Some(path.into()), watch: false, // It's already being watched. + force: true, }) .map(|_| ()) }