chore: Move more local code into LocalLspStore (#21794)

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2024-12-10 12:48:44 +01:00 committed by GitHub
parent bd2087675b
commit 9219b05c85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -133,6 +133,7 @@ impl FormatTrigger {
}
pub struct LocalLspStore {
worktree_store: Model<WorktreeStore>,
http_client: Arc<dyn HttpClient>,
environment: Model<ProjectEnvironment>,
fs: Arc<dyn Fs>,
@ -1562,6 +1563,247 @@ impl LocalLspStore {
failure_reason: None,
})
}
fn rebuild_watched_paths_inner<'a>(
&'a self,
language_server_id: LanguageServerId,
watchers: impl Iterator<Item = &'a FileSystemWatcher>,
cx: &mut ModelContext<LspStore>,
) -> LanguageServerWatchedPathsBuilder {
let worktrees = self
.worktree_store
.read(cx)
.worktrees()
.filter_map(|worktree| {
self.language_servers_for_worktree(worktree.read(cx).id())
.find(|server| server.server_id() == language_server_id)
.map(|_| worktree)
})
.collect::<Vec<_>>();
let mut worktree_globs = HashMap::default();
let mut abs_globs = HashMap::default();
log::trace!(
"Processing new watcher paths for language server with id {}",
language_server_id
);
enum PathToWatch {
Worktree {
literal_prefix: Arc<Path>,
pattern: String,
},
Absolute {
path: Arc<Path>,
pattern: String,
},
}
for watcher in watchers {
let mut found_host = false;
for worktree in &worktrees {
let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
if let Some(worktree_root_path) = tree.abs_path().to_str() {
let path_to_watch = match &watcher.glob_pattern {
lsp::GlobPattern::String(s) => {
match s.strip_prefix(worktree_root_path) {
Some(relative) => {
let pattern = relative
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(relative)
.to_owned();
let literal_prefix = glob_literal_prefix(&pattern);
let literal_prefix = Arc::from(PathBuf::from(
literal_prefix
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(literal_prefix),
));
PathToWatch::Worktree {
literal_prefix,
pattern,
}
}
None => {
let path = glob_literal_prefix(s);
let glob = &s[path.len()..];
let pattern = glob
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(glob)
.to_owned();
let path = if Path::new(path).components().next().is_none()
{
Arc::from(Path::new(worktree_root_path))
} else {
PathBuf::from(path).into()
};
PathToWatch::Absolute { path, pattern }
}
}
}
lsp::GlobPattern::Relative(rp) => {
let Ok(mut base_uri) = match &rp.base_uri {
lsp::OneOf::Left(workspace_folder) => &workspace_folder.uri,
lsp::OneOf::Right(base_uri) => base_uri,
}
.to_file_path() else {
return false;
};
match base_uri.strip_prefix(worktree_root_path) {
Ok(relative) => {
let mut literal_prefix = relative.to_owned();
literal_prefix.push(glob_literal_prefix(&rp.pattern));
PathToWatch::Worktree {
literal_prefix: literal_prefix.into(),
pattern: rp.pattern.clone(),
}
}
Err(_) => {
let path = glob_literal_prefix(&rp.pattern);
let glob = &rp.pattern[path.len()..];
let pattern = glob
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(glob)
.to_owned();
base_uri.push(path);
let path = if base_uri.components().next().is_none() {
Arc::from(Path::new("/"))
} else {
base_uri.into()
};
PathToWatch::Absolute { path, pattern }
}
}
}
};
match path_to_watch {
PathToWatch::Worktree {
literal_prefix,
pattern,
} => {
if let Some((tree, glob)) =
tree.as_local_mut().zip(Glob::new(&pattern).log_err())
{
tree.add_path_prefix_to_scan(literal_prefix);
worktree_globs
.entry(tree.id())
.or_insert_with(GlobSetBuilder::new)
.add(glob);
} else {
return false;
}
}
PathToWatch::Absolute { path, pattern } => {
if let Some(glob) = Glob::new(&pattern).log_err() {
abs_globs
.entry(path)
.or_insert_with(GlobSetBuilder::new)
.add(glob);
}
}
}
return true;
}
false
});
if glob_is_inside_worktree {
log::trace!(
"Watcher pattern `{}` has been attached to the worktree at `{}`",
serde_json::to_string(&watcher.glob_pattern).unwrap(),
worktree.read(cx).abs_path().display()
);
found_host = true;
}
}
if !found_host {
log::error!(
"Watcher pattern `{}` has not been attached to any worktree or absolute path",
serde_json::to_string(&watcher.glob_pattern).unwrap()
)
}
}
let mut watch_builder = LanguageServerWatchedPathsBuilder::default();
for (worktree_id, builder) in worktree_globs {
if let Ok(globset) = builder.build() {
watch_builder.watch_worktree(worktree_id, globset);
}
}
for (abs_path, builder) in abs_globs {
if let Ok(globset) = builder.build() {
watch_builder.watch_abs_path(abs_path, globset);
}
}
watch_builder
}
fn rebuild_watched_paths(
&mut self,
language_server_id: LanguageServerId,
cx: &mut ModelContext<LspStore>,
) {
let Some(watchers) = self
.language_server_watcher_registrations
.get(&language_server_id)
else {
return;
};
let watch_builder =
self.rebuild_watched_paths_inner(language_server_id, watchers.values().flatten(), cx);
let watcher = watch_builder.build(self.fs.clone(), language_server_id, cx);
self.language_server_watched_paths
.insert(language_server_id, watcher);
cx.notify();
}
fn on_lsp_did_change_watched_files(
&mut self,
language_server_id: LanguageServerId,
registration_id: &str,
params: DidChangeWatchedFilesRegistrationOptions,
cx: &mut ModelContext<LspStore>,
) {
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<LspStore>,
) {
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);
}
}
#[derive(Debug)]
@ -1577,7 +1819,7 @@ pub struct RemoteLspStore {
}
#[allow(clippy::large_enum_variant)]
pub enum LspStoreMode {
pub(crate) enum LspStoreMode {
Local(LocalLspStore), // ssh host and collab host
Remote(RemoteLspStore), // collab guest
}
@ -1777,6 +2019,7 @@ impl LspStore {
};
Self {
mode: LspStoreMode::Local(LocalLspStore {
worktree_store: worktree_store.clone(),
supplementary_language_servers: Default::default(),
languages: languages.clone(),
language_server_ids: Default::default(),
@ -2214,8 +2457,6 @@ impl LspStore {
<R::LspRequest as lsp::request::Request>::Result: Send,
<R::LspRequest as lsp::request::Request>::Params: Send,
{
let buffer = buffer_handle.read(cx);
if let Some((upstream_client, upstream_project_id)) = self.upstream_client() {
return self.send_lsp_proto_request(
buffer_handle,
@ -2225,7 +2466,7 @@ impl LspStore {
cx,
);
}
let buffer = buffer_handle.read(cx);
let language_server = match server {
LanguageServerToQuery::Primary => {
match self
@ -5027,208 +5268,6 @@ impl LspStore {
});
}
fn rebuild_watched_paths(
&mut self,
language_server_id: LanguageServerId,
cx: &mut ModelContext<Self>,
) {
let Some(watchers) = self.as_local().and_then(|local| {
local
.language_server_watcher_registrations
.get(&language_server_id)
}) else {
return;
};
let watch_builder =
self.rebuild_watched_paths_inner(language_server_id, watchers.values().flatten(), cx);
let Some(local_lsp_store) = self.as_local_mut() else {
return;
};
let watcher = watch_builder.build(local_lsp_store.fs.clone(), language_server_id, cx);
local_lsp_store
.language_server_watched_paths
.insert(language_server_id, watcher);
cx.notify();
}
fn rebuild_watched_paths_inner<'a>(
&'a self,
language_server_id: LanguageServerId,
watchers: impl Iterator<Item = &'a FileSystemWatcher>,
cx: &mut ModelContext<Self>,
) -> LanguageServerWatchedPathsBuilder {
let worktrees = self
.worktree_store
.read(cx)
.worktrees()
.filter_map(|worktree| {
self.as_local()?
.language_servers_for_worktree(worktree.read(cx).id())
.find(|server| server.server_id() == language_server_id)
.map(|_| worktree)
})
.collect::<Vec<_>>();
let mut worktree_globs = HashMap::default();
let mut abs_globs = HashMap::default();
log::trace!(
"Processing new watcher paths for language server with id {}",
language_server_id
);
enum PathToWatch {
Worktree {
literal_prefix: Arc<Path>,
pattern: String,
},
Absolute {
path: Arc<Path>,
pattern: String,
},
}
for watcher in watchers {
let mut found_host = false;
for worktree in &worktrees {
let glob_is_inside_worktree = worktree.update(cx, |tree, _| {
if let Some(worktree_root_path) = tree.abs_path().to_str() {
let path_to_watch = match &watcher.glob_pattern {
lsp::GlobPattern::String(s) => {
match s.strip_prefix(worktree_root_path) {
Some(relative) => {
let pattern = relative
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(relative)
.to_owned();
let literal_prefix = glob_literal_prefix(&pattern);
let literal_prefix = Arc::from(PathBuf::from(
literal_prefix
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(literal_prefix),
));
PathToWatch::Worktree {
literal_prefix,
pattern,
}
}
None => {
let path = glob_literal_prefix(s);
let glob = &s[path.len()..];
let pattern = glob
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(glob)
.to_owned();
let path = if Path::new(path).components().next().is_none()
{
Arc::from(Path::new(worktree_root_path))
} else {
PathBuf::from(path).into()
};
PathToWatch::Absolute { path, pattern }
}
}
}
lsp::GlobPattern::Relative(rp) => {
let Ok(mut base_uri) = match &rp.base_uri {
lsp::OneOf::Left(workspace_folder) => &workspace_folder.uri,
lsp::OneOf::Right(base_uri) => base_uri,
}
.to_file_path() else {
return false;
};
match base_uri.strip_prefix(worktree_root_path) {
Ok(relative) => {
let mut literal_prefix = relative.to_owned();
literal_prefix.push(glob_literal_prefix(&rp.pattern));
PathToWatch::Worktree {
literal_prefix: literal_prefix.into(),
pattern: rp.pattern.clone(),
}
}
Err(_) => {
let path = glob_literal_prefix(&rp.pattern);
let glob = &rp.pattern[path.len()..];
let pattern = glob
.strip_prefix(std::path::MAIN_SEPARATOR)
.unwrap_or(glob)
.to_owned();
base_uri.push(path);
let path = if base_uri.components().next().is_none() {
Arc::from(Path::new("/"))
} else {
base_uri.into()
};
PathToWatch::Absolute { path, pattern }
}
}
}
};
match path_to_watch {
PathToWatch::Worktree {
literal_prefix,
pattern,
} => {
if let Some((tree, glob)) =
tree.as_local_mut().zip(Glob::new(&pattern).log_err())
{
tree.add_path_prefix_to_scan(literal_prefix);
worktree_globs
.entry(tree.id())
.or_insert_with(GlobSetBuilder::new)
.add(glob);
} else {
return false;
}
}
PathToWatch::Absolute { path, pattern } => {
if let Some(glob) = Glob::new(&pattern).log_err() {
abs_globs
.entry(path)
.or_insert_with(GlobSetBuilder::new)
.add(glob);
}
}
}
return true;
}
false
});
if glob_is_inside_worktree {
log::trace!(
"Watcher pattern `{}` has been attached to the worktree at `{}`",
serde_json::to_string(&watcher.glob_pattern).unwrap(),
worktree.read(cx).abs_path().display()
);
found_host = true;
}
}
if !found_host {
log::error!(
"Watcher pattern `{}` has not been attached to any worktree or absolute path",
serde_json::to_string(&watcher.glob_pattern).unwrap()
)
}
}
let mut watch_builder = LanguageServerWatchedPathsBuilder::default();
for (worktree_id, builder) in worktree_globs {
if let Ok(globset) = builder.build() {
watch_builder.watch_worktree(worktree_id, globset);
}
}
for (abs_path, builder) in abs_globs {
if let Ok(globset) = builder.build() {
watch_builder.watch_abs_path(abs_path, globset);
}
}
watch_builder
}
pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
if let Some(local_lsp_store) = self.as_local() {
if let Some(LanguageServerState::Running { server, .. }) =
@ -5419,55 +5458,6 @@ impl LspStore {
})
}
fn on_lsp_did_change_watched_files(
&mut self,
language_server_id: LanguageServerId,
registration_id: &str,
params: DidChangeWatchedFilesRegistrationOptions,
cx: &mut ModelContext<Self>,
) {
if let Some(local) = self.as_local_mut() {
let registrations = local
.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>,
) {
if let Some(local) = self.as_local_mut() {
let registrations = local
.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);
}
}
pub async fn handle_resolve_completion_documentation(
this: Model<Self>,
envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
@ -6658,9 +6648,10 @@ impl LspStore {
if let Some(options) = reg.register_options {
let options = serde_json::from_value(options)?;
this.update(&mut cx, |this, cx| {
this.on_lsp_did_change_watched_files(
this.as_local_mut()?.on_lsp_did_change_watched_files(
server_id, &reg.id, options, cx,
);
Some(())
})?;
}
}
@ -6760,9 +6751,11 @@ impl LspStore {
match unreg.method.as_str() {
"workspace/didChangeWatchedFiles" => {
this.update(&mut cx, |this, cx| {
this.on_lsp_unregister_did_change_watched_files(
this.as_local_mut()?
.on_lsp_unregister_did_change_watched_files(
server_id, &unreg.id, cx,
);
Some(())
})?;
}
"textDocument/rename" => {