From 5c41f22c2a20a1b8a91ddd6397686bd752591ffc Mon Sep 17 00:00:00 2001
From: Ryan Fowler
Date: Fri, 21 Jul 2023 15:21:21 -0700
Subject: Add support for LSP DidChangeWatchedFiles (#7665)

* Add initial support for LSP DidChangeWatchedFiles

* Move file event Handler to helix-lsp

* Simplify file event handling

* Refactor file event handling

* Block on future within LSP file event handler

* Fully qualify uses of the file_event::Handler type

* Rename ops field to options

* Revert newline removal from helix-view/Cargo.toml

* Ensure file event Handler is cleaned up when lsp client is shutdown---
 helix-term/src/application.rs    | 70 ++++++++++++++++++++++++++++++++++------
 helix-term/src/commands/typed.rs | 15 ++++++++-
 2 files changed, 75 insertions(+), 10 deletions(-)

(limited to 'helix-term/src')

diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index b8950ae0..dc461198 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -5,7 +5,11 @@ use helix_core::{
     path::get_relative_path,
     pos_at_coords, syntax, Selection,
 };
-use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
+use helix_lsp::{
+    lsp::{self, notification::Notification},
+    util::lsp_pos_to_pos,
+    LspProgressMap,
+};
 use helix_view::{
     align_view,
     document::DocumentSavedEventResult,
@@ -1080,17 +1084,65 @@ impl Application {
                             .collect();
                         Ok(json!(result))
                     }
-                    Ok(MethodCall::RegisterCapability(_params)) => {
-                        log::warn!("Ignoring a client/registerCapability request because dynamic capability registration is not enabled. Please report this upstream to the language server");
-                        // Language Servers based on the `vscode-languageserver-node` library often send
-                        // client/registerCapability even though we do not enable dynamic registration
-                        // for any capabilities. We should send a MethodNotFound JSONRPC error in this
-                        // case but that rejects the registration promise in the server which causes an
-                        // exit. So we work around this by ignoring the request and sending back an OK
-                        // response.
+                    Ok(MethodCall::RegisterCapability(params)) => {
+                        if let Some(client) = self
+                            .editor
+                            .language_servers
+                            .iter_clients()
+                            .find(|client| client.id() == server_id)
+                        {
+                            for reg in params.registrations {
+                                match reg.method.as_str() {
+                                    lsp::notification::DidChangeWatchedFiles::METHOD => {
+                                        let Some(options) = reg.register_options else {
+                                            continue;
+                                        };
+                                        let ops: lsp::DidChangeWatchedFilesRegistrationOptions =
+                                            match serde_json::from_value(options) {
+                                                Ok(ops) => ops,
+                                                Err(err) => {
+                                                    log::warn!("Failed to deserialize DidChangeWatchedFilesRegistrationOptions: {err}");
+                                                    continue;
+                                                }
+                                            };
+                                        self.editor.language_servers.file_event_handler.register(
+                                            client.id(),
+                                            Arc::downgrade(client),
+                                            reg.id,
+                                            ops,
+                                        )
+                                    }
+                                    _ => {
+                                        // Language Servers based on the `vscode-languageserver-node` library often send
+                                        // client/registerCapability even though we do not enable dynamic registration
+                                        // for most capabilities. We should send a MethodNotFound JSONRPC error in this
+                                        // case but that rejects the registration promise in the server which causes an
+                                        // exit. So we work around this by ignoring the request and sending back an OK
+                                        // response.
+                                        log::warn!("Ignoring a client/registerCapability request because dynamic capability registration is not enabled. Please report this upstream to the language server");
+                                    }
+                                }
+                            }
+                        }
 
                         Ok(serde_json::Value::Null)
                     }
+                    Ok(MethodCall::UnregisterCapability(params)) => {
+                        for unreg in params.unregisterations {
+                            match unreg.method.as_str() {
+                                lsp::notification::DidChangeWatchedFiles::METHOD => {
+                                    self.editor
+                                        .language_servers
+                                        .file_event_handler
+                                        .unregister(server_id, unreg.id);
+                                }
+                                _ => {
+                                    log::warn!("Received unregistration request for unsupported method: {}", unreg.method);
+                                }
+                            }
+                        }
+                        Ok(serde_json::Value::Null)
+                    }
                 };
 
                 tokio::spawn(language_server!().reply(id, reply));
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index dfc71dfd..063658c6 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -1283,7 +1283,14 @@ fn reload(
     doc.reload(view, &cx.editor.diff_providers, redraw_handle)
         .map(|_| {
             view.ensure_cursor_in_view(doc, scrolloff);
-        })
+        })?;
+    if let Some(path) = doc.path() {
+        cx.editor
+            .language_servers
+            .file_event_handler
+            .file_changed(path.clone());
+    }
+    Ok(())
 }
 
 fn reload_all(
@@ -1324,6 +1331,12 @@ fn reload_all(
 
         let redraw_handle = cx.editor.redraw_handle.clone();
         doc.reload(view, &cx.editor.diff_providers, redraw_handle)?;
+        if let Some(path) = doc.path() {
+            cx.editor
+                .language_servers
+                .file_event_handler
+                .file_changed(path.clone());
+        }
 
         for view_id in view_ids {
             let view = view_mut!(cx.editor, view_id);
-- 
cgit v1.2.3-70-g09d2