Skip to content

Commit

Permalink
dev: vfs revise apis
Browse files Browse the repository at this point in the history
  • Loading branch information
Myriad-Dreamin committed Jan 18, 2025
1 parent 69081e4 commit bf149fc
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 209 deletions.
21 changes: 7 additions & 14 deletions crates/reflexo-typst/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ impl<F: CompilerFeat + Send + Sync + 'static> CompileActor<F> {
// Actual a delayed memory event.
reason = reason_by_mem();
}
verse.notify_fs_event(event)
verse.vfs().notify_fs_event(event)
});

reason
Expand Down Expand Up @@ -616,24 +616,17 @@ impl<F: CompilerFeat + Send + Sync + 'static> CompileActor<F> {

/// Apply memory changes to underlying compiler.
fn apply_memory_changes(verse: &mut Revising<CompilerUniverse<F>>, event: MemoryEvent) {
let mut vfs = verse.vfs();
if matches!(event, MemoryEvent::Sync(..)) {
verse.reset_shadow();
vfs.reset_shadow();
}
match event {
MemoryEvent::Update(event) | MemoryEvent::Sync(event) => {
for removes in event.removes {
let _ = verse.unmap_shadow(&removes);
for path in event.removes {
let _ = vfs.unmap_shadow(&path);
}
for (p, t) in event.inserts {
let insert_file = match t.content().cloned() {
Ok(content) => content,
Err(err) => {
log::error!("CompileActor: read memory file at {p:?}: {err}");
continue;
}
};

let _ = verse.map_shadow(&p, insert_file);
for (path, snap) in event.inserts {
let _ = vfs.map_shadow(&path, snap);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/reflexo-typst/src/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use tokio::sync::mpsc;
use typst::diag::FileError;

use crate::vfs::{
notify::{FileChangeSet, FileSnapshot, FilesystemEvent, NotifyMessage, UpstreamUpdateEvent},
notify::{FilesystemEvent, NotifyMessage, UpstreamUpdateEvent},
system::SystemAccessModel,
PathAccessModel,
FileChangeSet, FileSnapshot, PathAccessModel,
};
use crate::ImmutPath;

Expand Down
157 changes: 117 additions & 40 deletions crates/reflexo-vfs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ pub mod system;
/// [`Vfs`] will make a overlay access model over the provided dummy access
/// model.
pub mod dummy;

/// Provides snapshot models
pub mod snapshot;
pub use snapshot::*;

/// Provides notify access model which retrieves file system events and changes
/// from some notify backend.
pub mod notify;
Expand All @@ -39,6 +44,7 @@ pub use typst::syntax::FileId as TypstFileId;

pub use reflexo::time::Time;
pub use reflexo::ImmutPath;
use typst::syntax::Source;

use core::fmt;
use std::sync::OnceLock;
Expand Down Expand Up @@ -126,12 +132,14 @@ pub trait FsProvider {
#[derive(Debug, Clone, Default)]
struct VfsEntry {
bytes: Arc<OnceLock<FileResult<Bytes>>>,
source: Arc<OnceLock<FileResult<Source>>>,
}

/// Create a new `Vfs` harnessing over the given `access_model` specific for
/// `reflexo_world::CompilerWorld`. With vfs, we can minimize the
/// implementation overhead for [`AccessModel`] trait.
pub struct Vfs<M: PathAccessModel + Sized> {
touched_by_compile: bool,
managed: Arc<Mutex<RedBlackTreeMapSync<TypstFileId, VfsEntry>>>,
// access_model: TraceAccessModel<VfsAccessModel<M>>,
/// The wrapped access model.
Expand All @@ -147,6 +155,7 @@ impl<M: PathAccessModel + Sized> fmt::Debug for Vfs<M> {
impl<M: PathAccessModel + Clone + Sized> Vfs<M> {
pub fn snapshot(&self) -> Self {
Self {
touched_by_compile: self.touched_by_compile,
managed: self.managed.clone(),
access_model: self.access_model.clone(),
}
Expand Down Expand Up @@ -180,6 +189,7 @@ impl<M: PathAccessModel + Sized> Vfs<M> {
// let access_model = TraceAccessModel::new(access_model);

Self {
touched_by_compile: false,
managed: Arc::default(),
access_model,
}
Expand All @@ -201,14 +211,6 @@ impl<M: PathAccessModel + Sized> Vfs<M> {
self.access_model.inner.resolver.path_for_id(id)
}

/// Reset the shadowing files in [`OverlayAccessModel`].
///
/// Note: This function is independent from [`Vfs::reset`].
pub fn reset_shadow(&mut self) {
self.access_model.clear_shadow();
self.access_model.inner.inner.clear_shadow();
}

/// Get paths to all the shadowing files in [`OverlayAccessModel`].
pub fn shadow_paths(&self) -> Vec<ImmutPath> {
self.access_model.inner.inner.file_paths()
Expand All @@ -219,61 +221,136 @@ impl<M: PathAccessModel + Sized> Vfs<M> {
self.access_model.file_paths()
}

/// Returns the overall memory usage for the stored files.
pub fn memory_usage(&self) -> usize {
0
}

pub fn revise(&mut self) -> RevisingVfs<M> {
RevisingVfs { inner: self }
}

pub fn reset_shadow(&mut self) {
self.revise().reset_shadow();
}

pub fn map_shadow(&mut self, path: &Path, snap: FileSnapshot) -> FileResult<()> {
self.revise().map_shadow(path, snap)
}

pub fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
self.revise().unmap_shadow(path)
}

pub fn map_shadow_by_id(&mut self, file_id: TypstFileId, snap: FileSnapshot) -> FileResult<()> {
self.revise().map_shadow_by_id(file_id, snap)
}

pub fn remove_shadow_by_id(&mut self, file_id: TypstFileId) {
self.revise().remove_shadow_by_id(file_id);
}

pub fn notify_fs_event(&mut self, event: FilesystemEvent) {
self.revise().notify_fs_event(event);
}

/// Read a file.
pub fn read(&self, path: TypstFileId) -> FileResult<Bytes> {
let entry = self.slot(path, |entry| entry.bytes.clone());

let content = entry.get_or_init(|| self.access_model.content(path));
content.clone()
}

/// Read a source.
pub fn source(&self, path: TypstFileId) -> FileResult<Source> {
let (bytes, source) = self.slot(path, |entry| (entry.bytes.clone(), entry.source.clone()));

let source = source.get_or_init(|| {
let content = bytes
.get_or_init(|| self.access_model.content(path))
.as_ref()
.map_err(Clone::clone)?;

let content = std::str::from_utf8(content).map_err(|_| FileError::InvalidUtf8)?;

Ok(Source::new(path, content.into()))
});

source.clone()
}

/// Read a slot.
#[inline(always)]
fn slot<T>(&self, path: TypstFileId, f: impl FnOnce(&mut VfsEntry) -> T) -> T {
let mut m = self.managed.lock();

if let Some(entry) = m.get_mut(&path) {
f(entry)
} else {
let mut entry = VfsEntry::default();
let res = f(&mut entry);
m.insert_mut(path, entry);
res
}
}
}

pub struct RevisingVfs<'a, M: PathAccessModel + Sized> {
inner: &'a mut Vfs<M>,
}

impl<M: PathAccessModel + Sized> RevisingVfs<'_, M> {
pub fn vfs(&mut self) -> &mut Vfs<M> {
self.inner
}

fn access_model(&mut self) -> &mut VfsAccessModel<M> {
&mut self.inner.access_model
}

/// Reset the shadowing files in [`OverlayAccessModel`].
///
/// Note: This function is independent from [`Vfs::reset`].
pub fn reset_shadow(&mut self) {
self.access_model().clear_shadow();
self.access_model().inner.inner.clear_shadow();
}

/// Add a shadowing file to the [`OverlayAccessModel`].
pub fn map_shadow(&mut self, path: &Path, content: Bytes) -> FileResult<()> {
self.access_model
pub fn map_shadow(&mut self, path: &Path, snap: FileSnapshot) -> FileResult<()> {
self.access_model()
.inner
.inner
.add_file(path, content, |c| c.into());
.add_file(path, snap, |c| c.into());

Ok(())
}

/// Remove a shadowing file from the [`OverlayAccessModel`].
pub fn remove_shadow(&mut self, path: &Path) {
self.access_model.inner.inner.remove_file(path);
pub fn unmap_shadow(&mut self, path: &Path) -> FileResult<()> {
self.access_model().inner.inner.remove_file(path);

Ok(())
}

/// Add a shadowing file to the [`OverlayAccessModel`] by file id.
pub fn map_shadow_by_id(&mut self, file_id: TypstFileId, content: Bytes) -> FileResult<()> {
self.access_model.add_file(&file_id, content, |c| *c);
pub fn map_shadow_by_id(&mut self, file_id: TypstFileId, snap: FileSnapshot) -> FileResult<()> {
self.access_model().add_file(&file_id, snap, |c| *c);

Ok(())
}

/// Remove a shadowing file from the [`OverlayAccessModel`] by file id.
pub fn remove_shadow_by_id(&mut self, file_id: TypstFileId) {
self.access_model.remove_file(&file_id);
self.access_model().remove_file(&file_id);
}

/// Let the vfs notify the access model with a filesystem event.
///
/// See [`NotifyAccessModel`] for more information.
pub fn notify_fs_event(&mut self, event: FilesystemEvent) {
self.access_model.inner.inner.inner.notify(event);
}

/// Returns the overall memory usage for the stored files.
pub fn memory_usage(&self) -> usize {
0
}

/// Read a file.
pub fn read(&self, path: TypstFileId) -> FileResult<Bytes> {
let entry = {
let mut m = self.managed.lock();

if let Some(entry) = m.get(&path) {
entry.bytes.clone()
} else {
let entry = VfsEntry::default();
m.insert_mut(path, entry.clone());
entry.bytes.clone()
}
};

let content = entry.get_or_init(|| self.access_model.content(path));
content.clone()
self.access_model().inner.inner.inner.notify(event);
}
}

Expand Down
Loading

0 comments on commit bf149fc

Please sign in to comment.