mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-07 19:19:12 +00:00
lsp: Handle client/unregisterCapability
to fix gopls (#12086)
This fixes #10224 by handling `client/unregisterCapability` requests that have a `workspace/didChangeWatchedFiles` method. While debugging the issue, I found out that `gopls` seems to block indefinitely when there's no reply to the `client/unregisterCapability` request. Even an empty response would fix the issue. Seems like gopls 15.x and later seem to handle nested subfolders well, but do not handle unanswered requests. Instead of replying with an empty response, I decided to change how we handle file watching and keep a list of all registered paths so that we can then unregister paths and recreate the glob patterns. Release Notes: - Fixed `gopls` not working correctly when the `go.mod` file was in a subfolder and not the root folder of the project opened in Zed. ([#10224](https://github.com/zed-industries/zed/issues/10224)).
This commit is contained in:
parent
0563472832
commit
b89f360199
1 changed files with 81 additions and 5 deletions
|
@ -55,9 +55,9 @@ use language::{
|
|||
use log::error;
|
||||
use lsp::{
|
||||
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
|
||||
DocumentHighlightKind, Edit, LanguageServer, LanguageServerBinary, LanguageServerId,
|
||||
LspRequestFuture, MessageActionItem, OneOf, ServerCapabilities, ServerHealthStatus,
|
||||
ServerStatus, TextEdit,
|
||||
DocumentHighlightKind, Edit, FileSystemWatcher, LanguageServer, LanguageServerBinary,
|
||||
LanguageServerId, LspRequestFuture, MessageActionItem, OneOf, ServerCapabilities,
|
||||
ServerHealthStatus, ServerStatus, TextEdit,
|
||||
};
|
||||
use lsp_command::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -167,6 +167,8 @@ pub struct Project {
|
|||
last_formatting_failure: Option<String>,
|
||||
last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
|
||||
language_server_watched_paths: HashMap<LanguageServerId, HashMap<WorktreeId, GlobSet>>,
|
||||
language_server_watcher_registrations:
|
||||
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
|
||||
client: Arc<client::Client>,
|
||||
next_entry_id: Arc<AtomicUsize>,
|
||||
join_project_response_message_id: u32,
|
||||
|
@ -725,6 +727,7 @@ impl Project {
|
|||
last_formatting_failure: None,
|
||||
last_workspace_edits_by_language_server: Default::default(),
|
||||
language_server_watched_paths: HashMap::default(),
|
||||
language_server_watcher_registrations: HashMap::default(),
|
||||
buffers_being_formatted: Default::default(),
|
||||
buffers_needing_diff: Default::default(),
|
||||
git_diff_debouncer: DebouncedDelay::new(),
|
||||
|
@ -875,6 +878,7 @@ impl Project {
|
|||
last_formatting_failure: None,
|
||||
last_workspace_edits_by_language_server: Default::default(),
|
||||
language_server_watched_paths: HashMap::default(),
|
||||
language_server_watcher_registrations: HashMap::default(),
|
||||
opened_buffers: Default::default(),
|
||||
buffers_being_formatted: Default::default(),
|
||||
buffers_needing_diff: Default::default(),
|
||||
|
@ -3471,7 +3475,7 @@ impl Project {
|
|||
let options = serde_json::from_value(options)?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.on_lsp_did_change_watched_files(
|
||||
server_id, options, cx,
|
||||
server_id, ®.id, options, cx,
|
||||
);
|
||||
})?;
|
||||
}
|
||||
|
@ -3483,6 +3487,27 @@ impl Project {
|
|||
})
|
||||
.detach();
|
||||
|
||||
language_server
|
||||
.on_request::<lsp::request::UnregisterCapability, _, _>({
|
||||
let this = this.clone();
|
||||
move |params, mut cx| {
|
||||
let this = this.clone();
|
||||
async move {
|
||||
for unreg in params.unregisterations.iter() {
|
||||
if unreg.method == "workspace/didChangeWatchedFiles" {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.on_lsp_unregister_did_change_watched_files(
|
||||
server_id, &unreg.id, cx,
|
||||
);
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
language_server
|
||||
.on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
|
||||
let adapter = adapter.clone();
|
||||
|
@ -4208,16 +4233,67 @@ impl Project {
|
|||
fn on_lsp_did_change_watched_files(
|
||||
&mut self,
|
||||
language_server_id: LanguageServerId,
|
||||
registration_id: &str,
|
||||
params: DidChangeWatchedFilesRegistrationOptions,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let registrations = self
|
||||
.language_server_watcher_registrations
|
||||
.entry(language_server_id)
|
||||
.or_default();
|
||||
|
||||
registrations.insert(registration_id.to_string(), params.watchers);
|
||||
|
||||
self.rebuild_watched_paths(language_server_id, cx);
|
||||
}
|
||||
|
||||
fn on_lsp_unregister_did_change_watched_files(
|
||||
&mut self,
|
||||
language_server_id: LanguageServerId,
|
||||
registration_id: &str,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let registrations = self
|
||||
.language_server_watcher_registrations
|
||||
.entry(language_server_id)
|
||||
.or_default();
|
||||
|
||||
if registrations.remove(registration_id).is_some() {
|
||||
log::info!(
|
||||
"language server {}: unregistered workspace/DidChangeWatchedFiles capability with id {}",
|
||||
language_server_id,
|
||||
registration_id
|
||||
);
|
||||
} else {
|
||||
log::warn!(
|
||||
"language server {}: failed to unregister workspace/DidChangeWatchedFiles capability with id {}. not registered.",
|
||||
language_server_id,
|
||||
registration_id
|
||||
);
|
||||
}
|
||||
|
||||
self.rebuild_watched_paths(language_server_id, cx);
|
||||
}
|
||||
|
||||
fn rebuild_watched_paths(
|
||||
&mut self,
|
||||
language_server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let Some(watchers) = self
|
||||
.language_server_watcher_registrations
|
||||
.get(&language_server_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let watched_paths = self
|
||||
.language_server_watched_paths
|
||||
.entry(language_server_id)
|
||||
.or_default();
|
||||
|
||||
let mut builders = HashMap::default();
|
||||
for watcher in params.watchers {
|
||||
for watcher in watchers.values().flatten() {
|
||||
for worktree in &self.worktrees {
|
||||
if let Some(worktree) = worktree.upgrade() {
|
||||
let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
|
||||
|
|
Loading…
Reference in a new issue