Files
novem/src/component/code_view.rs

159 lines
4.7 KiB
Rust
Raw Normal View History

2026-05-24 16:44:10 +01:00
use std::{num::NonZeroUsize, rc::Rc, sync::Arc};
2026-05-18 22:30:46 +08:00
2026-05-24 16:44:10 +01:00
use gpui::{
IntoElement, ParentElement, Refineable, Styled, div, list, prelude::FluentBuilder, px, rems,
};
2026-05-18 22:30:46 +08:00
use crate::app;
#[derive(gpui::IntoElement, Clone)]
2026-05-24 16:44:10 +01:00
pub(crate) struct CodeLine {
line_number: Option<NonZeroUsize>,
content: Option<gpui::SharedString>,
diff_marker: CodeLineMarker,
gutter_width: gpui::Pixels,
style: gpui::StyleRefinement,
2026-05-18 22:30:46 +08:00
}
#[derive(Clone)]
2026-05-24 16:44:10 +01:00
pub(crate) enum CodeLineMarker {
2026-05-18 22:30:46 +08:00
Added,
Deleted,
Unchanged,
2026-05-24 16:44:10 +01:00
Placeholder,
2026-05-18 22:30:46 +08:00
}
#[derive(Clone)]
2026-05-24 16:44:10 +01:00
pub(crate) struct CodeViewState(gpui::ListState);
2026-05-18 22:30:46 +08:00
2026-05-24 16:44:10 +01:00
pub(crate) struct CodeView {
2026-05-18 22:30:46 +08:00
state: CodeViewState,
2026-05-24 16:44:10 +01:00
content: CodeViewContent,
}
pub(crate) struct CodeViewContent {
lines: Rc<[CodeLine]>,
}
pub(crate) fn code_view(state: CodeViewState, content: CodeViewContent) -> CodeView {
CodeView { state, content }
2026-05-18 22:30:46 +08:00
}
2026-05-24 16:44:10 +01:00
pub(crate) fn code_line(
line_index: Option<usize>,
content: Option<gpui::SharedString>,
marker: CodeLineMarker,
) -> CodeLine {
CodeLine {
line_number: line_index.map(|i| unsafe { NonZeroUsize::new_unchecked(i + 1) }),
content,
diff_marker: marker,
gutter_width: px(0.),
style: gpui::StyleRefinement::default(),
2026-05-18 22:30:46 +08:00
}
}
2026-05-24 16:44:10 +01:00
impl CodeViewContent {
pub(crate) fn new(lines: Vec<CodeLine>) -> Self {
Self {
lines: lines.into(),
}
}
2026-05-18 22:30:46 +08:00
}
2026-05-24 16:44:10 +01:00
impl CodeLine {
pub(crate) fn gutter_width(mut self, width: gpui::Pixels) -> Self {
self.gutter_width = width;
self
2026-05-18 22:30:46 +08:00
}
}
impl gpui::RenderOnce for CodeView {
fn render(self, window: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement {
let digits = self
2026-05-24 16:44:10 +01:00
.content
2026-05-18 22:30:46 +08:00
.lines
2026-05-24 16:44:10 +01:00
.iter()
.rfind(|l| l.line_number.is_some())
.map(|l| l.line_number.unwrap().to_string().len())
2026-05-18 22:30:46 +08:00
.unwrap_or(0);
let text_style = window.text_style();
let font_size = text_style.font_size.to_pixels(window.rem_size());
let font_id = window.text_system().resolve_font(&gpui::font("Menlo"));
2026-05-24 16:44:10 +01:00
let gutter_width = window
2026-05-18 22:30:46 +08:00
.text_system()
.ch_advance(font_id, font_size)
.unwrap_or(px(7.2))
* digits;
2026-05-24 16:44:10 +01:00
println!("gutter width {}", gutter_width);
2026-05-18 22:30:46 +08:00
2026-05-24 16:44:10 +01:00
list(self.state.0, move |i, _window, _app| {
let line = self.content.lines[i].clone();
2026-05-18 22:30:46 +08:00
div()
.flex()
.flex_row()
.items_start()
.w_full()
2026-05-24 16:44:10 +01:00
.child(line.gutter_width(gutter_width))
2026-05-18 22:30:46 +08:00
.into_any_element()
})
}
}
2026-05-24 16:44:10 +01:00
impl gpui::Styled for CodeLine {
#[doc = " Returns a reference to the style memory of this element."]
fn style(&mut self) -> &mut gpui::StyleRefinement {
&mut self.style
}
}
impl gpui::RenderOnce for CodeLine {
2026-05-18 22:30:46 +08:00
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
let theme = app::current_theme(cx);
2026-05-24 16:44:10 +01:00
let mut div = div()
2026-05-18 22:30:46 +08:00
.flex()
.flex_row()
.font_family("Menlo")
.text_color(theme.colors.text)
2026-05-24 16:44:10 +01:00
.text_xs()
2026-05-18 22:30:46 +08:00
.child(
div()
.bg(theme.colors.surface)
2026-05-24 16:44:10 +01:00
.w(self.gutter_width + px(16.))
2026-05-18 22:30:46 +08:00
.text_align(gpui::TextAlign::Right)
2026-05-24 16:44:10 +01:00
.px_2()
.when_some(self.line_number, |it, line_number| {
it.child(line_number.to_string())
})
.when(matches!(self.diff_marker, CodeLineMarker::Added), |it| {
it.bg(theme.colors.success_muted)
.text_color(theme.colors.success_fg)
.border_l_2()
.border_color(theme.colors.success_border)
})
.when(matches!(self.diff_marker, CodeLineMarker::Deleted), |it| {
it.bg(theme.colors.danger_muted)
.text_color(theme.colors.danger_fg)
.border_l_2()
.border_color(theme.colors.danger_border)
}),
2026-05-18 22:30:46 +08:00
)
2026-05-24 16:44:10 +01:00
.when_some(self.content, |it, content| {
it.child(div().px_2().w_full().min_w_0().child(content))
})
.when(matches!(self.diff_marker, CodeLineMarker::Added), |it| {
it.bg(theme.colors.success_muted)
})
.when(matches!(self.diff_marker, CodeLineMarker::Deleted), |it| {
it.bg(theme.colors.danger_muted)
});
div.style().refine(&self.style);
div
2026-05-18 22:30:46 +08:00
}
}