Files
oak-gpui/crates/fs/src/linux_watcher.rs
T
Caleb Heydon a47759fd03 Add initial FreeBSD support (#20480)
This PR adds initial support for FreeBSD
(https://github.com/zed-industries/zed/issues/15309). While there is
still work left to be done, it seems to be usable. As discussed by
@syobocat (https://github.com/zed-industries/zed/discussions/10247), the
changes were just adding ```target_os = "freebsd"``` to wherever it
checks if the OS is Linux.


![image](https://github.com/user-attachments/assets/80ea5b29-047f-4cbd-8263-42e5fa6c94b7)

Needs to be build with ```RUSTFLAGS="-C link-dead-code"```

Known Issues:
- There's an issue in ```crates/project/src/environment.rs``` where a
command fails because ```/bin/sh``` on FreeBSD doesn't support the
```-l``` option.

![image](https://github.com/user-attachments/assets/c3c38633-160f-4f47-8840-e3da67f6ebc8)
- The file/folder choosers provided by the ```ashpd``` crate don't work
on FreeBSD (at least with KDE). This isn't that bad since a fallback
dialog is used.

![image](https://github.com/user-attachments/assets/29373006-1eb9-4ed0-bd52-2d0047fab418)
 - Moving to trash won't work.
- Numerous tests fail (when running on FreeBSD). While I haven't looked
into this much, it appears that the corresponding features seem to work
fine.

Release Notes:

- Added initial support for FreeBSD
2024-11-11 18:39:05 +01:00

125 lines
3.8 KiB
Rust

use notify::EventKind;
use parking_lot::Mutex;
use std::sync::{Arc, OnceLock};
use util::ResultExt;
use crate::{PathEvent, PathEventKind, Watcher};
pub struct LinuxWatcher {
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
}
impl LinuxWatcher {
pub fn new(
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
) -> Self {
Self {
tx,
pending_path_events,
}
}
}
impl Watcher for LinuxWatcher {
fn add(&self, path: &std::path::Path) -> gpui::Result<()> {
let root_path = path.to_path_buf();
let tx = self.tx.clone();
let pending_paths = self.pending_path_events.clone();
use notify::Watcher;
global({
|g| {
g.add(move |event: &notify::Event| {
let kind = match event.kind {
EventKind::Create(_) => Some(PathEventKind::Created),
EventKind::Modify(_) => Some(PathEventKind::Changed),
EventKind::Remove(_) => Some(PathEventKind::Removed),
_ => None,
};
let mut path_events = event
.paths
.iter()
.filter_map(|event_path| {
event_path.starts_with(&root_path).then(|| PathEvent {
path: event_path.clone(),
kind,
})
})
.collect::<Vec<_>>();
if !path_events.is_empty() {
path_events.sort();
let mut pending_paths = pending_paths.lock();
if pending_paths.is_empty() {
tx.try_send(()).ok();
}
util::extend_sorted(
&mut *pending_paths,
path_events,
usize::MAX,
|a, b| a.path.cmp(&b.path),
);
}
})
}
})?;
global(|g| {
g.inotify
.lock()
.watch(path, notify::RecursiveMode::NonRecursive)
})??;
Ok(())
}
fn remove(&self, path: &std::path::Path) -> gpui::Result<()> {
use notify::Watcher;
Ok(global(|w| w.inotify.lock().unwatch(path))??)
}
}
pub struct GlobalWatcher {
// two mutexes because calling inotify.add triggers an inotify.event, which needs watchers.
#[cfg(target_os = "linux")]
pub(super) inotify: Mutex<notify::INotifyWatcher>,
#[cfg(target_os = "freebsd")]
pub(super) inotify: Mutex<notify::KqueueWatcher>,
pub(super) watchers: Mutex<Vec<Box<dyn Fn(&notify::Event) + Send + Sync>>>,
}
impl GlobalWatcher {
pub(super) fn add(&self, cb: impl Fn(&notify::Event) + Send + Sync + 'static) {
self.watchers.lock().push(Box::new(cb))
}
}
static INOTIFY_INSTANCE: OnceLock<anyhow::Result<GlobalWatcher, notify::Error>> = OnceLock::new();
fn handle_event(event: Result<notify::Event, notify::Error>) {
let Some(event) = event.log_err() else { return };
global::<()>(move |watcher| {
for f in watcher.watchers.lock().iter() {
f(&event)
}
})
.log_err();
}
pub fn global<T>(f: impl FnOnce(&GlobalWatcher) -> T) -> anyhow::Result<T> {
let result = INOTIFY_INSTANCE.get_or_init(|| {
notify::recommended_watcher(handle_event).map(|file_watcher| GlobalWatcher {
inotify: Mutex::new(file_watcher),
watchers: Default::default(),
})
});
match result {
Ok(g) => Ok(f(g)),
Err(e) => Err(anyhow::anyhow!("{}", e)),
}
}