This commit is contained in:
Cole Miller 2026-05-20 10:58:52 -04:00
parent 20b7f31e7d
commit 5348e0511b
5 changed files with 221 additions and 2 deletions

View file

@ -530,9 +530,18 @@ pub struct RealFs {
pub trait FileHandle: Send + Sync + std::fmt::Debug {
fn current_path(&self, fs: &Arc<dyn Fs>) -> Result<PathBuf>;
fn debug_fd(&self) -> Option<i64> {
None
}
}
impl FileHandle for std::fs::File {
#[cfg(unix)]
fn debug_fd(&self) -> Option<i64> {
Some(self.as_raw_fd().into())
}
#[cfg(target_os = "macos")]
fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> {
use std::{

View file

@ -12,7 +12,8 @@ use gpui::{
WeakEntity,
};
use language::{
Buffer, BufferEvent, Capability, DiskState, File as _, Language, LineEnding, Operation,
Buffer, BufferEvent, Capability, DiskState, File as _, Language, LineEnding, LocalFile as _,
Operation,
language_settings::{AllLanguageSettings, LineEndingSetting},
proto::{
deserialize_line_ending, deserialize_version, serialize_line_ending, serialize_version,
@ -963,7 +964,21 @@ impl BufferStore {
path: file.path.clone(),
worktree_id: file.worktree_id(cx),
});
let file = File::from_dyn(buffer.file()).cloned();
let is_remote = buffer.replica_id().is_remote();
log::debug!(
"adding buffer entity_id={:?}, buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={:?}, worktree_single_file={:?}, root_file_handle_fd={:?}",
buffer_entity.entity_id(),
remote_id,
file.as_ref().map(|file| file.abs_path(cx)),
file.as_ref().map(|file| file.worktree_id(cx)),
file.as_ref()
.map(|file| file.worktree.read(cx).is_visible()),
file.as_ref()
.map(|file| file.worktree.read(cx).is_single_file()),
file.as_ref()
.and_then(|file| file.worktree.read(cx).root_file_handle_debug_fd())
);
let open_buffer = OpenBuffer::Complete {
buffer: buffer_entity.downgrade(),
};
@ -971,6 +986,17 @@ impl BufferStore {
let handle = cx.entity().downgrade();
buffer_entity.update(cx, move |_, cx| {
cx.on_release(move |buffer, cx| {
let file = File::from_dyn(buffer.file()).cloned();
log::debug!(
"buffer released buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={:?}, worktree_single_file={:?}, root_file_handle_fd={:?}",
buffer.remote_id(),
file.as_ref().map(|file| file.abs_path(cx)),
file.as_ref().map(|file| file.worktree_id(cx)),
file.as_ref().map(|file| file.worktree.read(cx).is_visible()),
file.as_ref().map(|file| file.worktree.read(cx).is_single_file()),
file.as_ref()
.and_then(|file| file.worktree.read(cx).root_file_handle_debug_fd())
);
handle
.update(cx, |_, cx| {
cx.emit(BufferStoreEvent::BufferDropped(buffer.remote_id()))

View file

@ -2784,6 +2784,15 @@ impl LocalLspStore {
let Some(file) = File::from_dyn(buffer.file()) else {
return;
};
log::debug!(
"registering buffer with language servers buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={}, worktree_single_file={}, root_file_handle_fd={:?}",
buffer_id,
file.abs_path(cx),
file.worktree_id(cx),
file.worktree.read(cx).is_visible(),
file.worktree.read(cx).is_single_file(),
file.worktree.read(cx).root_file_handle_debug_fd()
);
if !file.is_local() {
return;
}
@ -2883,6 +2892,13 @@ impl LocalLspStore {
}
})
.collect::<Vec<_>>();
log::debug!(
"selected language servers for buffer buffer_id={:?}, path={:?}, server_count={}, reused={}",
buffer_id,
abs_path,
servers_and_adapters.len(),
reused
);
for (server, adapter) in servers_and_adapters {
buffer_handle.update(cx, |buffer, cx| {
buffer.set_completion_triggers(
@ -3027,6 +3043,18 @@ impl LocalLspStore {
cx: &mut App,
) {
buffer.update(cx, |buffer, cx| {
let file = File::from_dyn(buffer.file()).cloned();
log::debug!(
"unregistering buffer from language servers buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={:?}, worktree_single_file={:?}, root_file_handle_fd={:?}, uri={}",
buffer.remote_id(),
file.as_ref().map(|file| file.abs_path(cx)),
file.as_ref().map(|file| file.worktree_id(cx)),
file.as_ref().map(|file| file.worktree.read(cx).is_visible()),
file.as_ref().map(|file| file.worktree.read(cx).is_single_file()),
file.as_ref()
.and_then(|file| file.worktree.read(cx).root_file_handle_debug_fd()),
file_url
);
let mut snapshots = self.buffer_snapshots.remove(&buffer.remote_id());
for (_, language_server) in self.language_servers_for_buffer(buffer, cx) {
@ -4614,6 +4642,21 @@ impl LspStore {
) -> OpenLspBufferHandle {
let buffer_id = buffer.read(cx).remote_id();
let handle = OpenLspBufferHandle(cx.new(|_| OpenLspBuffer(buffer.clone())));
let file = File::from_dyn(buffer.read(cx).file()).cloned();
log::debug!(
"creating open lsp buffer handle entity_id={:?}, buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={:?}, worktree_single_file={:?}, root_file_handle_fd={:?}, ignore_refcounts={}",
handle.0.entity_id(),
buffer_id,
file.as_ref().map(|file| file.abs_path(cx)),
file.as_ref().map(|file| file.worktree_id(cx)),
file.as_ref()
.map(|file| file.worktree.read(cx).is_visible()),
file.as_ref()
.map(|file| file.worktree.read(cx).is_single_file()),
file.as_ref()
.and_then(|file| file.worktree.read(cx).root_file_handle_debug_fd()),
ignore_refcounts
);
if let Some(local) = self.as_local_mut() {
let refcount = local.registered_buffers.entry(buffer_id).or_insert(0);
if !ignore_refcounts {
@ -4636,6 +4679,18 @@ impl LspStore {
}
if !ignore_refcounts {
cx.observe_release(&handle.0, move |lsp_store, buffer, cx| {
let file = File::from_dyn(buffer.0.read(cx).file()).cloned();
log::debug!(
"open lsp buffer handle released entity_id={:?}, buffer_id={:?}, path={:?}, worktree_id={:?}, worktree_visible={:?}, worktree_single_file={:?}, root_file_handle_fd={:?}",
buffer.0.entity_id(),
buffer_id,
file.as_ref().map(|file| file.abs_path(cx)),
file.as_ref().map(|file| file.worktree_id(cx)),
file.as_ref().map(|file| file.worktree.read(cx).is_visible()),
file.as_ref().map(|file| file.worktree.read(cx).is_single_file()),
file.as_ref()
.and_then(|file| file.worktree.read(cx).root_file_handle_debug_fd())
);
let refcount = {
let local = lsp_store.as_local_mut().unwrap();
let Some(refcount) = local.registered_buffers.get_mut(&buffer_id) else {
@ -4646,6 +4701,11 @@ impl LspStore {
*refcount -= 1;
*refcount
};
log::debug!(
"open lsp buffer handle refcount after release buffer_id={:?}, refcount={}",
buffer_id,
refcount
);
if refcount == 0 {
lsp_store.lsp_data.remove(&buffer_id);
lsp_store.buffer_reload_tasks.remove(&buffer_id);

View file

@ -888,6 +888,16 @@ impl WorktreeStore {
} else {
WorktreeHandle::Weak(worktree.downgrade())
};
log::debug!(
"adding worktree entity_id={:?}, worktree_id={:?}, path={:?}, visible={}, single_file={}, retained_strongly={}, root_file_handle_fd={:?}",
worktree.entity_id(),
worktree_id,
worktree.read(cx).abs_path(),
worktree.read(cx).is_visible(),
worktree.read(cx).is_single_file(),
push_strong_handle,
worktree.read(cx).root_file_handle_debug_fd()
);
self.worktrees.push(handle);
cx.emit(WorktreeStoreEvent::WorktreeAdded(worktree.clone()));
@ -925,6 +935,15 @@ impl WorktreeStore {
})
.detach();
cx.observe_release(worktree, move |this, worktree, cx| {
log::debug!(
"worktree released entity_id={:?}, worktree_id={:?}, path={:?}, visible={}, single_file={}, root_file_handle_fd={:?}",
handle_id,
worktree.id(),
worktree.abs_path(),
worktree.is_visible(),
worktree.is_single_file(),
worktree.root_file_handle_debug_fd()
);
cx.emit(WorktreeStoreEvent::WorktreeReleased(
handle_id,
worktree.id(),
@ -942,6 +961,15 @@ impl WorktreeStore {
self.worktrees.retain(|worktree| {
if let Some(worktree) = worktree.upgrade() {
if worktree.read(cx).id() == id_to_remove {
log::debug!(
"removing worktree entity_id={:?}, worktree_id={:?}, path={:?}, visible={}, single_file={}, root_file_handle_fd={:?}",
worktree.entity_id(),
id_to_remove,
worktree.read(cx).abs_path(),
worktree.read(cx).is_visible(),
worktree.read(cx).is_single_file(),
worktree.read(cx).root_file_handle_debug_fd()
);
cx.emit(WorktreeStoreEvent::WorktreeRemoved(
worktree.entity_id(),
id_to_remove,

View file

@ -57,6 +57,7 @@ use std::{
future::Future,
mem::{self},
ops::{Deref, DerefMut, Range},
panic::Location,
path::{Path, PathBuf},
pin::Pin,
sync::{
@ -150,6 +151,7 @@ pub struct PathPrefixScanRequest {
struct ScanRequest {
relative_paths: Vec<Arc<RelPath>>,
trigger_locations: SmallVec<[&'static Location<'static>; 1]>,
done: SmallVec<[barrier::Sender; 1]>,
}
@ -258,6 +260,34 @@ pub struct LocalSnapshot {
root_file_handle: Option<Arc<dyn fs::FileHandle>>,
}
#[derive(Debug)]
struct RootFileHandle {
inner: Arc<dyn fs::FileHandle>,
path: Arc<Path>,
worktree_id: WorktreeId,
}
impl fs::FileHandle for RootFileHandle {
fn current_path(&self, fs: &Arc<dyn Fs>) -> Result<PathBuf> {
self.inner.current_path(fs)
}
fn debug_fd(&self) -> Option<i64> {
self.inner.debug_fd()
}
}
impl Drop for RootFileHandle {
fn drop(&mut self) {
log::debug!(
"dropping root file handle worktree_id={:?}, path={:?}, root_file_handle_fd={:?}",
self.worktree_id,
self.path,
self.inner.debug_fd()
);
}
}
struct BackgroundScannerState {
snapshot: LocalSnapshot,
symlink_paths_by_target: HashMap<Arc<Path>, SmallVec<[Arc<RelPath>; 1]>>,
@ -411,9 +441,26 @@ impl Worktree {
)
})
.log_err()
.map(|inner| {
Arc::new(RootFileHandle {
inner,
path: abs_path.clone(),
worktree_id,
}) as Arc<dyn fs::FileHandle>
})
} else {
None
};
log::debug!(
"created local worktree root handle path={:?}, visible={}, is_single_file={}, root_file_handle_fd={:?}, worktree_id={:?}",
abs_path,
visible,
metadata.as_ref().is_some_and(|metadata| !metadata.is_dir),
root_file_handle
.as_ref()
.and_then(|handle| handle.debug_fd()),
worktree_id
);
let root_repo_common_dir = if visible {
discover_root_repo_common_dir(&abs_path, fs.as_ref())
@ -739,6 +786,13 @@ impl Worktree {
}
}
pub fn root_file_handle_debug_fd(&self) -> Option<i64> {
match self {
Worktree::Local(worktree) => worktree.root_file_handle_debug_fd(),
Worktree::Remote(_) => None,
}
}
pub fn root_file(&self, cx: &Context<Self>) -> Option<Arc<File>> {
let entry = self.root_entry()?;
Some(File::for_entry(entry.clone(), cx.entity()))
@ -1111,11 +1165,31 @@ impl Worktree {
}
}
impl Drop for LocalWorktree {
fn drop(&mut self) {
log::debug!(
"dropping local worktree worktree_id={:?}, path={:?}, visible={}, single_file={}, root_file_handle_fd={:?}",
self.snapshot.id(),
self.snapshot.abs_path,
self.visible,
self.snapshot.root_dir().is_none(),
self.root_file_handle_debug_fd()
);
}
}
impl LocalWorktree {
pub fn fs(&self) -> &Arc<dyn Fs> {
&self.fs
}
pub fn root_file_handle_debug_fd(&self) -> Option<i64> {
self.snapshot
.root_file_handle
.as_ref()
.and_then(|handle| handle.debug_fd())
}
pub fn is_path_private(&self, path: &RelPath) -> bool {
!self.share_private_files && self.settings.is_path_private(path)
}
@ -1924,11 +1998,13 @@ impl LocalWorktree {
}))
}
#[track_caller]
pub fn refresh_entries_for_paths(&self, paths: Vec<Arc<RelPath>>) -> barrier::Receiver {
let (tx, rx) = barrier::channel();
self.scan_requests_tx
.try_send(ScanRequest {
relative_paths: paths,
trigger_locations: smallvec![Location::caller()],
done: smallvec![tx],
})
.ok();
@ -4210,7 +4286,24 @@ impl BackgroundScanner {
}
async fn process_scan_request(&self, mut request: ScanRequest, scanning: bool) -> bool {
log::debug!("rescanning paths {:?}", request.relative_paths);
let trigger_locations = request
.trigger_locations
.iter()
.map(|location| {
format!(
"{}:{}:{}",
location.file(),
location.line(),
location.column()
)
})
.collect::<Vec<_>>();
log::debug!(
"rescanning paths {:?}, requested from {:?}, single_file_worktree={}",
request.relative_paths,
trigger_locations,
self.is_single_file
);
request.relative_paths.sort_unstable();
self.forcibly_load_paths(&request.relative_paths).await;
@ -5572,6 +5665,9 @@ impl BackgroundScanner {
let mut request = self.scan_requests_rx.recv().await?;
while let Ok(next_request) = self.scan_requests_rx.try_recv() {
request.relative_paths.extend(next_request.relative_paths);
request
.trigger_locations
.extend(next_request.trigger_locations);
request.done.extend(next_request.done);
}
Ok(request)