Skip to content

Commit f502c81

Browse files
author
Stephan Dilly
committed
spinner showing pending background work
1 parent 7c678a9 commit f502c81

File tree

5 files changed

+93
-12
lines changed

5 files changed

+93
-12
lines changed

asyncgit/src/diff.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use crossbeam_channel::Sender;
33
use log::trace;
44
use std::{
55
hash::Hash,
6-
sync::{Arc, Mutex},
6+
sync::{
7+
atomic::{AtomicUsize, Ordering},
8+
Arc, Mutex,
9+
},
710
};
811

912
///
@@ -24,6 +27,7 @@ pub struct AsyncDiff {
2427
current: Arc<Mutex<Request<u64, FileDiff>>>,
2528
last: Arc<Mutex<Option<LastResult<DiffParams, FileDiff>>>>,
2629
sender: Sender<AsyncNotification>,
30+
pending: Arc<AtomicUsize>,
2731
}
2832

2933
impl AsyncDiff {
@@ -33,6 +37,7 @@ impl AsyncDiff {
3337
current: Arc::new(Mutex::new(Request(0, None))),
3438
last: Arc::new(Mutex::new(None)),
3539
sender,
40+
pending: Arc::new(AtomicUsize::new(0)),
3641
}
3742
}
3843

@@ -54,6 +59,11 @@ impl AsyncDiff {
5459
}
5560
}
5661

