buffer.rs

  1use std::marker::PhantomData;
  2
  3use gpui2::{Hsla, WindowContext};
  4
  5use crate::prelude::*;
  6use crate::{h_stack, theme, v_stack, Icon, IconElement};
  7
  8#[derive(Default, PartialEq, Copy, Clone)]
  9pub struct PlayerCursor {
 10    color: Hsla,
 11    index: usize,
 12}
 13
 14#[derive(Default, PartialEq, Clone)]
 15pub struct HighlightedText {
 16    pub text: String,
 17    pub color: Hsla,
 18}
 19
 20#[derive(Default, PartialEq, Clone)]
 21pub struct HighlightedLine {
 22    pub highlighted_texts: Vec<HighlightedText>,
 23}
 24
 25#[derive(Default, PartialEq, Clone)]
 26pub struct BufferRow {
 27    pub line_number: usize,
 28    pub code_action: bool,
 29    pub current: bool,
 30    pub line: Option<HighlightedLine>,
 31    pub cursors: Option<Vec<PlayerCursor>>,
 32    pub status: GitStatus,
 33    pub show_line_number: bool,
 34}
 35
 36pub struct BufferRows {
 37    pub show_line_numbers: bool,
 38    pub rows: Vec<BufferRow>,
 39}
 40
 41impl Default for BufferRows {
 42    fn default() -> Self {
 43        Self {
 44            show_line_numbers: true,
 45            rows: vec![BufferRow {
 46                line_number: 1,
 47                code_action: false,
 48                current: true,
 49                line: None,
 50                cursors: None,
 51                status: GitStatus::None,
 52                show_line_number: true,
 53            }],
 54        }
 55    }
 56}
 57
 58impl BufferRow {
 59    pub fn new(line_number: usize) -> Self {
 60        Self {
 61            line_number,
 62            code_action: false,
 63            current: false,
 64            line: None,
 65            cursors: None,
 66            status: GitStatus::None,
 67            show_line_number: true,
 68        }
 69    }
 70
 71    pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
 72        self.line = line;
 73        self
 74    }
 75
 76    pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
 77        self.cursors = cursors;
 78        self
 79    }
 80
 81    pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
 82        if let Some(cursors) = &mut self.cursors {
 83            cursors.push(cursor);
 84        } else {
 85            self.cursors = Some(vec![cursor]);
 86        }
 87        self
 88    }
 89
 90    pub fn set_status(mut self, status: GitStatus) -> Self {
 91        self.status = status;
 92        self
 93    }
 94
 95    pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
 96        self.show_line_number = show_line_number;
 97        self
 98    }
 99
100    pub fn set_code_action(mut self, code_action: bool) -> Self {
101        self.code_action = code_action;
102        self
103    }
104
105    pub fn set_current(mut self, current: bool) -> Self {
106        self.current = current;
107        self
108    }
109}
110
111#[derive(Element)]
112pub struct Buffer<V: 'static> {
113    view_type: PhantomData<V>,
114    scroll_state: ScrollState,
115    rows: Option<BufferRows>,
116    readonly: bool,
117    language: Option<String>,
118    title: Option<String>,
119    path: Option<String>,
120}
121
122impl<V: 'static> Buffer<V> {
123    pub fn new() -> Self {
124        Self {
125            view_type: PhantomData,
126            scroll_state: ScrollState::default(),
127            rows: Some(BufferRows::default()),
128            readonly: false,
129            language: None,
130            title: Some("untitled".to_string()),
131            path: None,
132        }
133    }
134
135    pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
136        self.scroll_state = scroll_state;
137    }
138
139    pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
140        self.title = title.into();
141        self
142    }
143
144    pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
145        self.path = path.into();
146        self
147    }
148
149    pub fn set_readonly(mut self, readonly: bool) -> Self {
150        self.readonly = readonly;
151        self
152    }
153
154    pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
155        self.rows = rows.into();
156        self
157    }
158
159    pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
160        self.language = language.into();
161        self
162    }
163
164    fn render_row(row: BufferRow, cx: &WindowContext) -> impl IntoElement<V> {
165        let theme = theme(cx);
166        let system_color = SystemColor::new();
167
168        let line_background = if row.current {
169            theme.middle.base.default.background
170        } else {
171            system_color.transparent
172        };
173
174        let line_number_color = if row.current {
175            HighlightColor::Default.hsla(cx)
176        } else {
177            HighlightColor::Comment.hsla(cx)
178        };
179
180        h_stack()
181            .fill(line_background)
182            .gap_2()
183            .px_2()
184            .child(h_stack().w_4().h_full().px_1().when(row.code_action, |c| {
185                div().child(IconElement::new(Icon::Bolt))
186            }))
187            .when(row.show_line_number, |this| {
188                this.child(
189                    h_stack().justify_end().px_1().w_4().child(
190                        div()
191                            .text_color(line_number_color)
192                            .child(row.line_number.to_string()),
193                    ),
194                )
195            })
196            .child(div().mx_1().w_1().h_full().fill(row.status.hsla(cx)))
197            .children(row.line.map(|line| {
198                div()
199                    .flex()
200                    .children(line.highlighted_texts.iter().map(|highlighted_text| {
201                        div()
202                            .text_color(highlighted_text.color)
203                            .child(highlighted_text.text.clone())
204                    }))
205            }))
206    }
207
208    fn render_rows(&self, cx: &WindowContext) -> Vec<impl IntoElement<V>> {
209        match &self.rows {
210            Some(rows) => rows
211                .rows
212                .iter()
213                .map(|row| Self::render_row(row.clone(), cx))
214                .collect(),
215            None => vec![],
216        }
217    }
218
219    fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
220        let theme = theme(cx);
221        let rows = self.render_rows(cx);
222        v_stack()
223            .flex_1()
224            .w_full()
225            .h_full()
226            .fill(theme.highest.base.default.background)
227            .children(rows)
228    }
229}