1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
use super::PluginInstruction;
use std::path::PathBuf;
use crate::thread_bus::ThreadSenders;
use std::path::Path;
use std::time::Duration;
use notify_debouncer_full::{
new_debouncer,
notify::{EventKind, RecommendedWatcher, RecursiveMode},
DebounceEventResult, Debouncer, RecommendedCache,
};
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, RecommendedCache>> {
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(¤t_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(¤t_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(¤t_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(¤t_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 mut updates = vec![];
if !read_paths.is_empty() {
updates.push((
None,
None,
Event::FileSystemRead(read_paths.into_iter().map(|p| (p, None)).collect()),
));
}
if !create_paths.is_empty() {
updates.push((
None,
None,
Event::FileSystemCreate(
create_paths.into_iter().map(|p| (p, None)).collect(),
),
));
}
if !update_paths.is_empty() {
updates.push((
None,
None,
Event::FileSystemUpdate(
update_paths.into_iter().map(|p| (p, None)).collect(),
),
));
}
if !delete_paths.is_empty() {
updates.push((
None,
None,
Event::FileSystemDelete(
delete_paths.into_iter().map(|p| (p, None)).collect(),
),
));
}
if !updates.is_empty() {
let _ = senders.send_to_plugin(PluginInstruction::Update(updates));
}
},
Err(errors) => errors
.iter()
.for_each(|error| log::error!("watch error: {error:?}")),
},
)?;
debouncer.watch(zellij_cwd, RecursiveMode::Recursive)?;
Ok(debouncer)
}
|