1use std::marker::PhantomData;
2
3use gpui3::{Hsla, WindowContext};
4
5use crate::prelude::*;
6use crate::{h_stack, 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
36#[derive(Clone)]
37pub struct BufferRows {
38 pub show_line_numbers: bool,
39 pub rows: Vec<BufferRow>,
40}
41
42impl Default for BufferRows {
43 fn default() -> Self {
44 Self {
45 show_line_numbers: true,
46 rows: vec![BufferRow {
47 line_number: 1,
48 code_action: false,
49 current: true,
50 line: None,
51 cursors: None,
52 status: GitStatus::None,
53 show_line_number: true,
54 }],
55 }
56 }
57}
58
59impl BufferRow {
60 pub fn new(line_number: usize) -> Self {
61 Self {
62 line_number,
63 code_action: false,
64 current: false,
65 line: None,
66 cursors: None,
67 status: GitStatus::None,
68 show_line_number: true,
69 }
70 }
71
72 pub fn set_line(mut self, line: Option<HighlightedLine>) -> Self {
73 self.line = line;
74 self
75 }
76
77 pub fn set_cursors(mut self, cursors: Option<Vec<PlayerCursor>>) -> Self {
78 self.cursors = cursors;
79 self
80 }
81
82 pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self {
83 if let Some(cursors) = &mut self.cursors {
84 cursors.push(cursor);
85 } else {
86 self.cursors = Some(vec![cursor]);
87 }
88 self
89 }
90
91 pub fn set_status(mut self, status: GitStatus) -> Self {
92 self.status = status;
93 self
94 }
95
96 pub fn set_show_line_number(mut self, show_line_number: bool) -> Self {
97 self.show_line_number = show_line_number;
98 self
99 }
100
101 pub fn set_code_action(mut self, code_action: bool) -> Self {
102 self.code_action = code_action;
103 self
104 }
105
106 pub fn set_current(mut self, current: bool) -> Self {
107 self.current = current;
108 self
109 }
110}
111
112#[derive(Element, Clone)]
113pub struct Buffer<S: 'static + Send + Sync + Clone> {
114 state_type: PhantomData<S>,
115 scroll_state: ScrollState,
116 rows: Option<BufferRows>,
117 readonly: bool,
118 language: Option<String>,
119 title: Option<String>,
120 path: Option<String>,
121}
122
123impl<S: 'static + Send + Sync + Clone> Buffer<S> {
124 pub fn new() -> Self {
125 Self {
126 state_type: PhantomData,
127 scroll_state: ScrollState::default(),
128 rows: Some(BufferRows::default()),
129 readonly: false,
130 language: None,
131 title: Some("untitled".to_string()),
132 path: None,
133 }
134 }
135
136 pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) {
137 self.scroll_state = scroll_state;
138 }
139
140 pub fn set_title<T: Into<Option<String>>>(mut self, title: T) -> Self {
141 self.title = title.into();
142 self
143 }
144
145 pub fn set_path<P: Into<Option<String>>>(mut self, path: P) -> Self {
146 self.path = path.into();
147 self
148 }
149
150 pub fn set_readonly(mut self, readonly: bool) -> Self {
151 self.readonly = readonly;
152 self
153 }
154
155 pub fn set_rows<R: Into<Option<BufferRows>>>(mut self, rows: R) -> Self {
156 self.rows = rows.into();
157 self
158 }
159
160 pub fn set_language<L: Into<Option<String>>>(mut self, language: L) -> Self {
161 self.language = language.into();
162 self
163 }
164
165 fn render_row(row: BufferRow, cx: &WindowContext) -> impl Element<ViewState = S> {
166 let system_color = SystemColor::new();
167 let color = ThemeColor::new(cx);
168
169 let line_background = if row.current {
170 color.editor_active_line
171 } else {
172 system_color.transparent
173 };
174
175 let line_number_color = if row.current {
176 color.text
177 } else {
178 color.syntax.comment
179 };
180
181 h_stack()
182 .bg(line_background)
183 .w_full()
184 .gap_2()
185 .px_1()
186 .child(
187 h_stack()
188 .w_4()
189 .h_full()
190 .px_0p5()
191 .when(row.code_action, |c| {
192 div().child(IconElement::new(Icon::Bolt))
193 }),
194 )
195 .when(row.show_line_number, |this| {
196 this.child(
197 h_stack().justify_end().px_0p5().w_3().child(
198 div()
199 .text_color(line_number_color)
200 .child(row.line_number.to_string()),
201 ),
202 )
203 })
204 .child(div().mx_0p5().w_1().h_full().bg(row.status.hsla(cx)))
205 .children(row.line.map(|line| {
206 div()
207 .flex()
208 .children(line.highlighted_texts.iter().map(|highlighted_text| {
209 div()
210 .text_color(highlighted_text.color)
211 .child(highlighted_text.text.clone())
212 }))
213 }))
214 }
215
216 fn render_rows(&self, cx: &WindowContext) -> Vec<impl Element<ViewState = S>> {
217 match &self.rows {
218 Some(rows) => rows
219 .rows
220 .iter()
221 .map(|row| Self::render_row(row.clone(), cx))
222 .collect(),
223 None => vec![],
224 }
225 }
226
227 fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
228 let color = ThemeColor::new(cx);
229 let rows = self.render_rows(cx);
230
231 v_stack()
232 .flex_1()
233 .w_full()
234 .h_full()
235 .bg(color.editor)
236 .children(rows)
237 }
238}
239
240#[cfg(feature = "stories")]
241pub use stories::*;
242
243#[cfg(feature = "stories")]
244mod stories {
245 use gpui3::rems;
246
247 use crate::{
248 empty_buffer_example, hello_world_rust_buffer_example,
249 hello_world_rust_buffer_with_status_example, Story,
250 };
251
252 use super::*;
253
254 #[derive(Element)]
255 pub struct BufferStory<S: 'static + Send + Sync + Clone> {
256 state_type: PhantomData<S>,
257 }
258
259 impl<S: 'static + Send + Sync + Clone> BufferStory<S> {
260 pub fn new() -> Self {
261 Self {
262 state_type: PhantomData,
263 }
264 }
265
266 fn render(
267 &mut self,
268 _view: &mut S,
269 cx: &mut ViewContext<S>,
270 ) -> impl Element<ViewState = S> {
271 let color = ThemeColor::new(cx);
272
273 Story::container(cx)
274 .child(Story::title_for::<_, Buffer<S>>(cx))
275 .child(Story::label(cx, "Default"))
276 .child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
277 .child(Story::label(cx, "Hello World (Rust)"))
278 .child(
279 div()
280 .w(rems(64.))
281 .h_96()
282 .child(hello_world_rust_buffer_example(&color)),
283 )
284 .child(Story::label(cx, "Hello World (Rust) with Status"))
285 .child(
286 div()
287 .w(rems(64.))
288 .h_96()
289 .child(hello_world_rust_buffer_with_status_example(&color)),
290 )
291 }
292 }
293}