This PR reverts #17173, as it introduced a segfault when opening any
Gleam project and the language server starts up.
This reverts commit a850731b0e
.
Release Notes:
- N/A
This commit is contained in:
parent
a850731b0e
commit
03d8e54fd4
7 changed files with 102 additions and 438 deletions
|
@ -22,8 +22,8 @@ use futures::{
|
|||
};
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Context, Entity, EventEmitter, Model, ModelContext, PromptLevel,
|
||||
Task, WeakModel,
|
||||
AppContext, AsyncAppContext, Entity, EventEmitter, Model, ModelContext, PromptLevel, Task,
|
||||
WeakModel,
|
||||
};
|
||||
use http_client::HttpClient;
|
||||
use itertools::Itertools;
|
||||
|
@ -57,7 +57,7 @@ use std::{
|
|||
convert::TryInto,
|
||||
ffi::OsStr,
|
||||
iter, mem,
|
||||
ops::{ControlFlow, Range},
|
||||
ops::Range,
|
||||
path::{self, Path, PathBuf},
|
||||
process::Stdio,
|
||||
str,
|
||||
|
@ -146,7 +146,7 @@ pub struct LspStore {
|
|||
language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
|
||||
language_server_statuses: BTreeMap<LanguageServerId, LanguageServerStatus>,
|
||||
last_workspace_edits_by_language_server: HashMap<LanguageServerId, ProjectTransaction>,
|
||||
language_server_watched_paths: HashMap<LanguageServerId, Model<LanguageServerWatchedPaths>>,
|
||||
language_server_watched_paths: HashMap<LanguageServerId, HashMap<WorktreeId, GlobSet>>,
|
||||
language_server_watcher_registrations:
|
||||
HashMap<LanguageServerId, HashMap<String, Vec<FileSystemWatcher>>>,
|
||||
active_entry: Option<ProjectEntryId>,
|
||||
|
@ -3022,38 +3022,6 @@ impl LspStore {
|
|||
.map(|(key, value)| (*key, value))
|
||||
}
|
||||
|
||||
fn lsp_notify_abs_paths_changed(
|
||||
&mut self,
|
||||
server_id: LanguageServerId,
|
||||
changes: Vec<PathEvent>,
|
||||
) {
|
||||
maybe!({
|
||||
let server = self.language_server_for_id(server_id)?;
|
||||
let changes = changes
|
||||
.into_iter()
|
||||
.filter_map(|event| {
|
||||
let typ = match event.kind? {
|
||||
PathEventKind::Created => lsp::FileChangeType::CREATED,
|
||||
PathEventKind::Removed => lsp::FileChangeType::DELETED,
|
||||
PathEventKind::Changed => lsp::FileChangeType::CHANGED,
|
||||
};
|
||||
Some(lsp::FileEvent {
|
||||
uri: lsp::Url::from_file_path(&event.path).ok()?,
|
||||
typ,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !changes.is_empty() {
|
||||
server
|
||||
.notify::<lsp::notification::DidChangeWatchedFiles>(
|
||||
lsp::DidChangeWatchedFilesParams { changes },
|
||||
)
|
||||
.log_err();
|
||||
}
|
||||
Some(())
|
||||
});
|
||||
}
|
||||
|
||||
fn rebuild_watched_paths(
|
||||
&mut self,
|
||||
language_server_id: LanguageServerId,
|
||||
|
@ -3066,169 +3034,62 @@ impl LspStore {
|
|||
return;
|
||||
};
|
||||
|
||||
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
|
||||
);
|
||||
let watched_paths = self
|
||||
.language_server_watched_paths
|
||||
.entry(language_server_id)
|
||||
.or_default();
|
||||
|
||||
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<_>>();
|
||||
|
||||
enum PathToWatch {
|
||||
Worktree {
|
||||
literal_prefix: Arc<Path>,
|
||||
pattern: String,
|
||||
},
|
||||
Absolute {
|
||||
path: Arc<Path>,
|
||||
pattern: String,
|
||||
},
|
||||
}
|
||||
let mut builders = HashMap::default();
|
||||
for watcher in watchers.values().flatten() {
|
||||
let mut found_host = false;
|
||||
for worktree in &worktrees {
|
||||
for worktree in self.worktree_store.read(cx).worktrees().collect::<Vec<_>>() {
|
||||
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 = PathBuf::from(path).into();
|
||||
|
||||
PathToWatch::Absolute { path, pattern }
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(abs_path) = tree.abs_path().to_str() {
|
||||
let relative_glob_pattern = match &watcher.glob_pattern {
|
||||
lsp::GlobPattern::String(s) => Some(
|
||||
s.strip_prefix(abs_path)
|
||||
.unwrap_or(s)
|
||||
.strip_prefix(std::path::MAIN_SEPARATOR)
|
||||
.unwrap_or(s),
|
||||
),
|
||||
lsp::GlobPattern::Relative(rp) => {
|
||||
let Ok(mut base_uri) = match &rp.base_uri {
|
||||
let 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);
|
||||
|
||||
PathToWatch::Absolute {
|
||||
path: base_uri.into(),
|
||||
pattern,
|
||||
}
|
||||
}
|
||||
}
|
||||
base_uri.to_file_path().ok().and_then(|file_path| {
|
||||
(file_path.to_str() == Some(abs_path))
|
||||
.then_some(rp.pattern.as_str())
|
||||
})
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
if let Some(relative_glob_pattern) = relative_glob_pattern {
|
||||
let literal_prefix = glob_literal_prefix(relative_glob_pattern);
|
||||
tree.as_local_mut()
|
||||
.unwrap()
|
||||
.add_path_prefix_to_scan(Path::new(literal_prefix).into());
|
||||
if let Some(glob) = Glob::new(relative_glob_pattern).log_err() {
|
||||
builders
|
||||
.entry(tree.id())
|
||||
.or_insert_with(|| GlobSetBuilder::new())
|
||||
.add(glob);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
watched_paths.clear();
|
||||
for (worktree_id, builder) in builders {
|
||||
if let Ok(globset) = builder.build() {
|
||||
watch_builder.watch_worktree(worktree_id, globset);
|
||||
watched_paths.insert(worktree_id, globset);
|
||||
}
|
||||
}
|
||||
for (abs_path, builder) in abs_globs {
|
||||
if let Ok(globset) = builder.build() {
|
||||
watch_builder.watch_abs_path(abs_path, globset);
|
||||
}
|
||||
}
|
||||
let watcher = watch_builder.build(self.fs.clone(), language_server_id, cx);
|
||||
self.language_server_watched_paths
|
||||
.insert(language_server_id, watcher);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -5681,7 +5542,7 @@ impl LspStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn update_local_worktree_language_servers(
|
||||
pub fn update_local_worktree_language_servers(
|
||||
&mut self,
|
||||
worktree_handle: &Model<Worktree>,
|
||||
changes: &[(Arc<Path>, ProjectEntryId, PathChange)],
|
||||
|
@ -5710,7 +5571,7 @@ impl LspStore {
|
|||
if let Some(watched_paths) = self
|
||||
.language_server_watched_paths
|
||||
.get(&server_id)
|
||||
.and_then(|paths| paths.read(cx).worktree_paths.get(&worktree_id))
|
||||
.and_then(|paths| paths.get(&worktree_id))
|
||||
{
|
||||
let params = lsp::DidChangeWatchedFilesParams {
|
||||
changes: changes
|
||||
|
@ -5871,102 +5732,6 @@ pub enum LanguageServerToQuery {
|
|||
Other(LanguageServerId),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LanguageServerWatchedPaths {
|
||||
worktree_paths: HashMap<WorktreeId, GlobSet>,
|
||||
abs_paths: HashMap<Arc<Path>, (GlobSet, Task<()>)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LanguageServerWatchedPathsBuilder {
|
||||
worktree_paths: HashMap<WorktreeId, GlobSet>,
|
||||
abs_paths: HashMap<Arc<Path>, GlobSet>,
|
||||
}
|
||||
|
||||
impl LanguageServerWatchedPathsBuilder {
|
||||
fn watch_worktree(&mut self, worktree_id: WorktreeId, glob_set: GlobSet) {
|
||||
self.worktree_paths.insert(worktree_id, glob_set);
|
||||
}
|
||||
fn watch_abs_path(&mut self, path: Arc<Path>, glob_set: GlobSet) {
|
||||
self.abs_paths.insert(path, glob_set);
|
||||
}
|
||||
fn build(
|
||||
self,
|
||||
fs: Arc<dyn Fs>,
|
||||
language_server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<LspStore>,
|
||||
) -> Model<LanguageServerWatchedPaths> {
|
||||
let project = cx.weak_model();
|
||||
|
||||
cx.new_model(|cx| {
|
||||
let this_id = cx.entity_id();
|
||||
const LSP_ABS_PATH_OBSERVE: Duration = Duration::from_millis(100);
|
||||
let abs_paths = self
|
||||
.abs_paths
|
||||
.into_iter()
|
||||
.map(|(abs_path, globset)| {
|
||||
let task = cx.spawn({
|
||||
let abs_path = abs_path.clone();
|
||||
let fs = fs.clone();
|
||||
|
||||
let lsp_store = project.clone();
|
||||
|_, mut cx| async move {
|
||||
maybe!(async move {
|
||||
let mut push_updates =
|
||||
fs.watch(&abs_path, LSP_ABS_PATH_OBSERVE).await;
|
||||
while let Some(update) = push_updates.0.next().await {
|
||||
let action = lsp_store
|
||||
.update(&mut cx, |this, cx| {
|
||||
let Some(watcher) = this
|
||||
.language_server_watched_paths
|
||||
.get(&language_server_id)
|
||||
else {
|
||||
return ControlFlow::Break(());
|
||||
};
|
||||
if watcher.entity_id() != this_id {
|
||||
// This watcher is no longer registered on the project, which means that we should
|
||||
// cease operations.
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
let (globs, _) = watcher
|
||||
.read(cx)
|
||||
.abs_paths
|
||||
.get(&abs_path)
|
||||
.expect(
|
||||
"Watched abs path is not registered with a watcher",
|
||||
);
|
||||
let matching_entries = update
|
||||
.into_iter()
|
||||
.filter(|event| globs.is_match(&event.path))
|
||||
.collect::<Vec<_>>();
|
||||
this.lsp_notify_abs_paths_changed(
|
||||
language_server_id,
|
||||
matching_entries,
|
||||
);
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
if action.is_break() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
});
|
||||
(abs_path, (globset, task))
|
||||
})
|
||||
.collect();
|
||||
LanguageServerWatchedPaths {
|
||||
worktree_paths: self.worktree_paths,
|
||||
abs_paths,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct LspBufferSnapshot {
|
||||
version: i32,
|
||||
snapshot: TextBufferSnapshot,
|
||||
|
@ -6078,9 +5843,7 @@ impl DiagnosticSummary {
|
|||
}
|
||||
|
||||
fn glob_literal_prefix(glob: &str) -> &str {
|
||||
let is_absolute = glob.starts_with(path::MAIN_SEPARATOR);
|
||||
|
||||
let mut literal_end = is_absolute as usize;
|
||||
let mut literal_end = 0;
|
||||
for (i, part) in glob.split(path::MAIN_SEPARATOR).enumerate() {
|
||||
if part.contains(&['*', '?', '{', '}']) {
|
||||
break;
|
||||
|
@ -6092,7 +5855,6 @@ fn glob_literal_prefix(glob: &str) -> &str {
|
|||
literal_end += part.len();
|
||||
}
|
||||
}
|
||||
let literal_end = literal_end.min(glob.len());
|
||||
&glob[..literal_end]
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue