fix: emit save events to cover issue of notify (#2006)

This commit is contained in:
Myriad-Dreamin 2025-08-06 21:15:56 +08:00 committed by GitHub
parent 58b43c006c
commit 7455d11629
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 52 additions and 1 deletions

View file

@ -243,6 +243,8 @@ pub enum Interrupt<F: CompilerFeat> {
Memory(MemoryEvent), Memory(MemoryEvent),
/// File system event. /// File system event.
Fs(FilesystemEvent), Fs(FilesystemEvent),
/// Save a file.
Save(ImmutPath),
} }
impl<F: CompilerFeat> fmt::Debug for Interrupt<F> { impl<F: CompilerFeat> fmt::Debug for Interrupt<F> {
@ -258,6 +260,7 @@ impl<F: CompilerFeat> fmt::Debug for Interrupt<F> {
Interrupt::CreationTimestamp(ts) => write!(f, "CreationTimestamp({ts:?})"), Interrupt::CreationTimestamp(ts) => write!(f, "CreationTimestamp({ts:?})"),
Interrupt::Memory(..) => write!(f, "Memory(..)"), Interrupt::Memory(..) => write!(f, "Memory(..)"),
Interrupt::Fs(..) => write!(f, "Fs(..)"), Interrupt::Fs(..) => write!(f, "Fs(..)"),
Interrupt::Save(path) => write!(f, "Save({path:?})"),
} }
} }
} }
@ -624,6 +627,23 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
let err = self.dep_tx.send(event); let err = self.dep_tx.send(event);
log_send_error("dep_tx", err); log_send_error("dep_tx", err);
} }
Interrupt::Save(event) => {
let changes = std::iter::repeat_n(&event, 1 + self.dedicates.len());
let proj = std::iter::once(&mut self.primary).chain(self.dedicates.iter_mut());
for (proj, saved_path) in proj.zip(changes) {
log::debug!(
"ProjectCompiler({}, rev={}): save changes",
proj.verse.revision.get(),
proj.id
);
// todo: only emit if saved_path is related
let _ = saved_path;
proj.reason.merge(reason_by_fs());
}
}
Interrupt::Fs(event) => { Interrupt::Fs(event) => {
log::debug!("ProjectCompiler: fs event incoming {event:?}"); log::debug!("ProjectCompiler: fs event incoming {event:?}");
@ -634,6 +654,12 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
let proj = std::iter::once(&mut self.primary).chain(self.dedicates.iter_mut()); let proj = std::iter::once(&mut self.primary).chain(self.dedicates.iter_mut());
for (proj, changes) in proj.zip(changes) { for (proj, changes) in proj.zip(changes) {
log::debug!(
"ProjectCompiler({}, rev={}): fs changes applying",
proj.verse.revision.get(),
proj.id
);
proj.verse.increment_revision(|verse| { proj.verse.increment_revision(|verse| {
let mut vfs = verse.vfs(); let mut vfs = verse.vfs();
@ -650,6 +676,12 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
vfs.notify_fs_changes(changes); vfs.notify_fs_changes(changes);
}); });
log::debug!(
"ProjectCompiler({},rev={}): fs changes applied, {is_sync}",
proj.id,
proj.verse.revision.get(),
);
if !self.ignore_first_sync || !is_sync { if !self.ignore_first_sync || !is_sync {
proj.reason.merge(reason_by_fs()); proj.reason.merge(reason_by_fs());
} }

View file

@ -87,6 +87,22 @@ impl ServerState {
self.update_sources(files) self.update_sources(files)
} }
/// Saves a source file.
pub fn save_source(&mut self, path: ImmutPath) -> Result<()> {
// FIXME: this is a workaround for the issue of notify, which does not fully
// emit fs changes.
//
// The case to fix:
// When editing a file, the last compilation sync deps to notify actor (rev=N-1,
// actual state=S). At the time, the next fs change comes, and notifier actor
// reads the change (notify state=S'). Next, another fs change comes (rev=N,
// actual state=S'). However, since the notifier actor read the state
// earlier (notify state=S'), the actor will not emit a change at rev N, bang...
self.project.interrupt(Interrupt::Save(path));
Ok(())
}
/// Queries a source file that must be in memory. /// Queries a source file that must be in memory.
pub fn query_source<T>( pub fn query_source<T>(
&self, &self,

View file

@ -113,7 +113,10 @@ impl ServerState {
Ok(()) Ok(())
} }
pub(crate) fn did_save(&mut self, _params: DidSaveTextDocumentParams) -> LspResult<()> { pub(crate) fn did_save(&mut self, params: DidSaveTextDocumentParams) -> LspResult<()> {
let path = as_path_(params.text_document.uri).as_path().into();
self.save_source(path).map_err(invalid_params)?;
Ok(()) Ok(())
} }
} }