Optimize LSP watched file reporting in 2 simple ways

* Convert globs to relative paths in advance. This avoids needing to convert
  every changed path to an absolute path before performing glob matching.
* Avoid duplicate reporting for language servers with multiple worktrees.
This commit is contained in:
Max Brunsfeld 2023-05-18 17:08:51 -07:00
parent 34b0d6200f
commit 4bda5c4d69
2 changed files with 49 additions and 13 deletions

View file

@ -73,6 +73,14 @@ impl LspGlobSet {
} }
} }
impl std::fmt::Debug for LspGlobSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_set()
.entries(self.patterns.iter().map(|p| p.as_str()))
.finish()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -226,7 +226,7 @@ pub enum LanguageServerState {
language: Arc<Language>, language: Arc<Language>,
adapter: Arc<CachedLspAdapter>, adapter: Arc<CachedLspAdapter>,
server: Arc<LanguageServer>, server: Arc<LanguageServer>,
watched_paths: LspGlobSet, watched_paths: HashMap<WorktreeId, LspGlobSet>,
simulate_disk_based_diagnostics_completion: Option<Task<()>>, simulate_disk_based_diagnostics_completion: Option<Task<()>>,
}, },
} }
@ -2869,7 +2869,25 @@ impl Project {
{ {
watched_paths.clear(); watched_paths.clear();
for watcher in params.watchers { for watcher in params.watchers {
watched_paths.add_pattern(&watcher.glob_pattern).log_err(); for worktree in &self.worktrees {
if let Some(worktree) = worktree.upgrade(cx) {
let worktree = worktree.read(cx);
if let Some(abs_path) = worktree.abs_path().to_str() {
if let Some(suffix) = watcher
.glob_pattern
.strip_prefix(abs_path)
.and_then(|s| s.strip_prefix(std::path::MAIN_SEPARATOR))
{
watched_paths
.entry(worktree.id())
.or_default()
.add_pattern(suffix)
.log_err();
break;
}
}
}
}
} }
cx.notify(); cx.notify();
} }
@ -4708,9 +4726,18 @@ impl Project {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
let worktree_id = worktree_handle.read(cx).id(); let worktree_id = worktree_handle.read(cx).id();
let mut language_server_ids = self
.language_server_ids
.iter()
.filter_map(|((server_worktree_id, _), server_id)| {
(*server_worktree_id == worktree_id).then_some(*server_id)
})
.collect::<Vec<_>>();
language_server_ids.sort();
language_server_ids.dedup();
let abs_path = worktree_handle.read(cx).abs_path(); let abs_path = worktree_handle.read(cx).abs_path();
for ((server_worktree_id, _), server_id) in &self.language_server_ids { for server_id in &language_server_ids {
if *server_worktree_id == worktree_id {
if let Some(server) = self.language_servers.get(server_id) { if let Some(server) = self.language_servers.get(server_id) {
if let LanguageServerState::Running { if let LanguageServerState::Running {
server, server,
@ -4718,14 +4745,15 @@ impl Project {
.. ..
} = server } = server
{ {
if let Some(watched_paths) = watched_paths.get(&worktree_id) {
let params = lsp::DidChangeWatchedFilesParams { let params = lsp::DidChangeWatchedFilesParams {
changes: changes changes: changes
.iter() .iter()
.filter_map(|((path, _), change)| { .filter_map(|((path, _), change)| {
let path = abs_path.join(path);
if watched_paths.matches(&path) { if watched_paths.matches(&path) {
Some(lsp::FileEvent { Some(lsp::FileEvent {
uri: lsp::Url::from_file_path(path).unwrap(), uri: lsp::Url::from_file_path(abs_path.join(path))
.unwrap(),
typ: match change { typ: match change {
PathChange::Added => lsp::FileChangeType::CREATED, PathChange::Added => lsp::FileChangeType::CREATED,
PathChange::Removed => lsp::FileChangeType::DELETED, PathChange::Removed => lsp::FileChangeType::DELETED,