use std::{num::NonZeroUsize, rc::Rc, sync::Arc}; use gpui::{ IntoElement, ParentElement, Refineable, Styled, div, list, prelude::FluentBuilder, px, rems, }; use crate::app; #[derive(gpui::IntoElement, Clone)] pub(crate) struct CodeLine { line_number: Option, content: Option, diff_marker: CodeLineMarker, gutter_width: gpui::Pixels, style: gpui::StyleRefinement, } #[derive(Clone)] pub(crate) enum CodeLineMarker { Added, Deleted, Unchanged, Placeholder, } #[derive(Clone)] pub(crate) struct CodeViewState(gpui::ListState); pub(crate) struct CodeView { state: CodeViewState, content: CodeViewContent, } pub(crate) struct CodeViewContent { lines: Rc<[CodeLine]>, } pub(crate) fn code_view(state: CodeViewState, content: CodeViewContent) -> CodeView { CodeView { state, content } } pub(crate) fn code_line( line_index: Option, content: Option, 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(), } } impl CodeViewContent { pub(crate) fn new(lines: Vec) -> Self { Self { lines: lines.into(), } } } impl CodeLine { pub(crate) fn gutter_width(mut self, width: gpui::Pixels) -> Self { self.gutter_width = width; self } } impl gpui::RenderOnce for CodeView { fn render(self, window: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement { let digits = self .content .lines .iter() .rfind(|l| l.line_number.is_some()) .map(|l| l.line_number.unwrap().to_string().len()) .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")); let gutter_width = window .text_system() .ch_advance(font_id, font_size) .unwrap_or(px(7.2)) * digits; println!("gutter width {}", gutter_width); list(self.state.0, move |i, _window, _app| { let line = self.content.lines[i].clone(); div() .flex() .flex_row() .items_start() .w_full() .child(line.gutter_width(gutter_width)) .into_any_element() }) } } 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 { fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement { let theme = app::current_theme(cx); let mut div = div() .flex() .flex_row() .font_family("Menlo") .text_color(theme.colors.text) .text_xs() .child( div() .bg(theme.colors.surface) .w(self.gutter_width + px(16.)) .text_align(gpui::TextAlign::Right) .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) }), ) .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 } }