use std::sync::Arc; use gpui::{AppContext, IntoElement, div}; use crate::{ api::{self}, app, component::diff_view::{DiffViewContent, DiffViewState, diff_view}, query::{self, QueryStatus, read_query, use_query, watch_query}, util, }; pub(crate) struct PullRequestDiffView { pr_query: query::Entity, content_diff_query: Option>, diff_view_state: DiffViewState, diff_view_content: Option, current_file_path: Option>, } impl PullRequestDiffView { 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 } 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); } fn start_content_queries(&mut self, cx: &mut gpui::Context) { if self.content_diff_query.is_some() { return; } let Some(selected_file_path) = self.current_file_path.as_deref() else { return; }; let Some((old_file_ref, new_file_ref)) = ({ if let QueryStatus::Loaded(pr) = read_query(&self.pr_query, cx) { Some(( api::repo::FileRef { repo_slug: pr.base_repo_slug.clone(), path: Arc::from(selected_file_path), reff: Some(pr.base_ref.clone()), }, api::repo::FileRef { repo_slug: pr.head_repo_slug.clone(), path: Arc::from(selected_file_path), reff: Some(pr.head_ref.clone()), }, )) } else { None } }) else { return; }; let content_diff_query = use_query( api::repo::FetchFileDiff { base: old_file_ref, head: new_file_ref, }, cx, ); _ = watch_query(&content_diff_query, Self::sync_content_diff_query, cx).detach(); 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, cx: &mut gpui::Context, ) { if let Some(diff) = { match read_query(query, cx) { | QueryStatus::Loaded(diff) => Some(Arc::clone(diff)), | _ => None, } } { self.load_diff_view(diff, cx); cx.notify(); } } fn load_diff_view( &mut self, content_diff: Arc, cx: &mut gpui::Context, ) { let theme = app::current_theme(cx); let old_content = content_diff.old_content.clone(); let new_content = content_diff.new_content.clone(); self.diff_view_state.reset(content_diff.len()); self.diff_view_content = Some(content_diff.into()); let theme_syntax = theme.syntax; if let Some(path) = &self.current_file_path { let path = Arc::clone(&path); let file_type = util::file::file_type_from_path(&path); let t1 = cx.background_spawn(async move { util::syntax_highlight::highlight_content(old_content, file_type, &theme_syntax) }); let t2 = cx.background_spawn(async move { util::syntax_highlight::highlight_content(new_content, file_type, &theme_syntax) }); _ = 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(); }); } | _ => {} }) .detach(); } } } impl gpui::Render for PullRequestDiffView { fn render( &mut self, _window: &mut gpui::Window, cx: &mut gpui::prelude::Context, ) -> impl gpui::IntoElement { let content_diff = self .content_diff_query .as_ref() .map(|q| read_query(q, cx)) .unwrap_or(QueryStatus::Loading); match (content_diff, &self.diff_view_content) { | (QueryStatus::Loaded(_), Some(content)) => { diff_view(self.diff_view_state.clone(), content.clone()).into_any_element() } | (_, _) => div().into_any_element(), } } }