use std::collections::HashSet;
use std::path::Path;
use std::sync::{Arc, Mutex};
use lsp_types::request::Request;
use reflexo::ImmutPath;
use reflexo_typst::vfs::{FileChangeSet, PathAccessModel};
use reflexo_typst::Bytes;
use serde::{Deserialize, Serialize};
use sync_ls::TypedLspClient;
use tinymist_project::{Interrupt, FILE_MISSING_ERROR};
use typst::diag::FileResult;
use crate::vfs::notify::NotifyMessage;
use crate::ServerState;
impl ServerState {
pub(crate) fn handle_deps(&mut self) {
let Some(dep_rx) = self.dep_rx.as_mut() else {
return;
};
while let Ok(msg) = dep_rx.try_recv() {
match msg {
NotifyMessage::Settle => {}
NotifyMessage::SyncDependency(notify_deps) => {
let mut deps = HashSet::new();
notify_deps.dependencies(&mut |path| {
deps.insert(path.clone());
});
let am = self.config.watch_access_model(&self.client);
am.retain_watch(|path| deps.contains(path));
}
NotifyMessage::UpstreamUpdate(evt) => {
let changeset = FileChangeSet::new_inserts(vec![]);
self.project.interrupt(Interrupt::Fs(
crate::vfs::FilesystemEvent::UpstreamUpdate {
changeset,
upstream_event: Some(evt),
},
));
}
}
}
}
}
#[derive(Clone)]
pub struct WatchAccessModel {
pub watches: Arc<Mutex<HashSet<ImmutPath>>>,
pub client: TypedLspClient<ServerState>,
}
impl WatchAccessModel {
pub fn new(client: TypedLspClient<ServerState>) -> Self {
Self {
client,
watches: Arc::new(Mutex::new(HashSet::new())),
}
}
pub fn add_watch(&self, path: &Path) {
log::info!("add watch: {path:?}");
let req = FsWatchRequest {
inserts: vec![tinymist_query::path_to_url(path).unwrap()],
removes: vec![],
};
self.client
.send_lsp_request::<FsWatchRequest>(req, |_stat, resp| {
if let Some(e) = resp.result {
log::error!("Failed to watch file: {e:?}");
}
});
let path = ImmutPath::from(path);
let mut watches = self.watches.lock().unwrap();
watches.insert(path);
}
pub fn retain_watch(&self, filter: impl Fn(&Path) -> bool) {
let mut removes = vec![];
let mut watches = self.watches.lock().unwrap();
watches.retain(|path| {
if !filter(path) {
removes.push(tinymist_query::path_to_url(path.as_ref()).unwrap());
return false;
}
true
});
drop(watches);
let req = FsWatchRequest {
inserts: vec![],
removes,
};
self.client
.send_lsp_request::<FsWatchRequest>(req, |_stat, resp| {
if let Some(e) = resp.result {
log::error!("Failed to watch file: {e:?}");
}
});
}
}
impl PathAccessModel for WatchAccessModel {
fn content(&self, src: &Path) -> FileResult<Bytes> {
self.add_watch(src);
Err(FILE_MISSING_ERROR.clone())
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FsWatchRequest {
inserts: Vec<lsp_types::Url>,
removes: Vec<lsp_types::Url>,
}
impl Request for FsWatchRequest {
type Params = Self;
type Result = ();
const METHOD: &'static str = "tinymist/fs/watch";
}