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}