62+
///
63+
pub fn is_pending(&self) -> bool {
64+
self.pending.load(Ordering::Relaxed) > 0
65+
}
66+
5767
///
5868
pub fn request(
5969
&mut self,
@@ -77,7 +87,10 @@ impl AsyncDiff {
7787
let arc_current = Arc::clone(&self.current);
7888
let arc_last = Arc::clone(&self.last);
7989
let sender = self.sender.clone();
90+
let arc_pending = Arc::clone(&self.pending);
8091
rayon_core::spawn(move || {
92+
arc_pending.fetch_add(1, Ordering::Relaxed);
93+
8194
let res =
8295
sync::diff::get_diff(CWD, params.0.clone(), params.1);
8396
let mut notify = false;
@@ -98,6 +111,8 @@ impl AsyncDiff {
98111
});
99112
}
100113

114+
arc_pending.fetch_sub(1, Ordering::Relaxed);
115+
101116
if notify {
102117
sender
103118
.send(AsyncNotification::Diff)

src/app.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,9 @@ impl App {
229229
self.do_quit
230230
}
231231

232-
fn can_focus_diff(&self) -> bool {
233-
match self.focus {
234-
Focus::WorkDir => self.index_wd.is_file_seleted(),
235-
Focus::Stage => self.index.is_file_seleted(),
236-
_ => false,
237-
}
232+
///
233+
pub fn any_work_pending(&self) -> bool {
234+
self.git_diff.is_pending()
238235
}
239236
}
240237

@@ -278,6 +275,14 @@ impl App {
278275
None
279276
}
280277

278+
fn can_focus_diff(&self) -> bool {
279+
match self.focus {
280+
Focus::WorkDir => self.index_wd.is_file_seleted(),
281+
Focus::Stage => self.index.is_file_seleted(),
282+
_ => false,
283+
}
284+
}
285+
281286
fn update_commands(&mut self) {
282287
self.help.set_cmds(self.commands(true));
283288
self.current_commands = self.commands(false);

src/main.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod components;
88
mod keys;
99
mod poll;
1010
mod queue;
11+
mod spinner;
1112
mod strings;
1213
mod ui;
1314
mod version;
@@ -28,6 +29,7 @@ use log::error;
2829
use scopeguard::defer;
2930
use scopetime::scope_time;
3031
use simplelog::{Config, LevelFilter, WriteLogger};
32+
use spinner::Spinner;
3133
use std::{
3234
env, fs,
3335
fs::File,
@@ -40,6 +42,7 @@ use tui::{
4042
};
4143

4244
static TICK_INTERVAL: Duration = Duration::from_secs(5);
45+
static SPINNER_INTERVAL: Duration = Duration::from_millis(50);
4346

4447
fn main() -> Result<()> {
4548
setup_logging();
@@ -65,28 +68,44 @@ fn main() -> Result<()> {
6568
set_panic_handlers();
6669

6770
let rx_input = poll::start_polling_thread();
68-
6971
let ticker = tick(TICK_INTERVAL);
72+
let spinner_ticker = tick(SPINNER_INTERVAL);
7073

7174
app.update();
7275
draw(&mut terminal, &mut app)?;
7376

77+
let mut spinner = Spinner::default();
78+
7479
loop {
75-
let events: Vec<QueueEvent> =
76-
select_event(&rx_input, &rx_git, &ticker);
80+
let events: Vec<QueueEvent> = select_event(
81+
&rx_input,
82+
&rx_git,
83+
&ticker,
84+
&spinner_ticker,
85+
);
7786

7887
{
7988
scope_time!("loop");
8089

90+
let mut needs_draw = true;
91+
8192
for e in events {
8293
match e {
8394
QueueEvent::InputEvent(ev) => app.event(ev),
8495
QueueEvent::Tick => app.update(),
8596
QueueEvent::GitEvent(ev) => app.update_git(ev),
97+
QueueEvent::SpinnerUpdate => {
98+
needs_draw = false;
99+
spinner.update()
100+
}
86101
}
87102
}
88103

89-
draw(&mut terminal, &mut app)?;
104+
if needs_draw {
105+
draw(&mut terminal, &mut app)?;
106+
}
107+
108+
spinner.draw(&mut terminal, app.any_work_pending())?;
90109

91110
if app.is_quit() {
92111
break;
@@ -112,6 +131,7 @@ fn select_event(
112131
rx_input: &Receiver<Vec<QueueEvent>>,
113132
rx_git: &Receiver<AsyncNotification>,
114133
rx_ticker: &Receiver<Instant>,
134+
rx_spinner: &Receiver<Instant>,
115135
) -> Vec<QueueEvent> {
116136
let mut events: Vec<QueueEvent> = Vec::new();
117137

@@ -120,6 +140,7 @@ fn select_event(
120140
sel.recv(rx_input);
121141
sel.recv(rx_git);
122142
sel.recv(rx_ticker);
143+
sel.recv(rx_spinner);
123144

124145
let oper = sel.select();
125146
let index = oper.index();
@@ -132,7 +153,10 @@ fn select_event(
132153
2 => oper
133154
.recv(rx_ticker)
134155
.map(|_| events.push(QueueEvent::Tick)),
135-
_ => Ok(()),
156+
3 => oper
157+
.recv(rx_spinner)
158+
.map(|_| events.push(QueueEvent::SpinnerUpdate)),
159+
_ => panic!("unknown select source"),
136160
}
137161
.unwrap();
138162

src/poll.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::time::{Duration, Instant};
77
#[derive(Clone, Copy)]
88
pub enum QueueEvent {
99
Tick,
10+
SpinnerUpdate,
1011
GitEvent(AsyncNotification),
1112
InputEvent(Event),
1213
}

src/spinner.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::io;
2+
use tui::{backend::Backend, buffer::Cell, Terminal};
3+
4+
static SPINNER_CHARS: &[char] = &['|', '/', '-', '\\'];
5+
6+
///
7+
#[derive(Default)]
8+
pub struct Spinner {
9+
idx: usize,
10+
}
11+
12+
impl Spinner {
13+
///
14+
pub fn update(&mut self) {
15+
self.idx += 1;
16+
self.idx %= SPINNER_CHARS.len();
17+
}
18+
19+
pub fn draw<B: Backend>(
20+
&self,
21+
terminal: &mut Terminal<B>,
22+
pending: bool,
23+
) -> io::Result<()> {
24+
let idx = self.idx;
25+
26+
let c: Cell = Cell::default()
27+
.set_char(if pending { SPINNER_CHARS[idx] } else { ' ' })
28+
.clone();
29+
terminal
30+
.backend_mut()
31+
.draw(vec![(0_u16, 0_u16, &c)].into_iter())?;
32+
tui::backend::Backend::flush(terminal.backend_mut())?;
33+
34+
Ok(())
35+
}
36+
}

0 commit comments

Comments
 (0)