Skip to content

Commit

Permalink
lsp: Add a filesystem watch request to our LS
Browse files Browse the repository at this point in the history
Get a notification of any file change/deleteion. This feels like
overdoing it a bit, but we need to reload when resources change
and those can be anything.
  • Loading branch information
hunger committed Oct 10, 2024
1 parent 610a23b commit d87833a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 3 deletions.
5 changes: 5 additions & 0 deletions editors/vscode/src/browser-lsp-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ slint_init(slint_wasm_data).then((_) => {
await the_lsp.process_preview_to_lsp_message(params);
},
);
connection.onDidChangeWatchedFiles(async (param) => {
for (const event of param.changes) {
await the_lsp.trigger_file_watcher(event.uri);
}
});

connection.onDidChangeTextDocument(async (param) => {
await the_lsp.reload_document(
Expand Down
48 changes: 48 additions & 0 deletions tools/lsp/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,43 @@ pub fn request_state(ctx: &std::rc::Rc<Context>) {
}
}

async fn register_file_watcher(ctx: &Context) -> common::Result<()> {
use lsp_types::notification::Notification;

if ctx
.init_param
.capabilities
.workspace
.as_ref()
.and_then(|ws| ws.did_change_watched_files)
.and_then(|wf| wf.dynamic_registration)
.unwrap_or(false)
{
i_slint_core::debug_log!("Registering FS watchers");
let fs_watcher = lsp_types::DidChangeWatchedFilesRegistrationOptions {
watchers: vec![lsp_types::FileSystemWatcher {
glob_pattern: lsp_types::GlobPattern::String("**/*.slint".to_string()),
kind: Some(lsp_types::WatchKind::Change | lsp_types::WatchKind::Delete),
}],
};
ctx.server_notifier
.send_request::<lsp_types::request::RegisterCapability>(
lsp_types::RegistrationParams {
registrations: vec![lsp_types::Registration {
id: "slint.file_watcher.registration".to_string(),
method: lsp_types::notification::DidChangeWatchedFiles::METHOD.to_string(),
register_options: Some(serde_json::to_value(fs_watcher).unwrap()),
}],
},
)?
.await?;
} else {
i_slint_core::debug_log!("FS watchers NOT SUPPORTED!");
}

Ok(())
}

pub struct Context {
pub document_cache: RefCell<DocumentCache>,
pub preview_config: RefCell<common::PreviewConfig>,
Expand Down Expand Up @@ -572,6 +609,7 @@ pub async fn reload_document(
version: Option<i32>,
document_cache: &mut DocumentCache,
) -> common::Result<()> {
i_slint_core::debug_log!("RELOAD FILE: {url}");
let lsp_diags =
reload_document_impl(Some(ctx), content, url.clone(), version, document_cache).await;

Expand All @@ -586,6 +624,11 @@ pub async fn reload_document(
Ok(())
}

pub async fn trigger_file_watcher(_context: &Context, url: &lsp_types::Url) -> common::Result<()> {
i_slint_core::debug_log!("FILE CHANGED: {url}");
Ok(())
}

/// return the token, and the offset within the file
fn token_descr(
document_cache: &mut DocumentCache,
Expand Down Expand Up @@ -1089,6 +1132,11 @@ fn find_element_id_for_highlight(
None
}

pub async fn startup_lsp(ctx: &Context) -> common::Result<()> {
register_file_watcher(ctx).await?;
load_configuration(ctx).await
}

pub async fn load_configuration(ctx: &Context) -> common::Result<()> {
if !ctx
.init_param
Expand Down
17 changes: 14 additions & 3 deletions tools/lsp/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ use common::Result;
use language::*;

use lsp_types::notification::{
DidChangeConfiguration, DidChangeTextDocument, DidOpenTextDocument, Notification,
DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidOpenTextDocument,
Notification,
};
use lsp_types::{
DidChangeTextDocumentParams, DidChangeWatchedFilesParams, DidOpenTextDocumentParams,
InitializeParams, Url,
};
use lsp_types::{DidChangeTextDocumentParams, DidOpenTextDocumentParams, InitializeParams, Url};

use clap::{Args, Parser, Subcommand};
use itertools::Itertools;
Expand Down Expand Up @@ -352,7 +356,7 @@ fn main_loop(connection: Connection, init_param: InitializeParams, cli_args: Cli
});

let mut futures = Vec::<Pin<Box<dyn Future<Output = Result<()>>>>>::new();
let mut first_future = Box::pin(load_configuration(&ctx));
let mut first_future = Box::pin(startup_lsp(&ctx));

// We are waiting in this loop for two kind of futures:
// - The compiler future should always be ready immediately because we do not set a callback to load files
Expand Down Expand Up @@ -448,6 +452,13 @@ async fn handle_notification(req: lsp_server::Notification, ctx: &Rc<Context>) -
.await
}
DidChangeConfiguration::METHOD => load_configuration(ctx).await,
DidChangeWatchedFiles::METHOD => {
let params: DidChangeWatchedFilesParams = serde_json::from_value(req.params)?;
for fe in params.changes {
trigger_file_watcher(ctx, &fe.uri).await?;
}
Ok(())
}

#[cfg(any(feature = "preview-builtin", feature = "preview-external"))]
"slint/showPreview" => {
Expand Down
26 changes: 26 additions & 0 deletions tools/lsp/wasm_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,32 @@ impl SlintServer {
Ok(to_value(&language::server_initialize_result(&serde_wasm_bindgen::from_value(cap)?))?)
}

#[wasm_bindgen]
pub async fn startup_lsp(&self) -> js_sys::Promise {
let ctx = self.ctx.clone();
let guard = self.reentry_guard.clone();
wasm_bindgen_futures::future_to_promise(async move {
let _lock = ReentryGuard::lock(guard).await;
language::startup_lsp(&ctx).await.map_err(|e| JsError::new(&e.to_string()))?;
Ok(JsValue::UNDEFINED)
})
}

#[wasm_bindgen]
pub fn trigger_file_watcher(&self, url: JsValue) -> js_sys::Promise {
let ctx = self.ctx.clone();
let guard = self.reentry_guard.clone();

wasm_bindgen_futures::future_to_promise(async move {
let _lock = ReentryGuard::lock(guard).await;
let url: lsp_types::Url = serde_wasm_bindgen::from_value(url)?;
language::trigger_file_watcher(&ctx, &url)
.await
.map_err(|e| JsError::new(&e.to_string()))?;
Ok(JsValue::UNDEFINED)
})
}

#[wasm_bindgen]
pub fn reload_document(&self, content: String, uri: JsValue, version: i32) -> js_sys::Promise {
let ctx = self.ctx.clone();
Expand Down
6 changes: 6 additions & 0 deletions tools/slintpad/src/worker/lsp_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ slint_init({}).then(() => {
},
);

connection.onDidChangeWatchedFiles(async (param) => {
for (const event of param.changes) {
await the_lsp.trigger_file_watcher(event.uri);
}
});

connection.onDidChangeTextDocument(async (param) => {
await the_lsp.reload_document(
param.contentChanges[param.contentChanges.length - 1].text,
Expand Down

0 comments on commit d87833a

Please sign in to comment.