Skip to content

Commit

Permalink
feat: (wip) project config system
Browse files Browse the repository at this point in the history
  • Loading branch information
Cubxity committed May 9, 2023
1 parent c001cf3 commit 6018bae
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 112 deletions.
14 changes: 10 additions & 4 deletions src-tauri/src/ipc/commands/typst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde::Serialize;
use serde_repr::Serialize_repr;
use siphasher::sip128::{Hasher128, SipHasher};
use std::hash::Hash;
use std::panic::catch_unwind;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
Expand Down Expand Up @@ -73,17 +74,22 @@ pub async fn typst_compile<R: Runtime>(
.slot_update(path.as_path(), Some(content))
.map_err(Into::<Error>::into)?;

// TODO: Configurable main
world.set_main(source_id);
if !world.is_main_set() {
let config = project.config.read().unwrap();
if config.apply_main(&*project, &mut *world).is_err() {
debug!("skipped compilation for {:?} (main not set)", project);
return Ok(());
}
}

debug!("compiling: {:?}", path);
debug!("compiling: {:?}", project);
let now = Instant::now();
match typst::compile(&*world) {
Ok(doc) => {
let elapsed = now.elapsed();
debug!(
"compilation succeeded for {:?} in {:?} ms",
path,
project,
elapsed.as_millis()
);

Expand Down
10 changes: 3 additions & 7 deletions src-tauri/src/menu.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::project::{Project, ProjectManager, ProjectWorld};
use crate::project::{Project, ProjectManager};
use std::fs;
use std::sync::{Arc, RwLock};
use std::sync::Arc;
use tauri::api::dialog::FileDialogBuilder;
use tauri::{Manager, Runtime, State, WindowMenuEvent};

Expand All @@ -14,11 +14,7 @@ pub fn handle_menu_event<R: Runtime>(e: WindowMenuEvent<R>) {

let window = e.window();
let project_manager: State<'_, Arc<ProjectManager<_>>> = window.state();
let project = Arc::new(Project {
world: ProjectWorld::new(path.clone()).into(),
cache: RwLock::new(Default::default()),
root: path,
});
let project = Arc::new(Project::load_from_path(path));
project_manager.set_project(window, Some(project));
}
}),
Expand Down
165 changes: 165 additions & 0 deletions src-tauri/src/project/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use crate::ipc::{FSRefreshEvent, ProjectChangeEvent, ProjectModel};
use crate::project::{is_project_config_file, Project, ProjectConfig};
use log::{debug, error, info, trace};
use notify::event::ModifyKind;
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, RwLock};
use tauri::{Runtime, Window};
use tokio::sync::mpsc::channel;

#[derive(Clone, Copy, Debug)]
enum FSHandleKind {
Refresh,
Reload,
}

pub struct ProjectManager<R: Runtime> {
projects: RwLock<HashMap<Window<R>, Arc<Project>>>,
watcher: Mutex<Option<Box<dyn Watcher + Send + Sync>>>,
}

impl<R: Runtime> ProjectManager<R> {
pub fn init_watcher(
project_manager: Arc<ProjectManager<R>>,
) -> anyhow::Result<Box<dyn Watcher + Send + Sync>> {
let (tx, mut rx) = channel(1);

let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;

let watcher = RecommendedWatcher::new(
move |res| {
let _ = rt.block_on(tx.send(res));
},
Config::default(),
)?;

tokio::spawn(async move {
while let Some(res) = rx.recv().await {
match res {
Ok(event) => project_manager.handle_fs_event(event),
Err(e) => error!("watch error {:?}", e),
}
}
});

Ok(Box::new(watcher))
}

pub fn set_watcher(&self, watcher: Box<dyn Watcher + Send + Sync>) {
let mut inner = self.watcher.lock().unwrap();
*inner = Some(watcher);
}

pub fn get_project(&self, window: &Window<R>) -> Option<Arc<Project>> {
self.projects.read().unwrap().get(window).cloned()
}

pub fn set_project(&self, window: &Window<R>, project: Option<Arc<Project>>) {
let mut projects = self.projects.write().unwrap();
let model = project.as_ref().map(|p| ProjectModel {
root: p.root.clone(),
});
match project {
None => {
if let Some(old) = projects.remove(window) {
let mut guard = self.watcher.lock().unwrap();
if let Some(watcher) = guard.as_mut() {
let _ = watcher.unwatch(&old.root);
}
}
}
Some(p) => {
p.config.read().unwrap().apply(&*p);

let root = &p.root.clone();
let mut guard = self.watcher.lock().unwrap();
if let Some(old) = projects.insert(window.clone(), p) {
if let Some(watcher) = guard.as_mut() {
let _ = watcher.unwatch(&old.root);
}
}
if let Some(watcher) = guard.as_mut() {
let _ = watcher.watch(root, RecursiveMode::Recursive);
}
}
};

info!("project set for window {}: {:?}", window.label(), model);
let _ = window.emit("project_changed", ProjectChangeEvent { project: model });
}

fn handle_fs_event(&self, event: notify::Event) {
let opt = match event.kind {
EventKind::Create(_) | EventKind::Remove(_) => event.paths[0]
.parent()
.map(|p| (p.to_path_buf(), FSHandleKind::Refresh)),
EventKind::Modify(kind) => match kind {
ModifyKind::Name(_) => event.paths[0]
.parent()
.map(|p| (p.to_path_buf(), FSHandleKind::Refresh)),
ModifyKind::Data(_) => Some((event.paths[0].clone(), FSHandleKind::Reload)),
_ => None,
},
_ => None,
};

if let Some((path, kind)) = opt {
let path = path.canonicalize().unwrap_or(path);
let projects = self.projects.read().unwrap();

for (window, project) in &*projects {
if path.starts_with(&project.root) {
self.handle_project_fs_event(project, window, &path, kind);
}
}
}
}

fn handle_project_fs_event(
&self,
project: &Project,
window: &Window<R>,
path: &PathBuf,
kind: FSHandleKind,
) {
trace!(
"handling fs event for {:?} (path: {:?}, kind: {:?})",
project,
path,
kind
);
match kind {
FSHandleKind::Refresh => {
if let Ok(relative) = path.strip_prefix(&project.root) {
let event = FSRefreshEvent {
path: relative.to_path_buf(),
};
let _ = window.emit("fs_refresh", &event);
}
}
FSHandleKind::Reload => {
if let Ok(relative) = path.strip_prefix(&project.root) {
if is_project_config_file(relative) {
if let Ok(config) = ProjectConfig::read_from_file(path) {
debug!("updating project config for {:?}: {:?}", project, config);
let mut config_write = project.config.write().unwrap();
*config_write = config;
config_write.apply(project);
}
}
}
}
}
}

pub fn new() -> Self {
Self {
projects: RwLock::new(HashMap::new()),
watcher: Mutex::new(None),
}
}
}
2 changes: 2 additions & 0 deletions src-tauri/src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod project;
mod world;
mod manager;

pub use project::*;
pub use world::*;
pub use manager::*;

0 comments on commit 6018bae

Please sign in to comment.