feat: pr tab switching btwn body/diff
This commit is contained in:
26
build.rs
26
build.rs
@@ -234,12 +234,12 @@ fn render_github_fixtures(fixture_root: &Path) -> String {
|
|||||||
output.push_str(&string_literal(&path));
|
output.push_str(&string_literal(&path));
|
||||||
output.push_str(", ");
|
output.push_str(", ");
|
||||||
match reff {
|
match reff {
|
||||||
| Some(reff) => {
|
| Some(reff) => {
|
||||||
output.push_str("Some(");
|
output.push_str("Some(");
|
||||||
output.push_str(&string_literal(&reff));
|
output.push_str(&string_literal(&reff));
|
||||||
output.push(')');
|
output.push(')');
|
||||||
}
|
}
|
||||||
| None => output.push_str("None"),
|
| None => output.push_str("None"),
|
||||||
}
|
}
|
||||||
output.push_str(") => Some(");
|
output.push_str(") => Some(");
|
||||||
output.push_str(&string_literal(&content));
|
output.push_str(&string_literal(&content));
|
||||||
@@ -293,8 +293,8 @@ fn render_github_fixtures(fixture_root: &Path) -> String {
|
|||||||
output.push_str(&string_literal(&id));
|
output.push_str(&string_literal(&id));
|
||||||
output.push_str(", ");
|
output.push_str(", ");
|
||||||
match previous_end_cursor.as_deref() {
|
match previous_end_cursor.as_deref() {
|
||||||
| Some(after) => output.push_str(&format!("Some({})", string_literal(after))),
|
| Some(after) => output.push_str(&format!("Some({})", string_literal(after))),
|
||||||
| None => output.push_str("None"),
|
| None => output.push_str("None"),
|
||||||
}
|
}
|
||||||
output.push_str(") => Some(");
|
output.push_str(") => Some(");
|
||||||
output.push_str(&string_literal(&fixture.json));
|
output.push_str(&string_literal(&fixture.json));
|
||||||
@@ -429,9 +429,9 @@ fn issue_fixture_state(issue: &serde_json::Value) -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match required_string(issue, &["state"]) {
|
match required_string(issue, &["state"]) {
|
||||||
| "open" => "OPEN",
|
| "open" => "OPEN",
|
||||||
| "closed" => "CLOSED",
|
| "closed" => "CLOSED",
|
||||||
| state => panic!("unsupported pull request state in fixture: {state}"),
|
| state => panic!("unsupported pull request state in fixture: {state}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,8 +500,8 @@ fn collect_repo_file_content_fixtures(
|
|||||||
let owner = parts[0].clone();
|
let owner = parts[0].clone();
|
||||||
let repo = parts[1].clone();
|
let repo = parts[1].clone();
|
||||||
let reff = match parts[2].as_str() {
|
let reff = match parts[2].as_str() {
|
||||||
| "@default" => None,
|
| "@default" => None,
|
||||||
| value => Some(value.to_owned()),
|
| value => Some(value.to_owned()),
|
||||||
};
|
};
|
||||||
let virtual_path = parts[3..].join("/");
|
let virtual_path = parts[3..].join("/");
|
||||||
let content = fs::read_to_string(&path).unwrap_or_else(|err| {
|
let content = fs::read_to_string(&path).unwrap_or_else(|err| {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use gpui::{HighlightStyle, IntoElement, ParentElement, Styled, div, list, px};
|
use gpui::{IntoElement, ParentElement, Styled, div, list, px};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component::code_view::{self, CodeLine, code_line, code_line_with_highlights},
|
component::code_view::{self, CodeLine, code_line, code_line_with_highlights},
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ pub(crate) mod code_view;
|
|||||||
pub(crate) mod diff_view;
|
pub(crate) mod diff_view;
|
||||||
pub(crate) mod font_icon;
|
pub(crate) mod font_icon;
|
||||||
pub(crate) mod markdown;
|
pub(crate) mod markdown;
|
||||||
|
pub(crate) mod segmented_control;
|
||||||
pub(crate) mod text;
|
pub(crate) mod text;
|
||||||
|
|||||||
119
src/component/segmented_control.rs
Normal file
119
src/component/segmented_control.rs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
InteractiveElement, IntoElement, ParentElement, StatefulInteractiveElement, Styled, div,
|
||||||
|
prelude::FluentBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app,
|
||||||
|
component::font_icon::{self, font_icon},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(gpui::IntoElement)]
|
||||||
|
pub(crate) struct SegmentedControl<T>
|
||||||
|
where
|
||||||
|
T: Copy + Eq + 'static,
|
||||||
|
{
|
||||||
|
items: Vec<SegmentedControlItem<T>>,
|
||||||
|
selected_value: Option<T>,
|
||||||
|
on_select: Option<Rc<dyn Fn(&T, &mut gpui::Window, &mut gpui::App)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SegmentedControlItem<T>
|
||||||
|
where
|
||||||
|
T: Copy + Eq + 'static,
|
||||||
|
{
|
||||||
|
value: T,
|
||||||
|
icon: font_icon::FontIcon,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn segmented_control<T>() -> SegmentedControl<T>
|
||||||
|
where
|
||||||
|
T: Copy + Eq + 'static,
|
||||||
|
{
|
||||||
|
SegmentedControl {
|
||||||
|
items: Vec::new(),
|
||||||
|
selected_value: None,
|
||||||
|
on_select: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SegmentedControl<T>
|
||||||
|
where
|
||||||
|
T: Copy + Eq + 'static,
|
||||||
|
{
|
||||||
|
pub(crate) fn item(mut self, value: T, icon: font_icon::FontIcon) -> Self {
|
||||||
|
self.items
|
||||||
|
.extend(std::iter::once(SegmentedControlItem { value, icon }));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_select(
|
||||||
|
mut self,
|
||||||
|
f: impl Fn(&T, &mut gpui::Window, &mut gpui::App) + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.on_select = Some(Rc::new(f));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn selected_value(mut self, value: T) -> Self {
|
||||||
|
self.selected_value = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> gpui::RenderOnce for SegmentedControl<T>
|
||||||
|
where
|
||||||
|
T: Copy + Eq + 'static,
|
||||||
|
{
|
||||||
|
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement {
|
||||||
|
let theme = app::current_theme(cx);
|
||||||
|
|
||||||
|
let mut children: Vec<gpui::AnyElement> = Vec::with_capacity(self.items.len() * 2);
|
||||||
|
for (i, item) in self.items.into_iter().enumerate() {
|
||||||
|
let is_selected = Some(item.value) == self.selected_value;
|
||||||
|
let cb = self.on_select.as_ref().map(Rc::clone);
|
||||||
|
|
||||||
|
// the segmented tab button
|
||||||
|
children.push(
|
||||||
|
div()
|
||||||
|
.id(i)
|
||||||
|
.px_2()
|
||||||
|
.py_1()
|
||||||
|
.bg(theme.colors.surface_button)
|
||||||
|
.child(
|
||||||
|
font_icon(item.icon)
|
||||||
|
.size_3p5()
|
||||||
|
.when(is_selected, |it| it.text_color(theme.colors.accent_solid)),
|
||||||
|
)
|
||||||
|
.on_click(move |_, window, cx| {
|
||||||
|
if let Some(f) = &cb {
|
||||||
|
f(&item.value, window, cx);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_any_element(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// divider next to the segmented tab
|
||||||
|
children.push(
|
||||||
|
div()
|
||||||
|
.h_full()
|
||||||
|
.w_px()
|
||||||
|
.bg(theme.colors.border_strong)
|
||||||
|
.into_any_element(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.truncate(children.len() - 1);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.colors.border_strong)
|
||||||
|
.rounded_sm()
|
||||||
|
.children(children)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/query.rs
46
src/query.rs
@@ -137,13 +137,13 @@ where
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
match wait_state {
|
match wait_state {
|
||||||
| WaitState::Cached => {
|
| WaitState::Cached => {
|
||||||
return Ok(ent);
|
return Ok(ent);
|
||||||
}
|
}
|
||||||
| WaitState::Waiting { rx, sub } => {
|
| WaitState::Waiting { rx, sub } => {
|
||||||
_ = sub;
|
_ = sub;
|
||||||
_ = rx.await;
|
_ = rx.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,13 +181,17 @@ where
|
|||||||
let state = query.raw.read(cx);
|
let state = query.raw.read(cx);
|
||||||
|
|
||||||
match &state.data {
|
match &state.data {
|
||||||
| QueryData::Loading | QueryData::Pending | QueryData::Stale => QueryStatus::Loading,
|
| QueryData::Loading | QueryData::Pending | QueryData::Stale => QueryStatus::Loading,
|
||||||
| QueryData::Some(data) => QueryStatus::Loaded(data.downcast_ref::<F::Data>().unwrap()),
|
| QueryData::Some(data) => QueryStatus::Loaded(data.downcast_ref::<F::Data>().unwrap()),
|
||||||
| QueryData::Err(error) => QueryStatus::Err(error.downcast_ref::<F::Error>().unwrap()),
|
| QueryData::Err(error) => QueryStatus::Err(error.downcast_ref::<F::Error>().unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn watch_query<E, F, H>(query: &Entity<F>, on_notify: H, cx: &mut gpui::Context<E>) -> gpui::Subscription
|
pub fn watch_query<E, F, H>(
|
||||||
|
query: &Entity<F>,
|
||||||
|
on_notify: H,
|
||||||
|
cx: &mut gpui::Context<E>,
|
||||||
|
) -> gpui::Subscription
|
||||||
where
|
where
|
||||||
E: 'static,
|
E: 'static,
|
||||||
F: QueryFn,
|
F: QueryFn,
|
||||||
@@ -308,14 +312,14 @@ where
|
|||||||
|
|
||||||
entity.raw.update(cx, |state, cx| {
|
entity.raw.update(cx, |state, cx| {
|
||||||
state.data = match result {
|
state.data = match result {
|
||||||
| Ok(data) => {
|
| Ok(data) => {
|
||||||
println!("[query] OK {}", q.key());
|
println!("[query] OK {}", q.key());
|
||||||
QueryData::Some(Box::new(data))
|
QueryData::Some(Box::new(data))
|
||||||
}
|
}
|
||||||
| Err(err) => {
|
| Err(err) => {
|
||||||
println!("[query] ERR {:?}: {:?}", q.key(), err);
|
println!("[query] ERR {:?}: {:?}", q.key(), err);
|
||||||
QueryData::Err(Box::new(err))
|
QueryData::Err(Box::new(err))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})?;
|
})?;
|
||||||
@@ -341,8 +345,8 @@ where
|
|||||||
.raw
|
.raw
|
||||||
.update(cx, |query, cx| {
|
.update(cx, |query, cx| {
|
||||||
query.data = match result {
|
query.data = match result {
|
||||||
| Ok(data) => QueryData::Some(Box::new(data)),
|
| Ok(data) => QueryData::Some(Box::new(data)),
|
||||||
| Err(err) => QueryData::Err(Box::new(err)),
|
| Err(err) => QueryData::Err(Box::new(err)),
|
||||||
};
|
};
|
||||||
cx.notify();
|
cx.notify();
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -157,8 +157,8 @@ impl gpui::RenderOnce for IssueListItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let repo_name_text = match self.repo_name {
|
let repo_name_text = match self.repo_name {
|
||||||
| Some(name) => text(name),
|
| Some(name) => text(name),
|
||||||
| None => text("Unknown repo"),
|
| None => text("Unknown repo"),
|
||||||
}
|
}
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.opacity(0.5);
|
.opacity(0.5);
|
||||||
@@ -171,21 +171,21 @@ impl gpui::RenderOnce for IssueListItem {
|
|||||||
.bg(theme.colors.surface)
|
.bg(theme.colors.surface)
|
||||||
} else {
|
} else {
|
||||||
match self.status {
|
match self.status {
|
||||||
| api::issues::PullRequestState::Closed => pill(
|
| api::issues::PullRequestState::Closed => pill(
|
||||||
text("Closed").text_color(theme.colors.danger_on_solid),
|
text("Closed").text_color(theme.colors.danger_on_solid),
|
||||||
font_icon(FontIcon::PullRequestClosed).text_color(theme.colors.danger_on_solid),
|
font_icon(FontIcon::PullRequestClosed).text_color(theme.colors.danger_on_solid),
|
||||||
)
|
)
|
||||||
.bg(theme.colors.danger_solid),
|
.bg(theme.colors.danger_solid),
|
||||||
| api::issues::PullRequestState::Merged => pill(
|
| api::issues::PullRequestState::Merged => pill(
|
||||||
text("Merged").text_color(theme.colors.accent_on_solid),
|
text("Merged").text_color(theme.colors.accent_on_solid),
|
||||||
font_icon(FontIcon::PullRequestClosed).text_color(theme.colors.accent_on_solid),
|
font_icon(FontIcon::PullRequestClosed).text_color(theme.colors.accent_on_solid),
|
||||||
)
|
)
|
||||||
.bg(theme.colors.accent_solid),
|
.bg(theme.colors.accent_solid),
|
||||||
| _ => pill(
|
| _ => pill(
|
||||||
text("Open").text_color(theme.colors.success_on_solid),
|
text("Open").text_color(theme.colors.success_on_solid),
|
||||||
font_icon(FontIcon::PullRequestArrow).text_color(theme.colors.success_on_solid),
|
font_icon(FontIcon::PullRequestArrow).text_color(theme.colors.success_on_solid),
|
||||||
)
|
)
|
||||||
.bg(theme.colors.success_solid),
|
.bg(theme.colors.success_solid),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -117,8 +117,8 @@ impl PullRequestDiffView {
|
|||||||
) {
|
) {
|
||||||
if let Some(diff) = {
|
if let Some(diff) = {
|
||||||
match read_query(query, cx) {
|
match read_query(query, cx) {
|
||||||
| QueryStatus::Loaded(diff) => Some(Arc::clone(diff)),
|
| QueryStatus::Loaded(diff) => Some(Arc::clone(diff)),
|
||||||
| _ => None,
|
| _ => None,
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
self.load_diff_view(diff, cx);
|
self.load_diff_view(diff, cx);
|
||||||
@@ -153,16 +153,16 @@ impl PullRequestDiffView {
|
|||||||
|
|
||||||
_ = cx
|
_ = cx
|
||||||
.spawn(async move |weak, cx| match tokio::join!(t1, t2) {
|
.spawn(async move |weak, cx| match tokio::join!(t1, t2) {
|
||||||
| (Some(old_side_highlights), Some(new_side_highlights)) => {
|
| (Some(old_side_highlights), Some(new_side_highlights)) => {
|
||||||
_ = weak.update(cx, |this, cx| {
|
_ = weak.update(cx, |this, cx| {
|
||||||
this.diff_view_state
|
this.diff_view_state
|
||||||
.set_old_side_highlights(old_side_highlights);
|
.set_old_side_highlights(old_side_highlights);
|
||||||
this.diff_view_state
|
this.diff_view_state
|
||||||
.set_new_side_highlights(new_side_highlights);
|
.set_new_side_highlights(new_side_highlights);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
| _ => {}
|
| _ => {}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
@@ -184,18 +184,18 @@ impl gpui::Render for PullRequestDiffView {
|
|||||||
.unwrap_or(QueryStatus::Loading);
|
.unwrap_or(QueryStatus::Loading);
|
||||||
|
|
||||||
match content_diff {
|
match content_diff {
|
||||||
| QueryStatus::Err(_) | QueryStatus::Loading => div()
|
| QueryStatus::Err(_) | QueryStatus::Loading => div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(theme.colors.surface)
|
.bg(theme.colors.surface)
|
||||||
.p_4()
|
.p_4()
|
||||||
.child(text("asd")),
|
.child(text("asd")),
|
||||||
|
|
||||||
| QueryStatus::Loaded(_) => match &self.diff_view_content {
|
| QueryStatus::Loaded(_) => match &self.diff_view_content {
|
||||||
| Some(content) => div()
|
| Some(content) => div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.child(diff_view(self.diff_view_state.clone(), content.clone())),
|
.child(diff_view(self.diff_view_state.clone(), content.clone())),
|
||||||
| None => div(),
|
| None => div(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AppContext, InteractiveElement, IntoElement, ParentElement, StatefulInteractiveElement, Styled,
|
AppContext, InteractiveElement, IntoElement, ParentElement, StatefulInteractiveElement, Styled,
|
||||||
div, img, linear_gradient, prelude::FluentBuilder,
|
div, img, prelude::FluentBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -12,6 +12,7 @@ use crate::{
|
|||||||
button::{self, Button, button},
|
button::{self, Button, button},
|
||||||
font_icon::{FontIcon, font_icon},
|
font_icon::{FontIcon, font_icon},
|
||||||
markdown::{self, MarkdownText},
|
markdown::{self, MarkdownText},
|
||||||
|
segmented_control::segmented_control,
|
||||||
text::text,
|
text::text,
|
||||||
},
|
},
|
||||||
query::{self, QueryStatus, read_query, use_query, watch_query},
|
query::{self, QueryStatus, read_query, use_query, watch_query},
|
||||||
@@ -19,17 +20,23 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) struct PullRequestView {
|
pub(crate) struct PullRequestView {
|
||||||
|
current_tab: Tab,
|
||||||
|
|
||||||
markdown_viewer: Option<gpui::Entity<MarkdownText>>,
|
markdown_viewer: Option<gpui::Entity<MarkdownText>>,
|
||||||
diff_view: Option<gpui::Entity<PullRequestDiffView>>,
|
diff_view: Option<gpui::Entity<PullRequestDiffView>>,
|
||||||
|
|
||||||
pull_request_query: Option<query::Entity<api::issues::FetchPullRequest>>,
|
pull_request_query: Option<query::Entity<api::issues::FetchPullRequest>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(gpui::IntoElement)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
struct Toolbar {}
|
enum Tab {
|
||||||
|
PullRequestBody,
|
||||||
|
DiffView,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(_cx: &mut gpui::Context<PullRequestView>) -> PullRequestView {
|
pub fn new(_cx: &mut gpui::Context<PullRequestView>) -> PullRequestView {
|
||||||
PullRequestView {
|
PullRequestView {
|
||||||
|
current_tab: Tab::PullRequestBody,
|
||||||
markdown_viewer: None,
|
markdown_viewer: None,
|
||||||
diff_view: None,
|
diff_view: None,
|
||||||
pull_request_query: None,
|
pull_request_query: None,
|
||||||
@@ -45,6 +52,7 @@ impl PullRequestView {
|
|||||||
let query = use_query(api::issues::FetchPullRequest { id }, cx);
|
let query = use_query(api::issues::FetchPullRequest { id }, cx);
|
||||||
|
|
||||||
self.pull_request_query = Some(query.clone());
|
self.pull_request_query = Some(query.clone());
|
||||||
|
self.current_tab = Tab::PullRequestBody;
|
||||||
|
|
||||||
_ = watch_query(&query, Self::sync_pull_request_query, cx).detach();
|
_ = watch_query(&query, Self::sync_pull_request_query, cx).detach();
|
||||||
|
|
||||||
@@ -98,6 +106,65 @@ impl PullRequestView {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toolbar(&self, cx: &gpui::Context<Self>) -> gpui::AnyElement {
|
||||||
|
fn toolbar_button(id: impl Into<gpui::ElementId>) -> Button {
|
||||||
|
button(id)
|
||||||
|
.px_2p5()
|
||||||
|
.py_1()
|
||||||
|
.variant(button::Variant::Secondary)
|
||||||
|
.border_0()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divider() -> gpui::Div {
|
||||||
|
div().h_full().w_px()
|
||||||
|
}
|
||||||
|
|
||||||
|
let theme = app::current_theme(cx);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.w_full()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.items_center()
|
||||||
|
.justify_start()
|
||||||
|
.p_1()
|
||||||
|
.bg(theme.colors.surface)
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(theme.colors.border_muted)
|
||||||
|
.child(
|
||||||
|
segmented_control()
|
||||||
|
.selected_value(self.current_tab)
|
||||||
|
.item(Tab::PullRequestBody, FontIcon::MessageCircleMore)
|
||||||
|
.item(Tab::DiffView, FontIcon::FileBracesCorner)
|
||||||
|
.on_select(cx.listener(|this, tab, _, cx| {
|
||||||
|
this.current_tab = *tab;
|
||||||
|
cx.notify();
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.child(div().flex_1())
|
||||||
|
.child(
|
||||||
|
toolbar_button("pr-close-btn")
|
||||||
|
.leading(font_icon(FontIcon::PullRequestClosed))
|
||||||
|
.mr_1(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
toolbar_button("pr-merge-btn")
|
||||||
|
.variant(button::Variant::Primary)
|
||||||
|
.leading(font_icon(FontIcon::GitMerge))
|
||||||
|
.rounded_r_none(),
|
||||||
|
)
|
||||||
|
.child(divider())
|
||||||
|
.child(
|
||||||
|
toolbar_button("chevron")
|
||||||
|
.py_1()
|
||||||
|
.px_0p5()
|
||||||
|
.variant(button::Variant::Primary)
|
||||||
|
.leading(font_icon(FontIcon::ChevronDown))
|
||||||
|
.rounded_l_none(),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
fn pr_content(
|
fn pr_content(
|
||||||
&self,
|
&self,
|
||||||
pr: &api::issues::DetailedPullRequest,
|
pr: &api::issues::DetailedPullRequest,
|
||||||
@@ -114,41 +181,41 @@ impl PullRequestView {
|
|||||||
.rounded_full();
|
.rounded_full();
|
||||||
|
|
||||||
match pr.state {
|
match pr.state {
|
||||||
| api::issues::PullRequestState::Open => {
|
| api::issues::PullRequestState::Open => {
|
||||||
status_pill = status_pill
|
status_pill = status_pill
|
||||||
.bg(theme.colors.success_solid)
|
.bg(theme.colors.success_solid)
|
||||||
.child(
|
.child(
|
||||||
font_icon(FontIcon::PullRequestArrow)
|
font_icon(FontIcon::PullRequestArrow)
|
||||||
.size_3()
|
.size_3()
|
||||||
.text_color(theme.colors.success_on_solid),
|
.text_color(theme.colors.success_on_solid),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
text("Open")
|
text("Open")
|
||||||
.text_color(theme.colors.success_on_solid)
|
.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(),
|
.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| {
|
let merge_text = pr.author.as_ref().map(|author| {
|
||||||
@@ -228,7 +295,6 @@ impl PullRequestView {
|
|||||||
.flex_col()
|
.flex_col()
|
||||||
.bg(theme.colors.surface)
|
.bg(theme.colors.surface)
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(Toolbar {})
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -282,9 +348,18 @@ impl gpui::Render for PullRequestView {
|
|||||||
_window: &mut gpui::Window,
|
_window: &mut gpui::Window,
|
||||||
cx: &mut gpui::Context<Self>,
|
cx: &mut gpui::Context<Self>,
|
||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
div().size_full().child(match &self.pull_request_query {
|
div()
|
||||||
| Some(q) => match read_query(q, cx) {
|
.size_full()
|
||||||
| QueryStatus::Loaded(pr) => self.pr_content(pr, cx),
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(self.toolbar(cx))
|
||||||
|
.child(match &self.pull_request_query {
|
||||||
|
| Some(q) => {
|
||||||
|
match read_query(q, cx) {
|
||||||
|
| QueryStatus::Loaded(pr) => match (&self.diff_view, self.current_tab) {
|
||||||
|
| (Some(diff_view), Tab::DiffView) => diff_view.clone().into_any_element(),
|
||||||
|
| _ => self.pr_content(pr, cx),
|
||||||
|
},
|
||||||
|
|
||||||
| QueryStatus::Err(e) => div()
|
| QueryStatus::Err(e) => div()
|
||||||
.size_full()
|
.size_full()
|
||||||
@@ -294,83 +369,10 @@ impl gpui::Render for PullRequestView {
|
|||||||
.size_full()
|
.size_full()
|
||||||
.child("loading pr content")
|
.child("loading pr content")
|
||||||
.into_any_element(),
|
.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(),
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl gpui::RenderOnce for Toolbar {
|
|
||||||
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
|
|
||||||
fn toolbar_button(id: impl Into<gpui::ElementId>) -> Button {
|
|
||||||
button(id)
|
|
||||||
.px_2p5()
|
|
||||||
.py_1()
|
|
||||||
.variant(button::Variant::Secondary)
|
|
||||||
.border_0()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn divider() -> gpui::Div {
|
|
||||||
div().h_full().w_px()
|
|
||||||
}
|
|
||||||
|
|
||||||
let theme = app::current_theme(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.items_center()
|
|
||||||
.justify_start()
|
|
||||||
.p_1()
|
|
||||||
.bg(theme.colors.surface)
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(theme.colors.border_muted)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_row()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.border_t_1()
|
|
||||||
.border_color(theme.colors.border_strong)
|
|
||||||
.rounded_sm()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.py_1()
|
|
||||||
.bg(theme.colors.surface_elevated)
|
|
||||||
.child(font_icon(FontIcon::MessageCircleMore).size_3p5()),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.px_2()
|
|
||||||
.py_1()
|
|
||||||
.bg(theme.colors.surface_elevated)
|
|
||||||
.child(font_icon(FontIcon::FileBracesCorner).size_3p5()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(div().flex_1())
|
|
||||||
.child(
|
|
||||||
toolbar_button("pr-close-btn")
|
|
||||||
.leading(font_icon(FontIcon::PullRequestClosed))
|
|
||||||
.mr_1(),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
toolbar_button("pr-merge-btn")
|
|
||||||
.variant(button::Variant::Primary)
|
|
||||||
.leading(font_icon(FontIcon::GitMerge))
|
|
||||||
.rounded_r_none(),
|
|
||||||
)
|
|
||||||
.child(divider())
|
|
||||||
.child(
|
|
||||||
toolbar_button("chevron")
|
|
||||||
.py_1()
|
|
||||||
.px_0p5()
|
|
||||||
.variant(button::Variant::Primary)
|
|
||||||
.leading(font_icon(FontIcon::ChevronDown))
|
|
||||||
.rounded_l_none(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
mod catppuccin;
|
mod catppuccin;
|
||||||
pub(crate) mod syntax;
|
pub(crate) mod syntax;
|
||||||
|
|
||||||
use gpui::Rgba;
|
use gpui::{Background, Rgba};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use syntax::{HIGHLIGHT_NAMES, ThemeSyntax, ThemeSyntaxHighlight};
|
pub use syntax::{HIGHLIGHT_NAMES, ThemeSyntax, ThemeSyntaxHighlight};
|
||||||
@@ -33,6 +33,7 @@ pub struct ThemeColors {
|
|||||||
pub background: Rgba,
|
pub background: Rgba,
|
||||||
pub surface: Rgba,
|
pub surface: Rgba,
|
||||||
pub surface_elevated: Rgba,
|
pub surface_elevated: Rgba,
|
||||||
|
pub surface_button: Background,
|
||||||
pub surface_chrome: Rgba,
|
pub surface_chrome: Rgba,
|
||||||
pub surface_hover: Rgba,
|
pub surface_hover: Rgba,
|
||||||
pub surface_active: Rgba,
|
pub surface_active: Rgba,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::colors::{hex, hex_alpha};
|
use crate::colors::{hex, hex_alpha};
|
||||||
|
use gpui::{linear_color_stop, linear_gradient};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Theme, ThemeColors, ThemeMode, ThemeSyntaxHighlight,
|
Theme, ThemeColors, ThemeMode, ThemeSyntaxHighlight,
|
||||||
@@ -44,6 +45,11 @@ pub(crate) fn latte() -> Theme {
|
|||||||
background: hex(0xeff1f5),
|
background: hex(0xeff1f5),
|
||||||
surface: hex(0xe6e9ef),
|
surface: hex(0xe6e9ef),
|
||||||
surface_elevated: hex(0xeff1f5),
|
surface_elevated: hex(0xeff1f5),
|
||||||
|
surface_button: linear_gradient(
|
||||||
|
180.,
|
||||||
|
linear_color_stop(hex(0xf6f7fb), 0.),
|
||||||
|
linear_color_stop(hex(0xeff1f5), 1.),
|
||||||
|
),
|
||||||
surface_chrome: hex(0xdce0e8),
|
surface_chrome: hex(0xdce0e8),
|
||||||
surface_hover: hex(0xdce0e8),
|
surface_hover: hex(0xdce0e8),
|
||||||
surface_active: hex(0xccd0da),
|
surface_active: hex(0xccd0da),
|
||||||
@@ -153,6 +159,11 @@ pub(crate) fn mocha() -> Theme {
|
|||||||
background: hex(0x1e1e2e),
|
background: hex(0x1e1e2e),
|
||||||
surface: hex(0x181825),
|
surface: hex(0x181825),
|
||||||
surface_elevated: hex(0x45475a),
|
surface_elevated: hex(0x45475a),
|
||||||
|
surface_button: linear_gradient(
|
||||||
|
180.,
|
||||||
|
linear_color_stop(hex(0x4f5068), 0.),
|
||||||
|
linear_color_stop(hex(0x45475a), 1.),
|
||||||
|
),
|
||||||
surface_chrome: hex(0x11111b),
|
surface_chrome: hex(0x11111b),
|
||||||
surface_hover: hex(0x313244),
|
surface_hover: hex(0x313244),
|
||||||
surface_active: hex(0x45475a),
|
surface_active: hex(0x45475a),
|
||||||
|
|||||||
224
src/util/diff.rs
224
src/util/diff.rs
@@ -44,117 +44,127 @@ pub(crate) fn diff_content(
|
|||||||
|
|
||||||
for op in diff.ops() {
|
for op in diff.ops() {
|
||||||
match op {
|
match op {
|
||||||
| &similar::DiffOp::Equal {
|
| &similar::DiffOp::Equal {
|
||||||
old_index,
|
old_index,
|
||||||
new_index,
|
new_index,
|
||||||
len,
|
len,
|
||||||
} => {
|
} => {
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
let old_line = old_index + i;
|
let old_line = old_index + i;
|
||||||
let new_line = new_index + i;
|
let new_line = new_index + i;
|
||||||
let old_line_range = &old_line_ranges[old_line];
|
let old_line_range = &old_line_ranges[old_line];
|
||||||
let content = Arc::from(old_content.slice(old_line_range.clone()).as_str()?);
|
let content = Arc::from(old_content.slice(old_line_range.clone()).as_str()?);
|
||||||
diff_lines.push(DiffLine {
|
diff_lines.push(DiffLine {
|
||||||
op: Op::Equal,
|
op: Op::Equal,
|
||||||
old_line: Some(old_line),
|
old_line: Some(old_line),
|
||||||
old_content: Some(Arc::clone(&content)),
|
old_content: Some(Arc::clone(&content)),
|
||||||
old_byte_range: old_line_range.clone(),
|
old_byte_range: old_line_range.clone(),
|
||||||
new_line: Some(new_line),
|
new_line: Some(new_line),
|
||||||
new_content: Some(content),
|
new_content: Some(content),
|
||||||
new_byte_range: new_line_ranges[new_line].clone(),
|
new_byte_range: new_line_ranges[new_line].clone(),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
| &similar::DiffOp::Insert {
|
|
||||||
new_index, new_len, ..
|
|
||||||
} => {
|
|
||||||
for i in 0..new_len {
|
|
||||||
let new_line_range = &new_line_ranges[new_index + i];
|
|
||||||
let content = Arc::from(new_content.slice(new_line_range.clone()).as_str()?);
|
|
||||||
diff_lines.push(DiffLine {
|
|
||||||
op: Op::Insert,
|
|
||||||
old_line: None,
|
|
||||||
old_content: None,
|
|
||||||
old_byte_range: 0..0,
|
|
||||||
new_line: Some(new_index + i),
|
|
||||||
new_content: Some(content),
|
|
||||||
new_byte_range: new_line_range.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
| &similar::DiffOp::Replace {
|
|
||||||
old_index,
|
|
||||||
old_len,
|
|
||||||
new_index,
|
|
||||||
new_len,
|
|
||||||
} => {
|
|
||||||
for i in 0..new_len.max(old_len) {
|
|
||||||
let old_line = old_index + i;
|
|
||||||
let new_line = new_index + i;
|
|
||||||
|
|
||||||
let diff_line = match (old_line_ranges.get(old_line), new_line_ranges.get(new_line))
|
|
||||||
{
|
|
||||||
| (Some(old_range), Some(new_range)) => DiffLine {
|
|
||||||
op: Op::Replace,
|
|
||||||
old_line: Some(old_line),
|
|
||||||
old_content: Some(Arc::from(old_content.slice(old_range.clone()).as_str()?)),
|
|
||||||
old_byte_range: old_range.clone(),
|
|
||||||
new_line: Some(new_line),
|
|
||||||
new_content: Some(Arc::from(new_content.slice(new_range.clone()).as_str()?)),
|
|
||||||
new_byte_range: new_range.clone(),
|
|
||||||
},
|
|
||||||
|
|
||||||
| (None, Some(new_range)) => DiffLine {
|
|
||||||
op: Op::Replace,
|
|
||||||
old_line: None,
|
|
||||||
old_content: None,
|
|
||||||
old_byte_range: 0..0,
|
|
||||||
new_line: Some(new_index + i),
|
|
||||||
new_content: Some(Arc::from(new_content.slice(new_range.clone()).as_str()?)),
|
|
||||||
new_byte_range: new_range.clone(),
|
|
||||||
},
|
|
||||||
|
|
||||||
| (Some(old_range), None) => DiffLine {
|
|
||||||
op: Op::Replace,
|
|
||||||
old_line: Some(old_index + i),
|
|
||||||
old_content: Some(Arc::from(old_content.slice(old_range.clone()).as_str()?)),
|
|
||||||
old_byte_range: old_range.clone(),
|
|
||||||
new_line: None,
|
|
||||||
new_content: None,
|
|
||||||
new_byte_range: 0..0,
|
|
||||||
},
|
|
||||||
|
|
||||||
| (None, None) => {
|
|
||||||
// unlickly to happen, but if it does, idk
|
|
||||||
panic!(
|
|
||||||
"the unlikely happened: both old & new index of DiffOps::Replace don't point to any line in the parsed line ranges."
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
diff_lines.push(diff_line);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
| &similar::DiffOp::Delete {
|
| &similar::DiffOp::Insert {
|
||||||
old_index, old_len, ..
|
new_index, new_len, ..
|
||||||
} => {
|
} => {
|
||||||
for i in 0..old_len {
|
for i in 0..new_len {
|
||||||
let old_line_range = &old_line_ranges[old_index];
|
let new_line_range = &new_line_ranges[new_index + i];
|
||||||
let content = Arc::from(old_content.slice(old_line_range.clone()).as_str()?);
|
let content = Arc::from(new_content.slice(new_line_range.clone()).as_str()?);
|
||||||
diff_lines.push(DiffLine {
|
diff_lines.push(DiffLine {
|
||||||
op: Op::Delete,
|
op: Op::Insert,
|
||||||
old_line: Some(old_index + i),
|
old_line: None,
|
||||||
old_content: Some(content),
|
old_content: None,
|
||||||
old_byte_range: old_line_range.clone(),
|
old_byte_range: 0..0,
|
||||||
new_line: None,
|
new_line: Some(new_index + i),
|
||||||
new_content: None,
|
new_content: Some(content),
|
||||||
new_byte_range: 0..0,
|
new_byte_range: new_line_range.clone(),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
| &similar::DiffOp::Replace {
|
||||||
|
old_index,
|
||||||
|
old_len,
|
||||||
|
new_index,
|
||||||
|
new_len,
|
||||||
|
} => {
|
||||||
|
for i in 0..new_len.max(old_len) {
|
||||||
|
let old_line = old_index + i;
|
||||||
|
let new_line = new_index + i;
|
||||||
|
|
||||||
|
let diff_line = match (
|
||||||
|
old_line_ranges.get(old_line),
|
||||||
|
new_line_ranges.get(new_line),
|
||||||
|
) {
|
||||||
|
| (Some(old_range), Some(new_range)) => DiffLine {
|
||||||
|
op: Op::Replace,
|
||||||
|
old_line: Some(old_line),
|
||||||
|
old_content: Some(Arc::from(
|
||||||
|
old_content.slice(old_range.clone()).as_str()?,
|
||||||
|
)),
|
||||||
|
old_byte_range: old_range.clone(),
|
||||||
|
new_line: Some(new_line),
|
||||||
|
new_content: Some(Arc::from(
|
||||||
|
new_content.slice(new_range.clone()).as_str()?,
|
||||||
|
)),
|
||||||
|
new_byte_range: new_range.clone(),
|
||||||
|
},
|
||||||
|
|
||||||
|
| (None, Some(new_range)) => DiffLine {
|
||||||
|
op: Op::Replace,
|
||||||
|
old_line: None,
|
||||||
|
old_content: None,
|
||||||
|
old_byte_range: 0..0,
|
||||||
|
new_line: Some(new_index + i),
|
||||||
|
new_content: Some(Arc::from(
|
||||||
|
new_content.slice(new_range.clone()).as_str()?,
|
||||||
|
)),
|
||||||
|
new_byte_range: new_range.clone(),
|
||||||
|
},
|
||||||
|
|
||||||
|
| (Some(old_range), None) => DiffLine {
|
||||||
|
op: Op::Replace,
|
||||||
|
old_line: Some(old_index + i),
|
||||||
|
old_content: Some(Arc::from(
|
||||||
|
old_content.slice(old_range.clone()).as_str()?,
|
||||||
|
)),
|
||||||
|
old_byte_range: old_range.clone(),
|
||||||
|
new_line: None,
|
||||||
|
new_content: None,
|
||||||
|
new_byte_range: 0..0,
|
||||||
|
},
|
||||||
|
|
||||||
|
| (None, None) => {
|
||||||
|
// unlickly to happen, but if it does, idk
|
||||||
|
panic!(
|
||||||
|
"the unlikely happened: both old & new index of DiffOps::Replace don't point to any line in the parsed line ranges."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
diff_lines.push(diff_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
| &similar::DiffOp::Delete {
|
||||||
|
old_index, old_len, ..
|
||||||
|
} => {
|
||||||
|
for i in 0..old_len {
|
||||||
|
let old_line_range = &old_line_ranges[old_index];
|
||||||
|
let content = Arc::from(old_content.slice(old_line_range.clone()).as_str()?);
|
||||||
|
diff_lines.push(DiffLine {
|
||||||
|
op: Op::Delete,
|
||||||
|
old_line: Some(old_index + i),
|
||||||
|
old_content: Some(content),
|
||||||
|
old_byte_range: old_line_range.clone(),
|
||||||
|
new_line: None,
|
||||||
|
new_content: None,
|
||||||
|
new_byte_range: 0..0,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ pub(crate) fn classify_content(content: &[u8]) -> ContentType {
|
|||||||
ContentType::Text
|
ContentType::Text
|
||||||
} else {
|
} else {
|
||||||
match memchr(0, &content[..content.len().min(8192)]) {
|
match memchr(0, &content[..content.len().min(8192)]) {
|
||||||
| None => ContentType::Text,
|
| None => ContentType::Text,
|
||||||
| Some(_) => ContentType::Binary,
|
| Some(_) => ContentType::Binary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn file_type_from_path(path: &str) -> FileType {
|
pub(crate) fn file_type_from_path(path: &str) -> FileType {
|
||||||
match Path::new(path).extension().map(|it| it.to_str()).flatten() {
|
match Path::new(path).extension().map(|it| it.to_str()).flatten() {
|
||||||
| Some("rs") => FileType::Rust,
|
| Some("rs") => FileType::Rust,
|
||||||
| Some("js") | Some("jsx") => FileType::JavaScript,
|
| Some("js") | Some("jsx") => FileType::JavaScript,
|
||||||
| _ => FileType::Unknown,
|
| _ => FileType::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,18 +55,18 @@ pub(crate) fn line_ranges(content: &[u8]) -> Vec<std::ops::Range<usize>> {
|
|||||||
let c = content[i];
|
let c = content[i];
|
||||||
|
|
||||||
match (c, content.get(i + 1)) {
|
match (c, content.get(i + 1)) {
|
||||||
| (b'\r', Some(b'\n')) => {
|
| (b'\r', Some(b'\n')) => {
|
||||||
// if \r found, check if its \r\n or if its a lone \r
|
// if \r found, check if its \r\n or if its a lone \r
|
||||||
// if \r\n, then treat as one line break
|
// if \r\n, then treat as one line break
|
||||||
ranges.push(line_start..i + 1);
|
ranges.push(line_start..i + 1);
|
||||||
// because we already counted the \n byte, the next iter into it needs to be skipped
|
// because we already counted the \n byte, the next iter into it needs to be skipped
|
||||||
skip_next = true;
|
skip_next = true;
|
||||||
line_start = i + 2;
|
line_start = i + 2;
|
||||||
}
|
}
|
||||||
| _ => {
|
| _ => {
|
||||||
ranges.push(line_start..i);
|
ranges.push(line_start..i);
|
||||||
line_start = i + 1;
|
line_start = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ fn ts_highlight_configuration_for_file_type(
|
|||||||
file_type: util::file::FileType,
|
file_type: util::file::FileType,
|
||||||
) -> Option<tree_sitter_highlight::HighlightConfiguration> {
|
) -> Option<tree_sitter_highlight::HighlightConfiguration> {
|
||||||
match file_type {
|
match file_type {
|
||||||
| util::file::FileType::Rust => tree_sitter_highlight::HighlightConfiguration::new(
|
| util::file::FileType::Rust => tree_sitter_highlight::HighlightConfiguration::new(
|
||||||
tree_sitter_rust::LANGUAGE.into(),
|
tree_sitter_rust::LANGUAGE.into(),
|
||||||
"rust",
|
"rust",
|
||||||
tree_sitter_rust::HIGHLIGHTS_QUERY,
|
tree_sitter_rust::HIGHLIGHTS_QUERY,
|
||||||
tree_sitter_rust::INJECTIONS_QUERY,
|
tree_sitter_rust::INJECTIONS_QUERY,
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
.ok(),
|
.ok(),
|
||||||
|
|
||||||
| _ => None,
|
| _ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,37 +44,37 @@ pub(crate) fn highlight_content(
|
|||||||
|
|
||||||
for highlight_event in events {
|
for highlight_event in events {
|
||||||
match highlight_event.ok()? {
|
match highlight_event.ok()? {
|
||||||
| tree_sitter_highlight::HighlightEvent::HighlightStart(h) => {
|
| tree_sitter_highlight::HighlightEvent::HighlightStart(h) => {
|
||||||
highlight = theme_syntax.as_slice()[h.0];
|
highlight = theme_syntax.as_slice()[h.0];
|
||||||
}
|
|
||||||
| tree_sitter_highlight::HighlightEvent::Source { start, end } => {
|
|
||||||
while current_line < line_ranges.len() && start >= line_ranges[current_line].end {
|
|
||||||
highlights.push(Vec::new());
|
|
||||||
current_line += 1;
|
|
||||||
}
|
}
|
||||||
let mut line = current_line;
|
| tree_sitter_highlight::HighlightEvent::Source { start, end } => {
|
||||||
while line < line_ranges.len() && end > line_ranges[line].start {
|
while current_line < line_ranges.len() && start >= line_ranges[current_line].end {
|
||||||
if highlights.get(line).is_none() {
|
|
||||||
highlights.push(Vec::new());
|
highlights.push(Vec::new());
|
||||||
|
current_line += 1;
|
||||||
|
}
|
||||||
|
let mut line = current_line;
|
||||||
|
while line < line_ranges.len() && end > line_ranges[line].start {
|
||||||
|
if highlights.get(line).is_none() {
|
||||||
|
highlights.push(Vec::new());
|
||||||
|
}
|
||||||
|
let line_range = &line_ranges[line];
|
||||||
|
let highlight_start = start.max(line_range.start);
|
||||||
|
let highlight_end = end.min(line_range.end);
|
||||||
|
highlights[line].push((
|
||||||
|
(highlight_start - line_range.start)..(highlight_end - line_range.start),
|
||||||
|
gpui::HighlightStyle {
|
||||||
|
color: Some(highlight.color.into()),
|
||||||
|
font_weight: highlight.font_weight,
|
||||||
|
font_style: Some(highlight.font_style),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
line += 1;
|
||||||
}
|
}
|
||||||
let line_range = &line_ranges[line];
|
|
||||||
let highlight_start = start.max(line_range.start);
|
|
||||||
let highlight_end = end.min(line_range.end);
|
|
||||||
highlights[line].push((
|
|
||||||
(highlight_start - line_range.start)..(highlight_end - line_range.start),
|
|
||||||
gpui::HighlightStyle {
|
|
||||||
color: Some(highlight.color.into()),
|
|
||||||
font_weight: highlight.font_weight,
|
|
||||||
font_style: Some(highlight.font_style),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
));
|
|
||||||
line += 1;
|
|
||||||
}
|
}
|
||||||
}
|
| tree_sitter_highlight::HighlightEvent::HighlightEnd => {
|
||||||
| tree_sitter_highlight::HighlightEvent::HighlightEnd => {
|
highlight = default_highlight;
|
||||||
highlight = default_highlight;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user