mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
parent
5f7213730b
commit
cf9ce9d4ff
6 changed files with 182 additions and 12 deletions
91
Cargo.lock
generated
91
Cargo.lock
generated
|
|
@ -455,6 +455,18 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetreelist"
|
name = "filetreelist"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
@ -502,6 +514,15 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.24"
|
version = "0.3.24"
|
||||||
|
|
@ -673,6 +694,8 @@ dependencies = [
|
||||||
"gh-emoji",
|
"gh-emoji",
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
|
"notify",
|
||||||
|
"notify-debouncer-mini",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pprof",
|
"pprof",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
|
@ -764,6 +787,26 @@ dependencies = [
|
||||||
"str_stack",
|
"str_stack",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.9.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
|
@ -816,6 +859,26 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d6112e8f37b59803ac47a42d14f1f3a59bbf72fc6857ffc5be455e28a691f8e"
|
||||||
|
dependencies = [
|
||||||
|
"kqueue-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue-sys"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
|
@ -955,6 +1018,34 @@ version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"filetime",
|
||||||
|
"fsevent-sys",
|
||||||
|
"inotify",
|
||||||
|
"kqueue",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"walkdir",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify-debouncer-mini"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c538ea1dd436b41e751922510cfbcaea2def87ed6ed94aa1edc15dc31b4c179"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"notify",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-format"
|
name = "num-format"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ fuzzy-matcher = "0.3"
|
||||||
gh-emoji = { version = "1.0", optional = true }
|
gh-emoji = { version = "1.0", optional = true }
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
notify = "5.0"
|
||||||
|
notify-debouncer-mini = "0.2"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
rayon-core = "1.9"
|
rayon-core = "1.9"
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ allow = [
|
||||||
"MIT",
|
"MIT",
|
||||||
"Apache-2.0",
|
"Apache-2.0",
|
||||||
"BSD-2-Clause",
|
"BSD-2-Clause",
|
||||||
"BSD-3-Clause"
|
"BSD-3-Clause",
|
||||||
|
"CC0-1.0",
|
||||||
|
"ISC"
|
||||||
]
|
]
|
||||||
copyleft = "warn"
|
copyleft = "warn"
|
||||||
allow-osi-fsf-free = "neither"
|
allow-osi-fsf-free = "neither"
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ impl App {
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
key_config: KeyConfig,
|
key_config: KeyConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
log::trace!("open repo at: {:?}", repo);
|
log::trace!("open repo at: {:?}", &repo);
|
||||||
|
|
||||||
let queue = Queue::new();
|
let queue = Queue::new();
|
||||||
let theme = Rc::new(theme);
|
let theme = Rc::new(theme);
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -36,11 +36,15 @@ mod strings;
|
||||||
mod tabs;
|
mod tabs;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod version;
|
mod version;
|
||||||
|
mod watcher;
|
||||||
|
|
||||||
use crate::{app::App, args::process_cmdline};
|
use crate::{app::App, args::process_cmdline};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use app::QuitState;
|
use app::QuitState;
|
||||||
use asyncgit::{sync::RepoPath, AsyncGitNotification};
|
use asyncgit::{
|
||||||
|
sync::{utils::repo_work_dir, RepoPath},
|
||||||
|
AsyncGitNotification,
|
||||||
|
};
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use crossbeam_channel::{tick, unbounded, Receiver, Select};
|
use crossbeam_channel::{tick, unbounded, Receiver, Select};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
|
|
@ -67,14 +71,14 @@ use tui::{
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
use ui::style::Theme;
|
use ui::style::Theme;
|
||||||
|
use watcher::RepoWatcher;
|
||||||
|
|
||||||
static TICK_INTERVAL: Duration = Duration::from_secs(5);
|
|
||||||
static SPINNER_INTERVAL: Duration = Duration::from_millis(80);
|
static SPINNER_INTERVAL: Duration = Duration::from_millis(80);
|
||||||
|
|
||||||
///
|
///
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum QueueEvent {
|
pub enum QueueEvent {
|
||||||
Tick,
|
Notify,
|
||||||
SpinnerUpdate,
|
SpinnerUpdate,
|
||||||
AsyncEvent(AsyncNotification),
|
AsyncEvent(AsyncNotification),
|
||||||
InputEvent(InputEvent),
|
InputEvent(InputEvent),
|
||||||
|
|
@ -161,7 +165,8 @@ fn run_app(
|
||||||
let (tx_app, rx_app) = unbounded();
|
let (tx_app, rx_app) = unbounded();
|
||||||
|
|
||||||
let rx_input = input.receiver();
|
let rx_input = input.receiver();
|
||||||
let ticker = tick(TICK_INTERVAL);
|
let watcher = RepoWatcher::new(repo_work_dir(&repo)?.as_str())?;
|
||||||
|
let rx_watcher = watcher.receiver();
|
||||||
let spinner_ticker = tick(SPINNER_INTERVAL);
|
let spinner_ticker = tick(SPINNER_INTERVAL);
|
||||||
|
|
||||||
let mut app = App::new(
|
let mut app = App::new(
|
||||||
|
|
@ -179,13 +184,13 @@ fn run_app(
|
||||||
loop {
|
loop {
|
||||||
let event = if first_update {
|
let event = if first_update {
|
||||||
first_update = false;
|
first_update = false;
|
||||||
QueueEvent::Tick
|
QueueEvent::Notify
|
||||||
} else {
|
} else {
|
||||||
select_event(
|
select_event(
|
||||||
&rx_input,
|
&rx_input,
|
||||||
&rx_git,
|
&rx_git,
|
||||||
&rx_app,
|
&rx_app,
|
||||||
&ticker,
|
&rx_watcher,
|
||||||
&spinner_ticker,
|
&spinner_ticker,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
@ -208,7 +213,7 @@ fn run_app(
|
||||||
}
|
}
|
||||||
app.event(ev)?;
|
app.event(ev)?;
|
||||||
}
|
}
|
||||||
QueueEvent::Tick => app.update()?,
|
QueueEvent::Notify => app.update()?,
|
||||||
QueueEvent::AsyncEvent(ev) => {
|
QueueEvent::AsyncEvent(ev) => {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
ev,
|
ev,
|
||||||
|
|
@ -282,7 +287,7 @@ fn select_event(
|
||||||
rx_input: &Receiver<InputEvent>,
|
rx_input: &Receiver<InputEvent>,
|
||||||
rx_git: &Receiver<AsyncGitNotification>,
|
rx_git: &Receiver<AsyncGitNotification>,
|
||||||
rx_app: &Receiver<AsyncAppNotification>,
|
rx_app: &Receiver<AsyncAppNotification>,
|
||||||
rx_ticker: &Receiver<Instant>,
|
rx_notify: &Receiver<()>,
|
||||||
rx_spinner: &Receiver<Instant>,
|
rx_spinner: &Receiver<Instant>,
|
||||||
) -> Result<QueueEvent> {
|
) -> Result<QueueEvent> {
|
||||||
let mut sel = Select::new();
|
let mut sel = Select::new();
|
||||||
|
|
@ -290,7 +295,7 @@ fn select_event(
|
||||||
sel.recv(rx_input);
|
sel.recv(rx_input);
|
||||||
sel.recv(rx_git);
|
sel.recv(rx_git);
|
||||||
sel.recv(rx_app);
|
sel.recv(rx_app);
|
||||||
sel.recv(rx_ticker);
|
sel.recv(rx_notify);
|
||||||
sel.recv(rx_spinner);
|
sel.recv(rx_spinner);
|
||||||
|
|
||||||
let oper = sel.select();
|
let oper = sel.select();
|
||||||
|
|
@ -304,7 +309,7 @@ fn select_event(
|
||||||
2 => oper.recv(rx_app).map(|e| {
|
2 => oper.recv(rx_app).map(|e| {
|
||||||
QueueEvent::AsyncEvent(AsyncNotification::App(e))
|
QueueEvent::AsyncEvent(AsyncNotification::App(e))
|
||||||
}),
|
}),
|
||||||
3 => oper.recv(rx_ticker).map(|_| QueueEvent::Tick),
|
3 => oper.recv(rx_notify).map(|_| QueueEvent::Notify),
|
||||||
4 => oper.recv(rx_spinner).map(|_| QueueEvent::SpinnerUpdate),
|
4 => oper.recv(rx_spinner).map(|_| QueueEvent::SpinnerUpdate),
|
||||||
_ => bail!("unknown select source"),
|
_ => bail!("unknown select source"),
|
||||||
}?;
|
}?;
|
||||||
|
|
|
||||||
70
src/watcher.rs
Normal file
70
src/watcher.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use crossbeam_channel::{unbounded, Sender};
|
||||||
|
use notify::{Error, RecommendedWatcher, RecursiveMode};
|
||||||
|
use notify_debouncer_mini::{
|
||||||
|
new_debouncer, DebouncedEvent, Debouncer,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
path::Path, sync::mpsc::RecvError, thread, time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RepoWatcher {
|
||||||
|
receiver: crossbeam_channel::Receiver<()>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
debouncer: Debouncer<RecommendedWatcher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepoWatcher {
|
||||||
|
pub fn new(workdir: &str) -> Result<Self> {
|
||||||
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
|
let mut debouncer =
|
||||||
|
new_debouncer(Duration::from_secs(2), None, tx)?;
|
||||||
|
|
||||||
|
debouncer
|
||||||
|
.watcher()
|
||||||
|
.watch(Path::new(workdir), RecursiveMode::Recursive)?;
|
||||||
|
|
||||||
|
let (out_tx, out_rx) = unbounded();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(e) = Self::forwarder(&rx, &out_tx) {
|
||||||
|
//maybe we need to restart the forwarder now?
|
||||||
|
log::error!("notify receive error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
debouncer,
|
||||||
|
receiver: out_rx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn receiver(&self) -> crossbeam_channel::Receiver<()> {
|
||||||
|
self.receiver.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forwarder(
|
||||||
|
receiver: &std::sync::mpsc::Receiver<
|
||||||
|
Result<Vec<DebouncedEvent>, Vec<Error>>,
|
||||||
|
>,
|
||||||
|
sender: &Sender<()>,
|
||||||
|
) -> Result<(), RecvError> {
|
||||||
|
loop {
|
||||||
|
let ev = receiver.recv()?;
|
||||||
|
|
||||||
|
if let Ok(ev) = ev {
|
||||||
|
log::debug!("notify events: {}", ev.len());
|
||||||
|
|
||||||
|
for (idx, ev) in ev.iter().enumerate() {
|
||||||
|
log::debug!("notify [{}]: {:?}", idx, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ev.is_empty() {
|
||||||
|
sender.send(()).expect("send error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue