1use {
2 crate::{
3 document::{DocumentLayout, IndentState},
4 inlays::{BlockInlay, InlineInlay},
5 selection::Affinity,
6 session::SessionLayout,
7 str::StrExt,
8 text::{Position, Text},
9 widgets::{BlockWidget, InlineWidget},
10 wrap::WrapData,
11 Token,
12 },
13 std::{cell::Ref, slice::Iter},
14};
15
16#[derive(Debug)]
17pub struct Layout<'a> {
18 pub text: Ref<'a, Text>,
19 pub document_layout: Ref<'a, DocumentLayout>,
20 pub session_layout: Ref<'a, SessionLayout>,
21}
22
23impl<'a> Layout<'a> {
24 pub fn as_text(&self) -> &Text {
25 &self.text
26 }
27
28 pub fn width(&self) -> f64 {
29 let mut width: f64 = 0.0;
30 for line in self.lines(0, self.as_text().as_lines().len()) {
31 width = width.max(line.width());
32 }
33 width
34 }
35
36 pub fn height(&self) -> f64 {
37 *self.session_layout.y.last().unwrap()
38 }
39
40 pub fn find_first_line_ending_after_y(&self, y: f64) -> usize {
41 match self.session_layout.y[..self.session_layout.y.len() - 1]
42 .binary_search_by(|current_y| current_y.partial_cmp(&y).unwrap())
43 {
44 Ok(line) => line,
45 Err(line) => line.saturating_sub(1),
46 }
47 }
48
49 pub fn find_first_line_starting_after_y(&self, y: f64) -> usize {
50 match self.session_layout.y[..self.session_layout.y.len() - 1]
51 .binary_search_by(|current_y| current_y.partial_cmp(&y).unwrap())
52 {
53 Ok(line) => line + 1,
54 Err(line) => line,
55 }
56 }
57
58 pub fn logical_to_normalized_position(
59 &self,
60 position: Position,
61 affinity: Affinity,
62 ) -> (f64, f64) {
63 let line = self.line(position.line_index);
64 let (row_index, column_index) =
65 line.logical_to_grid_position(position.byte_index, affinity);
66 let (x, y) = line.grid_to_normalized_position(row_index, column_index);
67 (x, line.y() + y)
68 }
69
70 pub fn line(&self, index: usize) -> Line<'_> {
71 Line {
72 y: self.session_layout.y.get(index).copied(),
73 column_count: self.session_layout.column_count[index],
74 fold: self.session_layout.fold_column[index],
75 scale: self.session_layout.scale[index],
76 text: &self.text.as_lines()[index],
77 indent_state: self.document_layout.indent_state[index],
78 tokens: &self.document_layout.tokens[index],
79 inlays: &self.document_layout.inline_inlays[index],
80 wrap_data: self.session_layout.wrap_data[index].as_ref(),
81 }
82 }
83
84 pub fn lines(&self, start: usize, end: usize) -> Lines<'_> {
85 Lines {
86 y: self.session_layout.y
87 [start.min(self.session_layout.y.len())..end.min(self.session_layout.y.len())]
88 .iter(),
89 column_count: self.session_layout.column_count[start..end].iter(),
90 fold: self.session_layout.fold_column[start..end].iter(),
91 scale: self.session_layout.scale[start..end].iter(),
92 text: self.text.as_lines()[start..end].iter(),
93 indent_state: self.document_layout.indent_state[start..end].iter(),
94 tokens: self.document_layout.tokens[start..end].iter(),
95 inline_inlays: self.document_layout.inline_inlays[start..end].iter(),
96 wrap_data: self.session_layout.wrap_data[start..end].iter(),
97 }
98 }
99
100 pub fn block_elements(&self, line_start: usize, line_end: usize) -> BlockElements<'_> {
101 let mut block_inlays = self.document_layout.block_inlays.iter();
102 while block_inlays
103 .as_slice()
104 .first()
105 .map_or(false, |&(position, _)| position < line_start)
106 {
107 block_inlays.next();
108 }
109 BlockElements {
110 lines: self.lines(line_start, line_end),
111 block_inlays,
112 position: line_start,
113 }
114 }
115}
116
117#[derive(Clone, Debug)]
118pub struct Lines<'a> {
119 y: Iter<'a, f64>,
120 column_count: Iter<'a, Option<usize>>,
121 fold: Iter<'a, usize>,
122 scale: Iter<'a, f64>,
123 text: Iter<'a, String>,
124 indent_state: Iter<'a, Option<IndentState>>,
125 tokens: Iter<'a, Vec<Token>>,
126 inline_inlays: Iter<'a, Vec<(usize, InlineInlay)>>,
127 wrap_data: Iter<'a, Option<WrapData>>,
128}
129
130impl<'a> Iterator for Lines<'a> {
131 type Item = Line<'a>;
132
133 fn next(&mut self) -> Option<Self::Item> {
134 let text = self.text.next()?;
135 Some(Line {
136 y: self.y.next().copied(),
137 column_count: *self.column_count.next().unwrap(),
138 fold: *self.fold.next().unwrap(),
139 scale: *self.scale.next().unwrap(),
140 text,
141 indent_state: *self.indent_state.next().unwrap(),
142 tokens: self.tokens.next().unwrap(),
143 inlays: self.inline_inlays.next().unwrap(),
144 wrap_data: self.wrap_data.next().unwrap().as_ref(),
145 })
146 }
147}
148
149#[derive(Clone, Copy, Debug, PartialEq)]
150pub struct Line<'a> {
151 pub y: Option<f64>,
152 pub column_count: Option<usize>,
153 pub fold: usize,
154 pub scale: f64,
155 pub text: &'a str,
156 pub indent_state: Option<IndentState>,
157 pub tokens: &'a [Token],
158 pub inlays: &'a [(usize, InlineInlay)],
159 pub wrap_data: Option<&'a WrapData>,
160}
161
162impl<'a> Line<'a> {
163 pub fn y(&self) -> f64 {
164 self.y.unwrap()
165 }
166
167 pub fn row_count(&self) -> usize {
168 self.wrap_data.unwrap().wraps.len() + 1
169 }
170
171 pub fn column_count(&self) -> usize {
172 self.column_count.unwrap()
173 }
174
175 pub fn width(&self) -> f64 {
176 let mut width: f64 = 0.0;
177 for row_index in 0..self.row_count() {
178 let (x, _) = self.grid_to_normalized_position(row_index, self.column_count());
179 width = width.max(x);
180 }
181 width
182 }
183
184 pub fn height(&self) -> f64 {
185 self.row_count() as f64 * self.scale
186 }
187
188 pub fn logical_to_grid_position(
189 &self,
190 byte_index: usize,
191 affinity: Affinity,
192 ) -> (usize, usize) {
193 let mut current_byte_index = 0;
194 let mut current_row_index = 0;
195 let mut current_column_index = 0;
196 if current_byte_index == byte_index && affinity == Affinity::Before {
197 return (current_row_index, current_column_index);
198 }
199 for element in self.wrapped_elements() {
200 match element {
201 WrappedElement::Text {
202 is_inlay: false,
203 text,
204 } => {
205 for grapheme in text.graphemes() {
206 if current_byte_index == byte_index && affinity == Affinity::After {
207 return (current_row_index, current_column_index);
208 }
209 current_byte_index += grapheme.len();
210 current_column_index += grapheme.column_count();
211 if current_byte_index == byte_index && affinity == Affinity::Before {
212 return (current_row_index, current_column_index);
213 }
214 }
215 }
216 WrappedElement::Text {
217 is_inlay: true,
218 text,
219 } => {
220 current_column_index += text.column_count();
221 }
222 WrappedElement::Widget(widget) => {
223 current_column_index += widget.column_count;
224 }
225 WrappedElement::Wrap => {
226 current_row_index += 1;
227 current_column_index = self.wrap_indent_column_count();
228 }
229 }
230 }
231 if current_byte_index == byte_index && affinity == Affinity::After {
232 return (current_row_index, current_column_index);
233 }
234 panic!()
235 }
236
237 pub fn grid_to_logical_position(
238 &self,
239 row_index: usize,
240 column_index: usize,
241 ) -> (usize, Affinity) {
242 let mut current_row_index = 0;
243 let mut current_column_index = 0;
244 let mut current_byte_index = 0;
245 for element in self.wrapped_elements() {
246 match element {
247 WrappedElement::Text {
248 is_inlay: false,
249 text,
250 } => {
251 for grapheme in text.graphemes() {
252 let next_column = current_column_index + grapheme.column_count();
253 if current_row_index == row_index
254 && (current_column_index..next_column).contains(&column_index)
255 {
256 return (current_byte_index, Affinity::After);
257 }
258 current_byte_index += grapheme.len();
259 current_column_index = next_column;
260 }
261 }
262 WrappedElement::Text {
263 is_inlay: true,
264 text,
265 } => {
266 let next_column = current_column_index + text.column_count();
267 if current_row_index == row_index
268 && (current_column_index..next_column).contains(&column_index)
269 {
270 return (current_byte_index, Affinity::Before);
271 }
272 current_column_index = next_column;
273 }
274 WrappedElement::Widget(widget) => {
275 current_column_index += widget.column_count;
276 }
277 WrappedElement::Wrap => {
278 if current_row_index == row_index {
279 return (current_byte_index, Affinity::Before);
280 }
281 current_row_index += 1;
282 current_column_index = self.wrap_indent_column_count();
283 }
284 }
285 }
286 if current_row_index == row_index {
287 return (current_byte_index, Affinity::After);
288 }
289 panic!()
290 }
291
292 pub fn grid_to_normalized_position(&self, row_index: usize, column_index: usize) -> (f64, f64) {
293 let before_fold = column_index.min(self.fold);
294 let after_fold = column_index - before_fold;
295 (
296 before_fold as f64 + after_fold as f64 * self.scale,
297 row_index as f64 * self.scale,
298 )
299 }
300
301 pub fn fold(&self) -> usize {
302 self.fold
303 }
304
305 pub fn scale(&self) -> f64 {
306 self.scale
307 }
308
309 pub fn wrap_indent_column_count(self) -> usize {
310 self.wrap_data.unwrap().indent_column_count
311 }
312
313 pub fn text(&self) -> &str {
314 self.text
315 }
316
317 pub fn indent_column_count(&self) -> usize {
318 match self.indent_state.unwrap() {
319 IndentState::Empty(indent_column_count) => indent_column_count,
320 IndentState::NonEmpty(indent_column_count, _) => indent_column_count,
321 }
322 }
323
324 pub fn tokens(&self) -> &[Token] {
325 self.tokens
326 }
327
328 pub fn inline_elements(&self) -> InlineElements<'a> {
329 InlineElements {
330 text: self.text,
331 inlays: self.inlays.iter(),
332 position: 0,
333 }
334 }
335
336 pub fn wrapped_elements(&self) -> WrappedElements<'a> {
337 let mut elements = self.inline_elements();
338 WrappedElements {
339 element: elements.next(),
340 elements,
341 wraps: self.wrap_data.unwrap().wraps.iter(),
342 position: 0,
343 }
344 }
345}
346
347#[derive(Clone, Debug)]
348pub struct InlineElements<'a> {
349 text: &'a str,
350 inlays: Iter<'a, (usize, InlineInlay)>,
351 position: usize,
352}
353
354impl<'a> Iterator for InlineElements<'a> {
355 type Item = InlineElement<'a>;
356
357 fn next(&mut self) -> Option<Self::Item> {
358 if self
359 .inlays
360 .as_slice()
361 .first()
362 .map_or(false, |&(position, _)| position == self.position)
363 {
364 let (_, inline_inlay) = self.inlays.next().unwrap();
365 return Some(match *inline_inlay {
366 InlineInlay::Text(ref text) => InlineElement::Text {
367 is_inlay: true,
368 text,
369 },
370 InlineInlay::Widget(widget) => InlineElement::Widget(widget),
371 });
372 }
373 if self.text.is_empty() {
374 return None;
375 }
376 let mut len: usize = self.text.len();
377 if let Some(&(position, _)) = self.inlays.as_slice().first() {
378 len = len.min(position - self.position);
379 }
380 let (text_0, text_1) = self.text.split_at(len);
381 self.text = text_1;
382 self.position += text_0.len();
383 Some(InlineElement::Text {
384 is_inlay: false,
385 text: text_0,
386 })
387 }
388}
389
390#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
391pub enum InlineElement<'a> {
392 Text { is_inlay: bool, text: &'a str },
393 Widget(InlineWidget),
394}
395
396#[derive(Clone, Debug)]
397pub struct WrappedElements<'a> {
398 element: Option<InlineElement<'a>>,
399 elements: InlineElements<'a>,
400 wraps: Iter<'a, usize>,
401 position: usize,
402}
403
404impl<'a> Iterator for WrappedElements<'a> {
405 type Item = WrappedElement<'a>;
406
407 fn next(&mut self) -> Option<Self::Item> {
408 if self
409 .wraps
410 .as_slice()
411 .first()
412 .map_or(false, |&position| position == self.position)
413 {
414 self.wraps.next();
415 return Some(WrappedElement::Wrap);
416 }
417 Some(match self.element.take()? {
418 InlineElement::Text { is_inlay, text } => {
419 let mut len: usize = text.len();
420 if let Some(&position) = self.wraps.as_slice().first() {
421 len = len.min(position - self.position);
422 }
423 let text = if len < text.len() {
424 let (text_0, text_1) = text.split_at(len);
425 self.element = Some(InlineElement::Text {
426 is_inlay,
427 text: text_1,
428 });
429 text_0
430 } else {
431 self.element = self.elements.next();
432 text
433 };
434 self.position += text.len();
435 WrappedElement::Text { is_inlay, text }
436 }
437 InlineElement::Widget(widget) => {
438 self.position += 1;
439 WrappedElement::Widget(widget)
440 }
441 })
442 }
443}
444
445#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
446pub enum WrappedElement<'a> {
447 Text { is_inlay: bool, text: &'a str },
448 Widget(InlineWidget),
449 Wrap,
450}
451
452#[derive(Clone, Debug)]
453pub struct BlockElements<'a> {
454 lines: Lines<'a>,
455 block_inlays: Iter<'a, (usize, BlockInlay)>,
456 position: usize,
457}
458
459impl<'a> Iterator for BlockElements<'a> {
460 type Item = BlockElement<'a>;
461
462 fn next(&mut self) -> Option<Self::Item> {
463 if self
464 .block_inlays
465 .as_slice()
466 .first()
467 .map_or(false, |&(line, _)| line == self.position)
468 {
469 let (_, block_inlay) = self.block_inlays.next().unwrap();
470 return Some(match *block_inlay {
471 BlockInlay::Widget(widget) => BlockElement::Widget(widget),
472 });
473 }
474 let line = self.lines.next()?;
475 self.position += 1;
476 Some(BlockElement::Line {
477 is_inlay: false,
478 line,
479 })
480 }
481}
482
483#[derive(Clone, Copy, Debug, PartialEq)]
484pub enum BlockElement<'a> {
485 Line { is_inlay: bool, line: Line<'a> },
486 Widget(BlockWidget),
487}