Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ Its tests run against real repositories made with `git` in a temporary folder

- `app.rs` holds the state. Views never change it directly: they push an `Action`, which
`Workspace::apply` carries out after the frame. Anything that touches the repository runs on a
background thread and is followed by a reload, so the window never waits. The tab on screen
also reloads by itself when its watcher saw a change.
background thread and is followed by a reload, so the window never waits; what the user asks for
in the meantime runs next. The tab on screen also reloads by itself when its watcher saw a
change.
- `start.rs` (open, clone and create), `ui.rs` (tabs and toolbar), `graph.rs`, `sidebar.rs`, `details.rs`
and `diff.rs` draw the views, and `theme.rs` has the colours.

Expand Down
16 changes: 13 additions & 3 deletions crates/app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ pub struct Workspace {
/// The graph rows on screen.
pub visible: Range<usize>,
job: Option<Job>,
/// What the user asked for while a job ran, to run after it, in order.
queue: Vec<Op>,
/// Set when something changed since the last reload began.
changed: Arc<AtomicBool>,
/// Keeps `changed` up to date.
Expand Down Expand Up @@ -207,6 +209,7 @@ impl Workspace {
scroll_to: None,
visible: 0..0,
job: None,
queue: Vec::new(),
changed: Arc::default(),
watch: None,
quiet_until: Instant::now(),
Expand Down Expand Up @@ -294,7 +297,10 @@ impl Workspace {
}

fn start(&mut self, ctx: &egui::Context, op: Option<Op>) {
if self.job.is_none() {
if self.job.is_some() {
// A change waits its turn; a reload follows the running job anyway.
self.queue.extend(op);
} else {
self.spawn(ctx, op, false);
}
}
Expand Down Expand Up @@ -322,8 +328,8 @@ impl Workspace {
self.job = Some(Job { op, outcome, started: Instant::now() });
}

/// Takes in the result of the background job, once it is there.
pub fn poll(&mut self) {
/// Takes in the result of the background job, once it is there, and starts the next.
pub fn poll(&mut self, ctx: &egui::Context) {
let Some(job) = &self.job else { return };
let (failed, snapshot, watch) = match job.outcome.try_recv() {
Err(TryRecvError::Empty) => return,
Expand All @@ -332,6 +338,10 @@ impl Workspace {
};
let job = self.job.take().expect("a job is running");
self.quiet_until = Instant::now() + job.started.elapsed().max(Duration::from_millis(100));
if !self.queue.is_empty() {
let next = self.queue.remove(0);
self.spawn(ctx, Some(next), false);
}
let op = job.op;
if failed.is_none() && matches!(op, Some(Op::Commit { .. })) {
self.summary.clear();
Expand Down
17 changes: 16 additions & 1 deletion crates/app/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use egui_kittest::kittest::Queryable;
use gitcraft_core::git::{Git, Op};
use tempfile::TempDir;

use crate::app::Selection;
use crate::app::{Action, Selection};
use crate::start::percent;
use crate::ui::{App, KEYS};

Expand Down Expand Up @@ -288,6 +288,21 @@ fn discard_single_lines_and_hunks() {
assert!(harness.query_by_label("Uncommitted changes").is_none());
}

#[test]
fn what_is_asked_for_while_git_is_busy_runs_next() {
let (_dir, git) = repo();
fs::write(git.dir().join("file.txt"), "changed").unwrap();
let mut harness = app(Some(git.dir()));
let ctx = harness.ctx.clone();
let ws = harness.state_mut().workspace_mut().unwrap();
ws.apply(&ctx, Action::Refresh);
ws.apply(&ctx, Action::Run(Op::Stage(vec!["file.txt".into()])));
ws.apply(&ctx, Action::Run(Op::Commit { message: "Second commit".into(), amend: false }));
settle(&mut harness);

assert_eq!(log(&git), "Second commit\nFirst commit\n");
}

#[test]
fn changes_made_outside_show_up_by_themselves() {
let (_dir, git) = repo();
Expand Down
5 changes: 2 additions & 3 deletions crates/app/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl App {
for path in std::mem::take(&mut self.pending) {
self.open(&ctx, &path);
}
self.tabs.iter_mut().for_each(Workspace::poll);
self.tabs.iter_mut().for_each(|ws| ws.poll(&ctx));
// A tab out of sight reloads when it is shown.
if let Some(ws) = self.workspace_mut() {
ws.reload_if_changed(&ctx);
Expand Down Expand Up @@ -269,8 +269,7 @@ fn confirm(ctx: &egui::Context, ws: &mut Workspace, actions: &mut Vec<Action>) {
ui.strong(&confirm.question);
ui.label("This cannot be undone.");
ui.horizontal(|ui| {
let verb = Button::new(egui::RichText::new(confirm.verb).color(theme::ERROR));
if ui.add_enabled(ws.busy().is_none(), verb).clicked() {
if ui.button(egui::RichText::new(confirm.verb).color(theme::ERROR)).clicked() {
actions.push(Action::Run(confirm.op.clone()));
done = true;
}
Expand Down
Loading