zellij/zellij-server/src/plugins/watch_filesystem.rs
Aram Drevekenin ee16a4b8c3
feat(plugins): session manager cwd and new filepicker (#3200)
* prototype

* folder selection ui in session manager

* overhaul strider

* scan folder host command

* get strider to work from the cli and some cli pipe fixes

* some ux improvements to strider

* improve strider's ui

* make strider ui responsive

* make session-manager new ui parts responsive

* fix tests

* style(fmt): rustfmt
2024-03-18 09:19:58 +01:00

135 lines
5.3 KiB
Rust

use super::PluginInstruction;
use std::path::PathBuf;
use crate::thread_bus::ThreadSenders;
use std::path::Path;
use std::time::Duration;
use zellij_utils::notify_debouncer_full::{
new_debouncer,
notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher},
DebounceEventResult, Debouncer, FileIdMap,
};
use zellij_utils::{data::Event, errors::prelude::Result};
const DEBOUNCE_DURATION_MS: u64 = 400;
pub fn watch_filesystem(
senders: ThreadSenders,
zellij_cwd: &Path,
) -> Result<Debouncer<RecommendedWatcher, FileIdMap>> {
let path_prefix_in_plugins = PathBuf::from("/host");
let current_dir = PathBuf::from(zellij_cwd);
let mut debouncer = new_debouncer(
Duration::from_millis(DEBOUNCE_DURATION_MS),
None,
move |result: DebounceEventResult| match result {
Ok(events) => {
let mut create_events = vec![];
let mut read_events = vec![];
let mut update_events = vec![];
let mut delete_events = vec![];
for event in events {
match event.kind {
EventKind::Access(_) => read_events.push(event),
EventKind::Create(_) => create_events.push(event),
EventKind::Modify(_) => update_events.push(event),
EventKind::Remove(_) => delete_events.push(event),
_ => {},
}
}
let create_paths: Vec<PathBuf> = create_events
.drain(..)
.map(|e| {
e.paths
.iter()
.map(|p| {
let stripped_prefix_path =
p.strip_prefix(&current_dir).unwrap_or_else(|_| p);
path_prefix_in_plugins.join(stripped_prefix_path)
})
.collect()
})
.collect();
let read_paths: Vec<PathBuf> = read_events
.drain(..)
.map(|e| {
e.paths
.iter()
.map(|p| {
let stripped_prefix_path =
p.strip_prefix(&current_dir).unwrap_or_else(|_| p);
path_prefix_in_plugins.join(stripped_prefix_path)
})
.collect()
})
.collect();
let update_paths: Vec<PathBuf> = update_events
.drain(..)
.map(|e| {
e.paths
.iter()
.map(|p| {
let stripped_prefix_path =
p.strip_prefix(&current_dir).unwrap_or_else(|_| p);
path_prefix_in_plugins.join(stripped_prefix_path)
})
.collect()
})
.collect();
let delete_paths: Vec<PathBuf> = delete_events
.drain(..)
.map(|e| {
e.paths
.iter()
.map(|p| {
let stripped_prefix_path =
p.strip_prefix(&current_dir).unwrap_or_else(|_| p);
path_prefix_in_plugins.join(stripped_prefix_path)
})
.collect()
})
.collect();
// TODO: at some point we might want to add FileMetadata to these, but right now
// the API is a bit unstable, so let's not rock the boat too much by adding another
// expensive syscall
let _ = senders.send_to_plugin(PluginInstruction::Update(vec![
(
None,
None,
Event::FileSystemRead(read_paths.into_iter().map(|p| (p, None)).collect()),
),
(
None,
None,
Event::FileSystemCreate(
create_paths.into_iter().map(|p| (p, None)).collect(),
),
),
(
None,
None,
Event::FileSystemUpdate(
update_paths.into_iter().map(|p| (p, None)).collect(),
),
),
(
None,
None,
Event::FileSystemDelete(
delete_paths.into_iter().map(|p| (p, None)).collect(),
),
),
]));
},
Err(errors) => errors
.iter()
.for_each(|error| log::error!("watch error: {error:?}")),
},
)?;
debouncer
.watcher()
.watch(zellij_cwd, RecursiveMode::Recursive)?;
Ok(debouncer)
}