feat: impl pull request file tree

This commit is contained in:
2026-05-28 00:37:35 +01:00
parent c4ebe91669
commit f8a1c3f42b
12 changed files with 642 additions and 249 deletions

View File

@@ -1,5 +1,6 @@
mod issue_list;
mod pull_request_diff_view;
mod pull_request_file_tree;
mod pull_request_view;
mod screen;
mod sidebar;

View File

@@ -7,7 +7,8 @@ use crate::{
text::text,
},
query::{self, QueryStatus, read_query, use_query, watch_query},
util,
screen::dashboard::pull_request_file_tree::{self, PullRequestFileTree},
util::{self},
};
use gpui::{AppContext, ParentElement, Styled, div};
@@ -20,6 +21,8 @@ pub(crate) struct PullRequestDiffView {
diff_view_state: DiffViewState,
diff_view_content: Option<DiffViewContent>,
file_tree: gpui::Entity<PullRequestFileTree>,
}
pub(crate) fn new(
@@ -31,7 +34,7 @@ pub(crate) fn new(
pr_query: use_query(api::issues::FetchPullRequest { id: pr_id.clone() }, cx),
file_tree_query: use_query(
api::issues::FetchPullRequestFileTree {
id: pr_id,
id: pr_id.clone(),
first: 100,
},
cx,
@@ -39,6 +42,8 @@ pub(crate) fn new(
content_diff_query: None,
diff_view_state: DiffViewState::new(),
diff_view_content: None,
file_tree: cx.new(|cx| pull_request_file_tree::new(pr_id, cx)),
};
view.on_create(cx);
view
@@ -117,8 +122,8 @@ impl PullRequestDiffView {
) {
if let Some(diff) = {
match read_query(query, cx) {
| QueryStatus::Loaded(diff) => Some(Arc::clone(diff)),
| _ => None,
| QueryStatus::Loaded(diff) => Some(Arc::clone(diff)),
| _ => None,
}
} {
self.load_diff_view(diff, cx);
@@ -153,16 +158,16 @@ impl PullRequestDiffView {
_ = cx
.spawn(async move |weak, cx| match tokio::join!(t1, t2) {
| (Some(old_side_highlights), Some(new_side_highlights)) => {
_ = weak.update(cx, |this, cx| {
this.diff_view_state
.set_old_side_highlights(old_side_highlights);
this.diff_view_state
.set_new_side_highlights(new_side_highlights);
cx.notify();
});
}
| _ => {}
| (Some(old_side_highlights), Some(new_side_highlights)) => {
_ = weak.update(cx, |this, cx| {
this.diff_view_state
.set_old_side_highlights(old_side_highlights);
this.diff_view_state
.set_new_side_highlights(new_side_highlights);
cx.notify();
});
}
| _ => {}
})
.detach();
}
@@ -184,18 +189,30 @@ impl gpui::Render for PullRequestDiffView {
.unwrap_or(QueryStatus::Loading);
match content_diff {
| QueryStatus::Err(_) | QueryStatus::Loading => div()
.size_full()
.bg(theme.colors.surface)
.p_4()
.child(text("asd")),
| QueryStatus::Err(_) | QueryStatus::Loading => div()
.size_full()
.bg(theme.colors.surface)
.p_4()
.child(text("asd")),
| QueryStatus::Loaded(_) => match &self.diff_view_content {
| Some(content) => div()
.size_full()
.child(diff_view(self.diff_view_state.clone(), content.clone())),
| None => div(),
},
| QueryStatus::Loaded(_) => match &self.diff_view_content {
| Some(content) => div()
.size_full()
.flex()
.flex_row()
.child(
div()
.flex()
.w_80()
.h_full()
.border_r_1()
.border_color(theme.colors.border_muted)
.p_1()
.child(self.file_tree.clone()),
)
.child(diff_view(self.diff_view_state.clone(), content.clone())),
| None => div(),
},
}
}
}

View File

@@ -0,0 +1,81 @@
use crate::{
api,
component::file_tree::{FileTree, FileTreeState, file_tree},
query::{self, QueryStatus, read_query, use_query, watch_query},
util::{
self,
file::{FileTreeItem, FileTreeItemKind},
},
};
pub(crate) struct PullRequestFileTree {
file_tree_query: query::Entity<api::issues::FetchPullRequestFileTree>,
file_tree_state: FileTreeState,
file_tree_items: Vec<FileTreeItem>,
}
pub(crate) fn new(
pr_id: api::issues::Id,
cx: &mut gpui::Context<PullRequestFileTree>,
) -> PullRequestFileTree {
let mut v = PullRequestFileTree {
file_tree_query: use_query(
api::issues::FetchPullRequestFileTree {
id: pr_id,
first: 100,
},
cx,
),
file_tree_state: FileTreeState::new(),
file_tree_items: Vec::new(),
};
v.on_create(cx);
v
}
impl PullRequestFileTree {
fn on_create(&mut self, cx: &mut gpui::Context<PullRequestFileTree>) {
watch_query(
&self.file_tree_query,
|this, query, cx| {
if let QueryStatus::Loaded(changed_files) = read_query(query, cx) {
let tree = util::file::build_file_tree(changed_files, |f| &f.path);
this.file_tree_state.reset(&tree);
this.file_tree_items = tree;
cx.notify();
};
},
cx,
)
.detach();
}
fn handle_file_tree_item_click(
&mut self,
i: usize,
cx: &mut gpui::Context<PullRequestFileTree>,
) {
let item = &self.file_tree_items[i];
if item.kind == FileTreeItemKind::Directory {
self.file_tree_state
.toggle_directory(&item.full_path, &self.file_tree_items);
cx.notify();
}
}
}
impl gpui::Render for PullRequestFileTree {
fn render(
&mut self,
_window: &mut gpui::Window,
cx: &mut gpui::prelude::Context<Self>,
) -> impl gpui::prelude::IntoElement {
let weak = cx.entity();
file_tree(self.file_tree_state.clone(), move |i, _, cx| {
weak.read(cx).file_tree_items[i].clone()
})
.on_item_click(cx.listener(|this, i, _, cx| {
this.handle_file_tree_item_click(*i, cx);
}))
}
}