chore: refactor the backend #17

Merged
reya merged 10 commits from chore/refactor into master 2026-09-10 09:43:36 +00:00
5 changed files with 1684 additions and 389 deletions
Showing only changes of commit 5f37f2ff2a - Show all commits
@@ -325,8 +325,6 @@ pub struct CommitDiffView {
error: Option<SharedString>,
/// Changed-files explorer and per-file diff, also used by the new PR panel's compare view.
pane: Entity<DiffPane>,
/// In-flight tasks, pruned on every push.
tasks: Vec<gpui::Task<Result<(), anyhow::Error>>>,
}
impl CommitDiffView {
@@ -358,7 +356,6 @@ impl CommitDiffView {
loading: true,
error: None,
pane,
tasks: Vec::new(),
}
}
@@ -371,7 +368,8 @@ impl CommitDiffView {
let worktree = self.worktree.clone();
let id = self.commit.id.clone();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
let commit = cx
.background_spawn({
let worktree = worktree.clone();
@@ -406,7 +404,7 @@ impl CommitDiffView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Header with the commit id, summary, author/time and overall change stats.
+30 -32
View File
@@ -10,8 +10,8 @@ use gix::Repository;
use gpui::prelude::*;
use gpui::{
Action, Anchor, AnyElement, App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
Focusable, PathPromptOptions, Pixels, Render, SharedString, Size, Subscription, Task,
WeakEntity, Window, div, px, relative, size, transparent_white,
Focusable, PathPromptOptions, Pixels, Render, SharedString, Size, Subscription, WeakEntity,
Window, div, px, relative, size, transparent_white,
};
use gpui_base::{Button as BaseButton, Disableable, Popover};
use gpui_component::alert::Alert;
@@ -175,9 +175,6 @@ pub struct RepoDetailView {
/// Bumped on every branch/tag switch.
/// In-flight loads with an older generation are discarded when they complete.
ref_generation: u64,
/// In-flight tasks, finished tasks are pruned on every push.
/// The vec stays bounded by the number of concurrent loads.
tasks: Vec<Task<Result<(), Error>>>,
/// Subscriptions keeping the selectors' confirm events alive.
_subscriptions: Vec<Subscription>,
/// `(path, branch)` ready-suggestions dismissed by the user, per panel.
@@ -346,7 +343,6 @@ impl RepoDetailView {
push_statuses: Vec::new(),
pending_upstream: None,
focus_handle: cx.focus_handle(),
tasks: Vec::new(),
_subscriptions: subscriptions,
}
}
@@ -363,7 +359,7 @@ impl RepoDetailView {
// Local repositories live on disk at their scan path.
// No clone step or network refresh applies here.
if let Some(local_path) = self.local_path.clone() {
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
let data = cx
.background_spawn(async move {
let repo = gix::open(&local_path)?;
@@ -383,7 +379,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
return;
}
@@ -411,7 +407,7 @@ impl RepoDetailView {
})
};
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
let disk = disk.await;
let had_clone = matches!(&disk, Ok(Some(_)));
@@ -531,7 +527,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Apply the loaded repository data.
@@ -619,7 +615,7 @@ impl RepoDetailView {
prompt: Some("Clone".into()),
});
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
// `Ok(Ok(Some(paths)))` means the user picked a folder.
// A cancel or picker failure resolves to anything else.
let picked = match prompt.await {
@@ -649,7 +645,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Preview the file at `path`, relative to the worktree root.
@@ -703,7 +699,7 @@ impl RepoDetailView {
self.load_commit(&path, cx);
let generation = self.ref_generation;
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
let path_for_read = path.clone();
let content = cx
.background_spawn(async move {
@@ -772,7 +768,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Queue `path` for the per-file commit query.
@@ -804,7 +800,7 @@ impl RepoDetailView {
let paths = std::mem::take(&mut self.pending_commits);
let generation = self.ref_generation;
let task = cx.spawn(async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
let rels: Vec<PathBuf> = paths.iter().map(PathBuf::from).collect();
let result = cx
.background_spawn(
@@ -834,7 +830,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Walk all commits reachable from HEAD on a background task.
@@ -852,7 +848,7 @@ impl RepoDetailView {
self.loading_all_commits = true;
let generation = self.ref_generation;
let task = cx.spawn(async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
let result = cx
.background_spawn(async move { signed_git::worktree_all_commits(&worktree) })
.await;
@@ -876,7 +872,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Open a new panel showing the diff of `commit_id`.
@@ -909,8 +905,9 @@ impl RepoDetailView {
self.error = None;
cx.notify();
self.tasks
.push(store.update(cx, |store, cx| store.push_repository(cx)));
store
.update(cx, |store, cx| store.push_repository(cx))
.detach();
}
/// Push the unpushed commits of the local checkout at `path`.
@@ -931,7 +928,7 @@ impl RepoDetailView {
self.error = None;
cx.notify();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
// The store owns the push, its busy flag and error reporting.
let push = this.update_in(cx, |_this, _window, cx| {
store.update(cx, |store, cx| store.push_checkout(path.clone(), cx))
@@ -948,7 +945,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Delete the repository from nostr, announcement, state and activity.
@@ -956,8 +953,9 @@ impl RepoDetailView {
let Some(store) = self.store.clone() else {
return;
};
self.tasks
.push(store.update(cx, |store, cx| store.delete_repository(cx)));
store
.update(cx, |store, cx| store.delete_repository(cx))
.detach();
}
/// Open the issues list panel in the dock area.
@@ -1025,7 +1023,7 @@ impl RepoDetailView {
});
self.pending_upstream = Some(addr);
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
for _ in 0..60 {
cx.background_executor()
.timer(Duration::from_millis(250))
@@ -1060,7 +1058,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Check out `name`, a branch or tag picked in the header.
@@ -1101,7 +1099,7 @@ impl RepoDetailView {
cx.notify();
let checkout_name = name.clone();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn_in(window, async move |this, cx| {
let result = cx
.background_spawn(async move {
match kind {
@@ -1131,7 +1129,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Restore a selector to `previous`, or clear it after a failed switch.
@@ -1157,7 +1155,7 @@ impl RepoDetailView {
return;
};
let task = cx.spawn(async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
let result = cx
.background_spawn(async move {
let snapshot = signed_git::worktree_snapshot(&worktree)?;
@@ -1218,7 +1216,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Refresh the file explorer, previews and commit list after the mirror
@@ -1233,7 +1231,7 @@ impl RepoDetailView {
return;
};
let task = cx.spawn(async move |this, cx| {
let task: gpui::Task<Result<(), Error>> = cx.spawn(async move |this, cx| {
let result = cx
.background_spawn(async move {
let snapshot = signed_git::worktree_snapshot(&worktree)?;
@@ -1314,7 +1312,7 @@ impl RepoDetailView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Drop the cached preview, editor and commit state of `path`.
@@ -6,8 +6,7 @@ use dock::{BasePanel, DockArea, Panel, PanelEvent, add_center_panel, panel_handl
use gpui::prelude::*;
use gpui::{
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, PathPromptOptions,
Pixels, Render, SharedString, Size, Subscription, Task, WeakEntity, Window, div, px, relative,
size,
Pixels, Render, SharedString, Size, Subscription, WeakEntity, Window, div, px, relative, size,
};
use gpui_base::{Button as BaseButton, StyledExt};
use gpui_component::button::{Button, ButtonVariants};
@@ -79,7 +78,6 @@ pub struct NewPullRequestView {
scroll_handle: VirtualListScrollHandle,
item_sizes: Rc<Vec<Size<Pixels>>>,
_subscriptions: Vec<Subscription>,
tasks: Vec<Task<Result<(), anyhow::Error>>>,
}
/// A fork-backed compare.
@@ -363,7 +361,6 @@ impl NewPullRequestView {
scroll_handle: VirtualListScrollHandle::new(),
item_sizes: Rc::new(Vec::new()),
_subscriptions: subscriptions,
tasks: Vec::new(),
};
// Prefill with the store's freshest associated checkout, no folder dialog.
@@ -426,7 +423,8 @@ impl NewPullRequestView {
prompt: Some("Choose local checkout".into()),
});
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
// `Ok(Ok(Some(paths)))` means the user picked a folder.
// A cancel or picker failure resolves to anything else.
let picked = match prompt.await {
@@ -444,7 +442,7 @@ impl NewPullRequestView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Apply `path` as the local checkout, no picker.
@@ -453,7 +451,8 @@ impl NewPullRequestView {
fn apply_folder_path(&mut self, path: PathBuf, window: &mut Window, cx: &mut Context<Self>) {
let path = path.to_string_lossy().to_string();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
// Branches and the current branch are read off the UI thread.
let info = cx
.background_spawn({
@@ -474,7 +473,7 @@ impl NewPullRequestView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Apply a picked checkout, filling the selectors and loading the compare.
@@ -615,7 +614,8 @@ impl NewPullRequestView {
self.error = None;
cx.notify();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
// The fork and base must share history for a merge-base to exist.
// The target's mirror is the object store both sides land in.
// `ensure_clone` fetches `origin` when the mirror already exists.
@@ -696,7 +696,7 @@ impl NewPullRequestView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Apply an imported fork, filling the selectors and loading the compare.
@@ -834,7 +834,8 @@ impl NewPullRequestView {
return;
}
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
let result = cx
.background_spawn({
let repo_path = repo_path.clone();
@@ -875,7 +876,8 @@ impl NewPullRequestView {
Ok((merge_base, commits, diff)) => {
this.merge_base = Some(merge_base);
let count = commits.len();
this.item_sizes = Rc::new(vec![size(px(0.), px(COMMIT_ROW_HEIGHT)); count]);
this.item_sizes =
Rc::new(vec![size(px(0.), px(COMMIT_ROW_HEIGHT)); count]);
this.commits = Some(commits);
this.pane.update(cx, |pane, cx| pane.set_diff(diff, cx));
}
@@ -893,7 +895,7 @@ impl NewPullRequestView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Publish the pull request.
@@ -927,7 +929,8 @@ impl NewPullRequestView {
self.error = None;
cx.notify();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
// Regenerate the series at submit time.
// The published patch covers the current tip of the compare branch.
let patch = cx
@@ -954,7 +957,8 @@ impl NewPullRequestView {
Err(error) => {
this.update_in(cx, |this, _window, cx| {
this.submitting = false;
this.error = Some(format!("Failed to generate the patch: {error}").into());
this.error =
Some(format!("Failed to generate the patch: {error}").into());
cx.notify();
})?;
return Ok(());
@@ -996,7 +1000,7 @@ impl NewPullRequestView {
Ok(())
});
self.tasks.push(task);
task.detach();
}
/// Open the diff of `commit_id`, from the Commits tab, in a new panel.
@@ -6,7 +6,7 @@ use dock::{BasePanel, DockArea, DockPlacement, Panel, PanelEvent, panel_handle};
use gpui::prelude::*;
use gpui::{
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render,
SharedString, Size, Task, WeakEntity, Window, div, px, relative, size,
SharedString, Size, WeakEntity, Window, div, px, relative, size,
};
use gpui_component::button::{Button, ButtonVariants};
use gpui_component::clipboard::Clipboard;
@@ -66,8 +66,6 @@ pub struct PullRequestDetailView {
commit_item_sizes: Rc<Vec<Size<Pixels>>>,
/// Virtual list state of the commits tab.
commit_scroll_handle: VirtualListScrollHandle,
/// In-flight tasks, finished tasks are pruned on every push.
tasks: Vec<Task<Result<(), anyhow::Error>>>,
}
impl PullRequestDetailView {
@@ -106,7 +104,6 @@ impl PullRequestDetailView {
pane,
commit_item_sizes: Rc::new(Vec::new()),
commit_scroll_handle: VirtualListScrollHandle::new(),
tasks: Vec::new(),
}
}
@@ -162,7 +159,8 @@ impl PullRequestDetailView {
self.description = description.into();
let task = cx.spawn_in(window, async move |this, cx| {
let task: gpui::Task<Result<(), anyhow::Error>> =
cx.spawn_in(window, async move |this, cx| {
let nostr_diff = cx
.background_spawn({
let patch = patch.clone();
@@ -202,8 +200,8 @@ impl PullRequestDetailView {
.ok_or_else(|| anyhow::anyhow!("repository has no worktree"))?
.to_path_buf();
let tip =
tip.ok_or_else(|| anyhow::anyhow!("pull request has no tip commit"))?;
let tip = tip
.ok_or_else(|| anyhow::anyhow!("pull request has no tip commit"))?;
let base = match base {
Some(base) => base,
@@ -217,7 +215,8 @@ impl PullRequestDetailView {
}
};
let diff = signed_git::worktree_commit_range_diff(&workdir, &base, &tip)?;
let diff =
signed_git::worktree_commit_range_diff(&workdir, &base, &tip)?;
let commits =
signed_git::worktree_commit_range_commits(&workdir, &base, &tip)?;
@@ -237,7 +236,8 @@ impl PullRequestDetailView {
this.loading = false;
this.worktree = worktree;
this.current_commit = current_commit.map(SharedString::from);
this.commit_item_sizes = Rc::new(vec![size(px(0.), px(ROW_HEIGHT)); commits.len()]);
this.commit_item_sizes =
Rc::new(vec![size(px(0.), px(ROW_HEIGHT)); commits.len()]);
this.commits = commits;
match diff {
@@ -255,8 +255,7 @@ impl PullRequestDetailView {
Ok(())
});
self.tasks.retain(|task| !task.is_ready());
self.tasks.push(task);
task.detach();
}
/// Open the diff of `commit_id` in the bottom dock of the area.
File diff suppressed because it is too large Load Diff