1use crate::state::EditorState;
8use crate::view::overlay::OverlayFace;
9use crate::view::virtual_text::VirtualTextPosition;
10use ratatui::style::Style;
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum ViewTokenKind {
15 Text(String),
17 Newline,
19 Space,
21 VirtualText {
23 text: String,
24 style: Style,
25 position: VirtualTextPosition,
26 priority: i32,
27 },
28 StyleStart(Style),
30 StyleEnd,
31 Overlay(OverlayFace),
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct ViewToken {
38 pub source_offset: Option<usize>,
40 pub kind: ViewTokenKind,
42}
43
44#[derive(Debug, Clone, Default)]
46pub struct ViewStream {
47 pub tokens: Vec<ViewToken>,
48 pub source_map: Vec<Option<usize>>,
50}
51
52impl ViewStream {
53 pub fn new() -> Self {
54 Self {
55 tokens: Vec::new(),
56 source_map: Vec::new(),
57 }
58 }
59
60 pub fn push(&mut self, token: ViewToken) {
61 self.source_map.push(token.source_offset);
62 self.tokens.push(token);
63 }
64}
65
66pub fn build_base_stream(state: &mut EditorState, start: usize, end: usize) -> ViewStream {
70 let mut stream = ViewStream::new();
71
72 if start >= end {
73 return stream;
74 }
75
76 let text = state.get_text_range(start, end);
77
78 let mut current_offset = start;
79 let mut buffer = String::new();
80
81 for ch in text.chars() {
82 if ch == '\n' {
83 if !buffer.is_empty() {
84 stream.push(ViewToken {
85 source_offset: Some(current_offset - buffer.len()),
86 kind: ViewTokenKind::Text(buffer.clone()),
87 });
88 buffer.clear();
89 }
90 stream.push(ViewToken {
91 source_offset: Some(current_offset),
92 kind: ViewTokenKind::Newline,
93 });
94 current_offset += 1;
95 } else {
96 buffer.push(ch);
97 current_offset += ch.len_utf8();
98 }
99 }
100
101 if !buffer.is_empty() {
102 stream.push(ViewToken {
103 source_offset: Some(current_offset - buffer.len()),
104 kind: ViewTokenKind::Text(buffer),
105 });
106 }
107
108 stream
109}