Skip to content

Commit 1c3707d

Browse files
authored
feat(core): watcher + LSP improvements (#5283)
1 parent 77f2382 commit 1c3707d

File tree

18 files changed

+303
-191
lines changed

18 files changed

+303
-191
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_cli/src/commands/daemon.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use crate::{
55
use biome_console::{ConsoleExt, markup};
66
use biome_fs::OsFileSystem;
77
use biome_lsp::ServerFactory;
8-
use biome_service::{TransportError, WorkspaceError, WorkspaceWatcher, workspace::WorkspaceClient};
8+
use biome_service::{
9+
TransportError, WatcherInstruction, WorkspaceError, WorkspaceWatcher,
10+
workspace::WorkspaceClient,
11+
};
912
use camino::{Utf8Path, Utf8PathBuf};
1013
use std::{env, fs};
1114
use tokio::io;
@@ -73,7 +76,7 @@ pub(crate) fn run_server(
7376
) -> Result<(), CliDiagnostic> {
7477
setup_tracing_subscriber(log_path.as_deref(), log_file_name_prefix.as_deref());
7578

76-
let (mut watcher, instruction_channel, _) = WorkspaceWatcher::new()?;
79+
let (mut watcher, instruction_channel) = WorkspaceWatcher::new()?;
7780

7881
let rt = Runtime::new()?;
7982
let factory = ServerFactory::new(stop_on_disconnect, instruction_channel.sender.clone());
@@ -99,6 +102,7 @@ pub(crate) fn run_server(
99102
}
100103
_ = cancellation.notified() => {
101104
tracing::info!("Received shutdown signal");
105+
let _ = instruction_channel.sender.send(WatcherInstruction::Stop);
102106
Ok(())
103107
}
104108
}

crates/biome_cli/src/service/unix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ pub(crate) async fn print_socket() -> io::Result<()> {
196196
pub(crate) async fn run_daemon(factory: ServerFactory) -> io::Result<Infallible> {
197197
let path = get_socket_name();
198198

199-
info!("Trying to connect to socket {}", path.as_str());
199+
info!("Trying to connect to socket {path}");
200200

201201
// Try to remove the socket file if it already exists
202202
if path.exists() {
203-
info!("Remove socket folder {}", path.as_str());
203+
info!("Remove socket {path}");
204204
fs::remove_file(&path)?;
205205
}
206206

crates/biome_dependency_graph/src/dependency_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl DependencyGraph {
149149
.is_err()
150150
{
151151
break;
152-
};
152+
}
153153
parent = path.parent();
154154
}
155155
}

crates/biome_lsp/src/handlers/text_document.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::sync::Arc;
2+
13
use crate::diagnostics::LspError;
24
use crate::utils::apply_document_changes;
35
use crate::{documents::Document, session::Session};
@@ -19,7 +21,7 @@ use tracing::{debug, error, field, info};
1921
)
2022
)]
2123
pub(crate) async fn did_open(
22-
session: &Session,
24+
session: &Arc<Session>,
2325
params: lsp_types::DidOpenTextDocumentParams,
2426
) -> Result<(), LspError> {
2527
let url = params.text_document.uri;
@@ -41,7 +43,7 @@ pub(crate) async fn did_open(
4143
path: parent_path.clone(),
4244
open_uninitialized: true,
4345
})?;
44-
session.insert_project(parent_path, project_key);
46+
session.insert_and_scan_project(project_key, parent_path);
4547
project_key
4648
}
4749
};

