feat: syntax highlighting for diff view
This commit is contained in:
@@ -10,7 +10,7 @@ use crate::{
|
||||
font_icon::{FontIcon, FontIconSvg, font_icon},
|
||||
text::text,
|
||||
},
|
||||
query::{self, QueryStatus, read_query, use_query},
|
||||
query::{self, QueryStatus, read_query, use_query, watch_query},
|
||||
util::str::ToSharedString,
|
||||
};
|
||||
|
||||
@@ -56,28 +56,44 @@ pub(crate) fn new(cx: &mut gpui::Context<IssueList>) -> IssueList {
|
||||
|
||||
impl IssueList {
|
||||
fn on_create(&mut self, cx: &mut gpui::Context<Self>) {
|
||||
cx.observe(&self.pr_query, |this, _, cx| {
|
||||
let data = read_query(&this.pr_query, cx);
|
||||
if let QueryStatus::Loaded(res) = data {
|
||||
let old_len = this.list_state.item_count();
|
||||
let new_len = res.items.len();
|
||||
let pr_query = self.pr_query.clone();
|
||||
|
||||
let new_items = res.items.iter().enumerate().map(|(i, it)| IssueListItem {
|
||||
watch_query(&pr_query, Self::sync_pr_query, cx).detach();
|
||||
}
|
||||
|
||||
fn sync_pr_query(
|
||||
&mut self,
|
||||
query: &query::Entity<api::issues::ListPullRequests>,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) {
|
||||
let data = read_query(query, cx);
|
||||
if let QueryStatus::Loaded(res) = data {
|
||||
let selected_id = self
|
||||
.list_items
|
||||
.iter()
|
||||
.find(|item| item.is_selected)
|
||||
.map(|item| item.id.clone());
|
||||
let old_len = self.list_state.item_count();
|
||||
let new_len = res.items.len();
|
||||
|
||||
self.list_items = res
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, it)| IssueListItem {
|
||||
is_selected: selected_id.as_ref().is_some_and(|id| *id == it.id),
|
||||
id: it.id.clone(),
|
||||
repo_name: Some(it.repo_slug.to_shared_string()),
|
||||
title: it.title.to_shared_string(),
|
||||
description: None,
|
||||
status: it.state,
|
||||
is_selected: false,
|
||||
is_last: i == new_len - 1,
|
||||
is_draft: it.is_draft,
|
||||
});
|
||||
})
|
||||
.collect();
|
||||
|
||||
this.list_items.splice(old_len..old_len, new_items);
|
||||
this.list_state.splice(old_len..old_len, new_len);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
self.list_state.splice(0..old_len, new_len);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_click(&mut self, i: usize, cx: &mut gpui::Context<Self>) {
|
||||
|
||||
@@ -6,9 +6,10 @@ use crate::{
|
||||
diff_view::{DiffViewContent, DiffViewState, diff_view},
|
||||
text::text,
|
||||
},
|
||||
query::{self, QueryStatus, observe_query, read_query, use_query},
|
||||
query::{self, QueryStatus, read_query, use_query, watch_query},
|
||||
util,
|
||||
};
|
||||
use gpui::{ParentElement, Styled, div};
|
||||
use gpui::{AppContext, ParentElement, Styled, div};
|
||||
|
||||
pub(crate) struct PullRequestDiffView {
|
||||
selected_file_path: Option<Arc<str>>,
|
||||
@@ -104,23 +105,68 @@ impl PullRequestDiffView {
|
||||
},
|
||||
cx,
|
||||
);
|
||||
|
||||
_ = observe_query(
|
||||
&content_diff_query,
|
||||
|this, query, cx| {
|
||||
if let QueryStatus::Loaded(diff) = read_query(query, cx) {
|
||||
println!("diff len {}", diff.len());
|
||||
this.diff_view_state.reset(diff.len());
|
||||
this.diff_view_content = Some(Arc::clone(diff).into());
|
||||
}
|
||||
cx.notify();
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
_ = watch_query(&content_diff_query, Self::sync_content_diff_query, cx).detach();
|
||||
|
||||
self.content_diff_query = Some(content_diff_query);
|
||||
}
|
||||
|
||||
fn sync_content_diff_query(
|
||||
&mut self,
|
||||
query: &query::Entity<api::repo::FetchFileDiff>,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) {
|
||||
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<util::diff::ContentDiff>,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) {
|
||||
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.selected_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 {
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
markdown::{self, MarkdownText},
|
||||
text::text,
|
||||
},
|
||||
query::{self, QueryStatus, read_query, use_query},
|
||||
query::{self, QueryStatus, read_query, use_query, watch_query},
|
||||
screen::dashboard::pull_request_diff_view::{self, PullRequestDiffView},
|
||||
};
|
||||
|
||||
@@ -46,21 +46,20 @@ impl PullRequestView {
|
||||
|
||||
self.pull_request_query = Some(query.clone());
|
||||
|
||||
_ = cx
|
||||
.observe(&query.clone(), move |this, _, cx| {
|
||||
this.load_markdown_content(cx);
|
||||
this.load_pr_diff(cx);
|
||||
})
|
||||
.detach();
|
||||
|
||||
// cached query will not trigger observe callback
|
||||
// this is required so that content is loaded immediately for cached query
|
||||
self.load_markdown_content(cx);
|
||||
self.load_pr_diff(cx);
|
||||
_ = watch_query(&query, Self::sync_pull_request_query, cx).detach();
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn sync_pull_request_query(
|
||||
&mut self,
|
||||
_query: &query::Entity<api::issues::FetchPullRequest>,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) {
|
||||
self.load_markdown_content(cx);
|
||||
self.load_pr_diff(cx);
|
||||
}
|
||||
|
||||
fn load_markdown_content(&mut self, cx: &mut gpui::Context<Self>) {
|
||||
let Some(query) = &self.pull_request_query else {
|
||||
return;
|
||||
@@ -115,41 +114,41 @@ impl PullRequestView {
|
||||
.rounded_full();
|
||||
|
||||
match pr.state {
|
||||
| api::issues::PullRequestState::Open => {
|
||||
status_pill = status_pill
|
||||
.bg(theme.colors.success_solid)
|
||||
.child(
|
||||
font_icon(FontIcon::PullRequestArrow)
|
||||
.size_3()
|
||||
.text_color(theme.colors.success_on_solid),
|
||||
)
|
||||
.child(
|
||||
text("Open")
|
||||
.text_color(theme.colors.success_on_solid)
|
||||
| api::issues::PullRequestState::Open => {
|
||||
status_pill = status_pill
|
||||
.bg(theme.colors.success_solid)
|
||||
.child(
|
||||
font_icon(FontIcon::PullRequestArrow)
|
||||
.size_3()
|
||||
.text_color(theme.colors.success_on_solid),
|
||||
)
|
||||
.child(
|
||||
text("Open")
|
||||
.text_color(theme.colors.success_on_solid)
|
||||
.text_xs(),
|
||||
);
|
||||
}
|
||||
| api::issues::PullRequestState::Closed => {
|
||||
status_pill = status_pill
|
||||
.bg(theme.colors.danger_solid)
|
||||
.child(
|
||||
font_icon(FontIcon::PullRequestClosed)
|
||||
.size_3()
|
||||
.text_color(theme.colors.danger_on_solid),
|
||||
)
|
||||
.child(
|
||||
text("Closed")
|
||||
.text_color(theme.colors.danger_on_solid)
|
||||
.text_xs(),
|
||||
);
|
||||
}
|
||||
| api::issues::PullRequestState::Merged => {
|
||||
status_pill = status_pill.bg(theme.colors.accent_solid).child(
|
||||
text("Merged")
|
||||
.text_color(theme.colors.accent_on_solid)
|
||||
.text_xs(),
|
||||
);
|
||||
}
|
||||
| api::issues::PullRequestState::Closed => {
|
||||
status_pill = status_pill
|
||||
.bg(theme.colors.danger_solid)
|
||||
.child(
|
||||
font_icon(FontIcon::PullRequestClosed)
|
||||
.size_3()
|
||||
.text_color(theme.colors.danger_on_solid),
|
||||
)
|
||||
.child(
|
||||
text("Closed")
|
||||
.text_color(theme.colors.danger_on_solid)
|
||||
.text_xs(),
|
||||
);
|
||||
}
|
||||
| api::issues::PullRequestState::Merged => {
|
||||
status_pill = status_pill.bg(theme.colors.accent_solid).child(
|
||||
text("Merged")
|
||||
.text_color(theme.colors.accent_on_solid)
|
||||
.text_xs(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let merge_text = pr.author.as_ref().map(|author| {
|
||||
@@ -284,23 +283,23 @@ impl gpui::Render for PullRequestView {
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) -> impl gpui::IntoElement {
|
||||
div().size_full().child(match &self.pull_request_query {
|
||||
| Some(q) => match read_query(q, cx) {
|
||||
| QueryStatus::Loaded(pr) => match &self.diff_view {
|
||||
| Some(v) => v.clone().into_any_element(),
|
||||
| None => self.pr_content(pr, cx),
|
||||
},
|
||||
| Some(q) => match read_query(q, cx) {
|
||||
| QueryStatus::Loaded(pr) => match &self.diff_view {
|
||||
| Some(v) => v.clone().into_any_element(),
|
||||
| None => self.pr_content(pr, cx),
|
||||
},
|
||||
|
||||
| QueryStatus::Err(e) => div()
|
||||
.size_full()
|
||||
.child(format!("{:?}", e))
|
||||
.into_any_element(),
|
||||
| QueryStatus::Loading => div()
|
||||
.size_full()
|
||||
.child("loading pr content")
|
||||
.into_any_element(),
|
||||
},
|
||||
| QueryStatus::Err(e) => div()
|
||||
.size_full()
|
||||
.child(format!("{:?}", e))
|
||||
.into_any_element(),
|
||||
| QueryStatus::Loading => div()
|
||||
.size_full()
|
||||
.child("loading pr content")
|
||||
.into_any_element(),
|
||||
},
|
||||
|
||||
| None => div().size_full().child("no pr selected").into_any_element(),
|
||||
| None => div().size_full().child("no pr selected").into_any_element(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ impl Screen {
|
||||
fn on_create(&mut self, cx: &mut gpui::Context<Self>) {
|
||||
_ = cx
|
||||
.subscribe(&self.issue_list, |this, _, event, cx| match event {
|
||||
| issue_list::Event::ItemSelected(pr_id) => {
|
||||
this.handle_issue_list_item_selected(pr_id, cx);
|
||||
}
|
||||
| issue_list::Event::ItemSelected(pr_id) => {
|
||||
this.handle_issue_list_item_selected(pr_id, cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user