diff --git a/src/component/file_tree.rs b/src/component/file_tree.rs index f97b951..01b823c 100644 --- a/src/component/file_tree.rs +++ b/src/component/file_tree.rs @@ -28,6 +28,7 @@ pub(crate) struct FileTree { pub(crate) struct Item { item: util::file::FileTreeItem, is_expanded: bool, + is_highlighed: bool, on_click: Box, } @@ -38,6 +39,7 @@ struct FileTreeStateInner { list_state: gpui::ListState, collapsed_dirs: HashSet>, visible_items: Vec, + highlighted_items: HashSet, } pub(crate) fn file_tree( @@ -57,6 +59,7 @@ impl FileTreeState { list_state: gpui::ListState::new(0, gpui::ListAlignment::Top, px(50.)), collapsed_dirs: HashSet::new(), visible_items: Vec::new(), + highlighted_items: HashSet::new(), }))) } @@ -66,6 +69,12 @@ impl FileTreeState { state.visible_items = (0..items.len()).collect::>(); } + pub(crate) fn highlight_item(&mut self, index: usize) { + let mut state = self.0.borrow_mut(); + state.highlighted_items.clear(); + state.highlighted_items.insert(index); + } + pub(crate) fn toggle_directory( &mut self, dir_path: &Arc, @@ -125,6 +134,7 @@ impl gpui::RenderOnce for FileTree { let item = (self.item)(item_index, window, cx); let item = Item { is_expanded: !state.collapsed_dirs.contains(&item.full_path), + is_highlighed: state.highlighted_items.contains(&item_index), item, on_click: Box::new(move |window, cx| { if let Some(f) = &on_item_click { @@ -153,7 +163,13 @@ impl gpui::RenderOnce for Item { .py_0p5() .pl(px(8. + (8 * self.item.level) as f32)) .rounded_sm() - .hover(|it| it.bg(theme.colors.surface_hover)) + .hover(|it| { + if self.is_highlighed { + it + } else { + it.bg(theme.colors.surface_hover) + } + }) .on_click(move |_, window, cx| (self.on_click)(window, cx)) .when( matches!(self.item.kind, FileTreeItemKind::Directory), @@ -171,6 +187,10 @@ impl gpui::RenderOnce for Item { .when(matches!(self.item.kind, FileTreeItemKind::File), |it| { it.child(font_icon(FontIcon::FileBracesCorner).size_3()) }) + .when(self.is_highlighed, |it| { + it.bg(theme.colors.accent_muted) + .text_color(theme.colors.accent_fg) + }) .child(text(gpui::SharedString::new(self.item.name)).text_sm()) } } diff --git a/src/screen/dashboard/mod.rs b/src/screen/dashboard/mod.rs index b229758..abd440a 100644 --- a/src/screen/dashboard/mod.rs +++ b/src/screen/dashboard/mod.rs @@ -1,4 +1,5 @@ mod issue_list; +mod pull_request_change_view; mod pull_request_diff_view; mod pull_request_file_tree; mod pull_request_view; diff --git a/src/screen/dashboard/pull_request_change_view.rs b/src/screen/dashboard/pull_request_change_view.rs new file mode 100644 index 0000000..9a9c985 --- /dev/null +++ b/src/screen/dashboard/pull_request_change_view.rs @@ -0,0 +1,119 @@ +use std::sync::Arc; + +use crate::{ + api, app, + component::file_tree::{FileTreeState, file_tree}, + query::{self, QueryStatus, read_query, use_query, watch_query}, + screen::dashboard::pull_request_diff_view::PullRequestDiffView, + util::{ + self, + file::{FileTreeItem, FileTreeItemKind}, + }, +}; +use gpui::{AppContext, ParentElement, Styled, div}; + +pub(crate) struct PullRequestChangeView { + selected_file_path: Option>, + diff_view: gpui::Entity, + + file_tree_query: query::Entity, + file_tree_state: FileTreeState, + file_tree_items: Vec, +} + +pub(crate) fn new( + pr_id: api::issues::Id, + cx: &mut gpui::Context, +) -> PullRequestChangeView { + let mut view = PullRequestChangeView { + selected_file_path: None, + diff_view: cx.new(|cx| PullRequestDiffView::new(pr_id.clone(), cx)), + + file_tree_query: use_query( + api::issues::FetchPullRequestFileTree { + id: pr_id, + first: 100, + }, + cx, + ), + file_tree_state: FileTreeState::new(), + file_tree_items: Vec::new(), + }; + view.on_create(cx); + view +} + +impl PullRequestChangeView { + fn on_create(&mut self, cx: &mut gpui::Context) { + 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) { + let item = &self.file_tree_items[i]; + match item.kind { + | FileTreeItemKind::Directory => { + self.file_tree_state + .toggle_directory(&item.full_path, &self.file_tree_items); + cx.notify(); + } + | FileTreeItemKind::File => { + self.selected_file_path = Some(Arc::clone(&item.full_path)); + self.file_tree_state.highlight_item(i); + self.diff_view.update(cx, |diff_view, cx| { + diff_view.show_diff_for_file(&item.full_path, cx); + }); + cx.notify(); + } + } + } +} + +impl gpui::Render for PullRequestChangeView { + fn render( + &mut self, + _window: &mut gpui::Window, + cx: &mut gpui::Context, + ) -> impl gpui::IntoElement { + let theme = app::current_theme(cx); + let weak = cx.entity(); + + div() + .size_full() + .flex() + .flex_row() + .child( + div() + .flex() + .w_80() + .h_full() + .bg(theme.colors.surface_chrome) + .border_r_1() + .border_color(theme.colors.border_muted) + .p_1() + .child( + 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); + })), + ), + ) + .child( + gpui::AnyView::from(self.diff_view.clone()) + .cached(gpui::StyleRefinement::default().size_full()), + ) + } +} diff --git a/src/screen/dashboard/pull_request_diff_view.rs b/src/screen/dashboard/pull_request_diff_view.rs index 7aa9a55..98955c8 100644 --- a/src/screen/dashboard/pull_request_diff_view.rs +++ b/src/screen/dashboard/pull_request_diff_view.rs @@ -1,69 +1,42 @@ use std::sync::Arc; +use gpui::{AppContext, IntoElement, div}; + use crate::{ - api, app, - component::{ - diff_view::{DiffViewContent, DiffViewState, diff_view}, - text::text, - }, + api::{self}, + app, + component::diff_view::{DiffViewContent, DiffViewState, diff_view}, query::{self, QueryStatus, read_query, use_query, watch_query}, - screen::dashboard::pull_request_file_tree::{self, PullRequestFileTree}, - util::{self}, + util, }; -use gpui::{AppContext, ParentElement, Styled, div}; pub(crate) struct PullRequestDiffView { - selected_file_path: Option>, - pr_query: query::Entity, - file_tree_query: query::Entity, content_diff_query: Option>, - diff_view_state: DiffViewState, diff_view_content: Option, - - file_tree: gpui::Entity, -} - -pub(crate) fn new( - pr_id: api::issues::Id, - cx: &mut gpui::Context, -) -> PullRequestDiffView { - let mut view = PullRequestDiffView { - selected_file_path: None, - pr_query: use_query(api::issues::FetchPullRequest { id: pr_id.clone() }, cx), - file_tree_query: use_query( - api::issues::FetchPullRequestFileTree { - id: pr_id.clone(), - first: 100, - }, - cx, - ), - 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 + current_file_path: Option>, } impl PullRequestDiffView { - fn on_create(&mut self, cx: &mut gpui::Context) { - _ = cx - .observe(&self.pr_query, |this, _, cx| { - this.start_content_queries(cx); - }) - .detach(); + pub(crate) fn new(pr_id: api::issues::Id, cx: &mut gpui::Context) -> Self { + let mut s = Self { + pr_query: use_query(api::issues::FetchPullRequest { id: pr_id }, cx), + content_diff_query: None, + diff_view_state: DiffViewState::new(), + diff_view_content: None, + current_file_path: None, + }; + s.on_create(cx); + s + } - _ = cx - .observe(&self.file_tree_query, |this, _, cx| { - this.start_content_queries(cx); - }) - .detach(); - - // if pr is already loaded, start content queries + pub(crate) fn show_diff_for_file( + &mut self, + file_path: &Arc, + cx: &mut gpui::Context, + ) { + self.current_file_path = Some(Arc::clone(file_path)); self.start_content_queries(cx); } @@ -71,14 +44,7 @@ impl PullRequestDiffView { if self.content_diff_query.is_some() { return; } - - if self.selected_file_path.is_none() - && let QueryStatus::Loaded(files) = read_query(&self.file_tree_query, cx) - { - self.selected_file_path = files.first().map(|file| Arc::clone(&file.path)); - } - - let Some(selected_file_path) = self.selected_file_path.as_deref() else { + let Some(selected_file_path) = self.current_file_path.as_deref() else { return; }; @@ -115,6 +81,14 @@ impl PullRequestDiffView { self.content_diff_query = Some(content_diff_query); } + fn on_create(&mut self, cx: &mut gpui::Context) { + _ = cx + .observe(&self.pr_query, |this, _, cx| { + this.start_content_queries(cx); + }) + .detach(); + } + fn sync_content_diff_query( &mut self, query: &query::Entity, @@ -122,8 +96,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); @@ -145,7 +119,7 @@ impl PullRequestDiffView { let theme_syntax = theme.syntax; - if let Some(path) = &self.selected_file_path { + if let Some(path) = &self.current_file_path { let path = Arc::clone(&path); let file_type = util::file::file_type_from_path(&path); @@ -158,16 +132,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(); } @@ -178,42 +152,20 @@ impl gpui::Render for PullRequestDiffView { fn render( &mut self, _window: &mut gpui::Window, - cx: &mut gpui::Context, + cx: &mut gpui::prelude::Context, ) -> impl gpui::IntoElement { - let theme = app::current_theme(cx); - let content_diff = self .content_diff_query .as_ref() .map(|q| read_query(q, cx)) .unwrap_or(QueryStatus::Loading); - match content_diff { - | QueryStatus::Err(_) | QueryStatus::Loading => div() - .size_full() - .bg(theme.colors.surface) - .p_4() - .child(text("asd")), + match (content_diff, &self.diff_view_content) { + | (QueryStatus::Loaded(_), Some(content)) => { + diff_view(self.diff_view_state.clone(), content.clone()).into_any_element() + } - | QueryStatus::Loaded(_) => match &self.diff_view_content { - | Some(content) => div() - .size_full() - .flex() - .flex_row() - .child( - div() - .flex() - .w_80() - .h_full() - .bg(theme.colors.surface_chrome) - .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(), - }, + | (_, _) => div().into_any_element(), } } } diff --git a/src/screen/dashboard/pull_request_view.rs b/src/screen/dashboard/pull_request_view.rs index b268b11..5cc4e61 100644 --- a/src/screen/dashboard/pull_request_view.rs +++ b/src/screen/dashboard/pull_request_view.rs @@ -16,14 +16,14 @@ use crate::{ text::text, }, query::{self, QueryStatus, read_query, use_query, watch_query}, - screen::dashboard::pull_request_diff_view::{self, PullRequestDiffView}, + screen::dashboard::pull_request_change_view::{self, PullRequestChangeView}, }; pub(crate) struct PullRequestView { current_tab: Tab, markdown_viewer: Option>, - diff_view: Option>, + diff_view: Option>, pull_request_query: Option>, } @@ -101,7 +101,7 @@ impl PullRequestView { } }; - self.diff_view = pr_id.map(|id| cx.new(|cx| pull_request_diff_view::new(id, cx))); + self.diff_view = pr_id.map(|id| cx.new(|cx| pull_request_change_view::new(id, cx))); cx.notify(); } diff --git a/src/screen/dashboard/screen.rs b/src/screen/dashboard/screen.rs index d877079..dfbcc8e 100644 --- a/src/screen/dashboard/screen.rs +++ b/src/screen/dashboard/screen.rs @@ -4,7 +4,7 @@ use crate::{ api, app, screen::dashboard::{ issue_list::{self, IssueList}, - pull_request_diff_view::{self, PullRequestDiffView}, + pull_request_change_view::{self, PullRequestChangeView}, pull_request_view::{self, PullRequestView}, titlebar::{self, TitleBar}, }, @@ -14,7 +14,7 @@ pub(crate) struct Screen { titlebar: gpui::Entity, issue_list: gpui::Entity, pull_request_view: gpui::Entity, - pull_request_diff_view: Option>, + pull_request_change_view: Option>, issue_filter: Option<&'static str>, } @@ -24,7 +24,7 @@ pub(crate) fn new(cx: &mut gpui::Context) -> Screen { titlebar: cx.new(titlebar::new), issue_list: cx.new(issue_list::new), pull_request_view: cx.new(pull_request_view::new), - pull_request_diff_view: None, + pull_request_change_view: None, issue_filter: None, }; @@ -54,8 +54,8 @@ impl Screen { println!("change displayed pull request: {:?}", id); cx.notify(); }); - self.pull_request_diff_view = - Some(cx.new(|cx| pull_request_diff_view::new(id.clone(), cx))); + self.pull_request_change_view = + Some(cx.new(|cx| pull_request_change_view::new(id.clone(), cx))); } }