Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-lsp/src/file_event.rs')
-rw-r--r--helix-lsp/src/file_event.rs98
1 files changed, 66 insertions, 32 deletions
diff --git a/helix-lsp/src/file_event.rs b/helix-lsp/src/file_event.rs
index 5e7f8ca6..ccfe4558 100644
--- a/helix-lsp/src/file_event.rs
+++ b/helix-lsp/src/file_event.rs
@@ -1,14 +1,19 @@
+use std::path::Path;
use std::{collections::HashMap, path::PathBuf, sync::Weak};
use globset::{GlobBuilder, GlobSetBuilder};
+use helix_core::file_watcher::{EventType, Events, FileSystemDidChange};
+use helix_event::register_hook;
use tokio::sync::mpsc;
use crate::{lsp, Client, LanguageServerId};
enum Event {
- FileChanged {
+ /// file written by helix, special cased to not wait on FS
+ FileWritten {
path: PathBuf,
},
+ FileWatcher(Events),
Register {
client_id: LanguageServerId,
client: Weak<Client>,
@@ -54,6 +59,11 @@ impl Handler {
pub fn new() -> Self {
let (tx, rx) = mpsc::unbounded_channel();
tokio::spawn(Self::run(rx));
+ let tx_ = tx.clone();
+ register_hook!(move |event: &mut FileSystemDidChange| {
+ let _ = tx_.send(Event::FileWatcher(event.fs_events.clone()));
+ Ok(())
+ });
Self { tx }
}
@@ -80,48 +90,72 @@ impl Handler {
}
pub fn file_changed(&self, path: PathBuf) {
- let _ = self.tx.send(Event::FileChanged { path });
+ let _ = self.tx.send(Event::FileWritten { path });
}
pub fn remove_client(&self, client_id: LanguageServerId) {
let _ = self.tx.send(Event::RemoveClient { client_id });
}
+ fn notify_files<'a>(
+ state: &mut HashMap<LanguageServerId, ClientState>,
+ changes: impl Iterator<Item = (&'a Path, lsp::FileChangeType)> + Clone,
+ ) {
+ state.retain(|id, client_state| {
+ let notifications: Vec<_> = changes
+ .clone()
+ .filter(|(path, _)| {
+ client_state
+ .registered
+ .values()
+ .any(|glob| glob.is_match(path))
+ })
+ .filter_map(|(path, typ)| {
+ let uri = lsp::Url::from_file_path(path).ok()?;
+ let event = lsp::FileEvent { uri, typ };
+ Some(event)
+ })
+ .collect();
+ if notifications.is_empty() {
+ return false;
+ }
+ let Some(client) = client_state.client.upgrade() else {
+ log::warn!("LSP client was dropped: {id}");
+ return false;
+ };
+ log::debug!(
+ "Sending didChangeWatchedFiles notification to client '{}'",
+ client.name()
+ );
+ client.did_change_watched_files(notifications);
+ true
+ })
+ }
+
async fn run(mut rx: mpsc::UnboundedReceiver<Event>) {
let mut state: HashMap<LanguageServerId, ClientState> = HashMap::new();
while let Some(event) = rx.recv().await {
match event {
- Event::FileChanged { path } => {
+ Event::FileWatcher(events) => {
+ Self::notify_files(
+ &mut state,
+ events.iter().filter_map(|event| {
+ let ty = match event.ty {
+ EventType::Create => lsp::FileChangeType::CREATED,
+ EventType::Delete => lsp::FileChangeType::DELETED,
+ EventType::Modified => lsp::FileChangeType::CHANGED,
+ EventType::Tempfile => return None,
+ };
+ Some((event.path.as_std_path(), ty))
+ }),
+ );
+ }
+ Event::FileWritten { path } => {
log::debug!("Received file event for {:?}", &path);
-
- state.retain(|id, client_state| {
- if !client_state
- .registered
- .values()
- .any(|glob| glob.is_match(&path))
- {
- return true;
- }
- let Some(client) = client_state.client.upgrade() else {
- log::warn!("LSP client was dropped: {id}");
- return false;
- };
- let Ok(uri) = lsp::Url::from_file_path(&path) else {
- return true;
- };
- log::debug!(
- "Sending didChangeWatchedFiles notification to client '{}'",
- client.name()
- );
- client.did_change_watched_files(vec![lsp::FileEvent {
- uri,
- // We currently always send the CHANGED state
- // since we don't actually have more context at
- // the moment.
- typ: lsp::FileChangeType::CHANGED,
- }]);
- true
- });
+ Self::notify_files(
+ &mut state,
+ [(&*path, lsp::FileChangeType::CHANGED)].iter().cloned(),
+ );
}
Event::Register {
client_id,