crates/biome_lsp/src/server.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use biome_diagnostics::panic::PanicError;
1111
use biome_fs::{ConfigName, FileSystem, MemoryFileSystem, OsFileSystem};
1212
use biome_service::workspace::{
1313
CloseProjectParams, OpenProjectParams, RageEntry, RageParams, RageResult,
14+
ServiceDataNotification,
1415
};
1516
use biome_service::{WatcherInstruction, WorkspaceServer};
1617
use crossbeam::channel::{Sender, bounded};
@@ -22,7 +23,7 @@ use std::panic::RefUnwindSafe;
2223
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2324
use std::sync::{Arc, Mutex};
2425
use tokio::io::{AsyncRead, AsyncWrite};
25-
use tokio::sync::Notify;
26+
use tokio::sync::{Notify, watch};
2627
use tokio::task::spawn_blocking;
2728
use tower_lsp::jsonrpc::Result as LspResult;
2829
use tower_lsp::{ClientSocket, lsp_types::*};
@@ -398,11 +399,7 @@ impl LanguageServer for LSPServer {
398399
match result {
399400
Ok(project_key) => {
400401
self.session
401-
.insert_project(project_path.clone(), project_key);
402-
403-
self.session
404-
.scan_project_folder(project_key, project_path)
405-
.await;
402+
.insert_and_scan_project(project_key, project_path.clone());
406403

407404
self.session.update_all_diagnostics().await;
408405
}
@@ -553,6 +550,12 @@ pub struct ServerFactory {
553550
/// This shared flag is set to true once at least one sessions has been
554551
/// initialized on this server instance
555552
is_initialized: Arc<AtomicBool>,
553+
554+
/// Receiver for service data notifications.
555+
///
556+
/// If we receive a notification here, diagnostics for open documents are
557+
/// all refreshed.
558+
service_data_rx: watch::Receiver<ServiceDataNotification>,
556559
}
557560

558561
impl Default for ServerFactory {
@@ -564,29 +567,34 @@ impl Default for ServerFactory {
564567
impl ServerFactory {
565568
/// Regular constructor for use in the daemon.
566569
pub fn new(stop_on_disconnect: bool, instruction_tx: Sender<WatcherInstruction>) -> Self {
570+
let (service_data_tx, service_data_rx) = watch::channel(ServiceDataNotification::Updated);
567571
Self {
568572
cancellation: Arc::default(),
569573
workspace: Arc::new(WorkspaceServer::new(
570574
Box::new(OsFileSystem::default()),
571575
instruction_tx,
576+
service_data_tx,
572577
)),
573578
sessions: Sessions::default(),
574579
next_session_key: AtomicU64::new(0),
575580
stop_on_disconnect,
576581
is_initialized: Arc::default(),
582+
service_data_rx,
577583
}
578584
}
579585

580586
/// Constructor for use in tests.
581587
pub fn new_with_fs(fs: Box<dyn FileSystem>) -> Self {
582-
let (tx, _) = bounded(0);
588+
let (watcher_tx, _) = bounded(0);
589+
let (service_data_tx, service_data_rx) = watch::channel(ServiceDataNotification::Updated);
583590
Self {
584591
cancellation: Arc::default(),
585-
workspace: Arc::new(WorkspaceServer::new(fs, tx)),
592+
workspace: Arc::new(WorkspaceServer::new(fs, watcher_tx, service_data_tx)),
586593
sessions: Sessions::default(),
587594
next_session_key: AtomicU64::new(0),
588595
stop_on_disconnect: true,
589596
is_initialized: Arc::default(),
597+
service_data_rx,
590598
}
591599
}
592600

@@ -597,7 +605,13 @@ impl ServerFactory {
597605
let session_key = SessionKey(self.next_session_key.fetch_add(1, Ordering::Relaxed));
598606

599607
let mut builder = LspService::build(move |client| {
600-
let session = Session::new(session_key, client, workspace, self.cancellation.clone());
608+
let session = Session::new(
609+
session_key,
610+
client,
611+
workspace,
612+
self.cancellation.clone(),
613+
self.service_data_rx.clone(),
614+
);
601615
let handle = Arc::new(session);
602616

603617
let mut sessions = self.sessions.lock().unwrap();

crates/biome_lsp/src/server.tests.rs

+40-26
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ macro_rules! url {
5252
};
5353
}
5454

55+
macro_rules! clear_notifications {
56+
($channel:expr) => {
57+
if $channel
58+
.has_changed()
59+
.expect("Channel should not be closed")
60+
{
61+
let _ = $channel.changed().await;
62+
}
63+
};
64+
}
65+
5566
fn fixable_diagnostic(line: u32) -> Result<lsp::Diagnostic> {
5667
Ok(lsp::Diagnostic {
5768
range: Range {
@@ -2992,9 +3003,9 @@ export function bar() {
29923003
fs.create_file("foo.ts", FOO_CONTENT);
29933004
fs.create_file("bar.ts", BAR_CONTENT);
29943005

2995-
let (mut watcher, instruction_channel, notification_channel) = WorkspaceWatcher::new()?;
3006+
let (mut watcher, instruction_channel) = WorkspaceWatcher::new()?;
29963007

2997-
let factory = ServerFactory::new(true, instruction_channel.sender.clone());
3008+
let mut factory = ServerFactory::new(true, instruction_channel.sender.clone());
29983009

29993010
let workspace = factory.workspace();
30003011
tokio::task::spawn_blocking(move || {
@@ -3062,14 +3073,15 @@ export function bar() {
30623073
"This import is part of a cycle."
30633074
);
30643075

3065-
let _ = notification_channel.receiver.try_recv(); // Clear notification, if any.
3076+
clear_notifications!(factory.service_data_rx);
30663077

30673078
// ARRANGE: Remove `bar.ts`.
30683079
std::fs::remove_file(fs.working_directory.join("bar.ts")).expect("Cannot remove bar.ts");
30693080

3070-
notification_channel
3071-
.receiver
3072-
.recv()
3081+
factory
3082+
.service_data_rx
3083+
.changed()
3084+
.await
30733085
.expect("Expected notification");
30743086

30753087
// ACT: Pull diagnostics.
@@ -3094,13 +3106,14 @@ export function bar() {
30943106
assert_eq!(result.diagnostics.len(), 0);
30953107

30963108
// ARRANGE: Recreate `bar.ts`.
3097-
let _ = notification_channel.receiver.try_recv(); // Clear notification, if any.
3109+
clear_notifications!(factory.service_data_rx);
30983110

30993111
fs.create_file("bar.ts", BAR_CONTENT);
31003112

3101-
notification_channel
3102-
.receiver
3103-
.recv()
3113+
factory
3114+
.service_data_rx
3115+
.changed()
3116+
.await
31043117
.expect("Expected notification");
31053118

31063119
// ACT: Pull diagnostics.
@@ -3129,13 +3142,14 @@ export function bar() {
31293142
);
31303143

31313144
// ARRANGE: Fix `bar.ts`.
3132-
let _ = notification_channel.receiver.try_recv(); // Clear notification, if any.
3145+
clear_notifications!(factory.service_data_rx);
31333146

31343147
fs.create_file("bar.ts", BAR_CONTENT_FIXED);
31353148

3136-
notification_channel
3137-
.receiver
3138-
.recv()
3149+
factory
3150+
.service_data_rx
3151+
.changed()
3152+
.await
31393153
.expect("Expected notification");
31403154

31413155
// ACT: Pull diagnostics.
@@ -3159,7 +3173,6 @@ export function bar() {
31593173
// ASSERT: Diagnostic should disappear again with a fixed `bar.ts`.
31603174
assert_eq!(result.diagnostics.len(), 0);
31613175

3162-
let _ = instruction_channel.sender.send(WatcherInstruction::Stop);
31633176
server.shutdown().await?;
31643177
reader.abort();
31653178

@@ -3201,9 +3214,9 @@ export function bar() {
32013214
fs.create_file("foo.ts", FOO_CONTENT);
32023215
fs.create_file("utils/bar.ts", BAR_CONTENT);
32033216

3204-
let (mut watcher, instruction_channel, notification_channel) = WorkspaceWatcher::new()?;
3217+
let (mut watcher, instruction_channel) = WorkspaceWatcher::new()?;
32053218

3206-
let factory = ServerFactory::new(true, instruction_channel.sender.clone());
3219+
let mut factory = ServerFactory::new(true, instruction_channel.sender.clone());
32073220

32083221
let workspace = factory.workspace();
32093222
tokio::task::spawn_blocking(move || {
@@ -3271,7 +3284,7 @@ export function bar() {
32713284
"This import is part of a cycle."
32723285
);
32733286

3274-
let _ = notification_channel.receiver.try_recv(); // Clear notification, if any.
3287+
clear_notifications!(factory.service_data_rx);
32753288

32763289
// ARRANGE: Move `utils` directory.
32773290
std::fs::rename(
@@ -3280,9 +3293,10 @@ export function bar() {
32803293
)
32813294
.expect("Cannot move utils");
32823295

3283-
notification_channel
3284-
.receiver
3285-
.recv()
3296+
factory
3297+
.service_data_rx
3298+
.changed()
3299+
.await
32863300
.expect("Expected notification");
32873301

32883302
// ACT: Pull diagnostics.
@@ -3308,17 +3322,18 @@ export function bar() {
33083322
assert_eq!(result.diagnostics.len(), 0);
33093323

33103324
// ARRANGE: Move `utils` back.
3311-
let _ = notification_channel.receiver.try_recv(); // Clear notification, if any.
3325+
clear_notifications!(factory.service_data_rx);
33123326

33133327
std::fs::rename(
33143328
fs.working_directory.join("bin"),
33153329
fs.working_directory.join("utils"),
33163330
)
33173331
.expect("Cannot restore utils");
33183332

3319-
notification_channel
3320-
.receiver
3321-
.recv()
3333+
factory
3334+
.service_data_rx
3335+
.changed()
3336+
.await
33223337
.expect("Expected notification");
33233338

33243339
// ACT: Pull diagnostics.
@@ -3346,7 +3361,6 @@ export function bar() {
33463361
"This import is part of a cycle."
33473362
);
33483363

3349-
let _ = instruction_channel.sender.send(WatcherInstruction::Stop);
33503364
server.shutdown().await?;
33513365
reader.abort();
33523366

0 commit comments

Comments
 (0)