floem/views/editor/
phantom_text.rs1use std::borrow::Cow;
2
3use crate::{
4 peniko::Color,
5 text::{Attrs, AttrsList},
6};
7use floem_editor_core::cursor::CursorAffinity;
8use smallvec::SmallVec;
9
10#[derive(Debug, Clone)]
14pub struct PhantomText {
15 pub kind: PhantomTextKind,
17 pub col: usize,
19 pub affinity: Option<CursorAffinity>,
22 pub text: String,
23 pub font_size: Option<usize>,
24 pub fg: Option<Color>,
26 pub bg: Option<Color>,
27 pub under_line: Option<Color>,
28}
29
30#[derive(Debug, Clone, Copy, Ord, Eq, PartialEq, PartialOrd)]
31pub enum PhantomTextKind {
32 Ime,
34 Placeholder,
35 Completion,
37 InlayHint,
39 Diagnostic,
41}
42
43#[derive(Debug, Default, Clone)]
48pub struct PhantomTextLine {
49 pub text: SmallVec<[PhantomText; 6]>,
51}
52
53impl PhantomTextLine {
54 pub fn col_at(&self, pre_col: usize) -> usize {
56 let mut last = pre_col;
57 for (col_shift, size, col, _) in self.offset_size_iter() {
58 if pre_col >= col {
59 last = pre_col + col_shift + size;
60 }
61 }
62
63 last
64 }
65
66 pub fn col_after(&self, pre_col: usize, before_cursor: bool) -> usize {
71 let mut last = pre_col;
72 for (col_shift, size, col, text) in self.offset_size_iter() {
73 let before_cursor = match text.affinity {
74 Some(CursorAffinity::Forward) => true,
75 Some(CursorAffinity::Backward) => false,
76 None => before_cursor,
77 };
78
79 if pre_col > col || (pre_col == col && before_cursor) {
80 last = pre_col + col_shift + size;
81 }
82 }
83
84 last
85 }
86
87 pub fn col_after_force(&self, pre_col: usize, before_cursor: bool) -> usize {
92 let mut last = pre_col;
93 for (col_shift, size, col, _) in self.offset_size_iter() {
94 if pre_col > col || (pre_col == col && before_cursor) {
95 last = pre_col + col_shift + size;
96 }
97 }
98
99 last
100 }
101
102 pub fn col_after_ignore(
111 &self,
112 pre_col: usize,
113 before_cursor: bool,
114 skip: impl Fn(&PhantomText) -> bool,
115 ) -> usize {
116 let mut last = pre_col;
117 for (col_shift, size, col, phantom) in self.offset_size_iter() {
118 if skip(phantom) {
119 continue;
120 }
121
122 if pre_col > col || (pre_col == col && before_cursor) {
123 last = pre_col + col_shift + size;
124 }
125 }
126
127 last
128 }
129
130 pub fn before_col(&self, col: usize) -> usize {
132 let mut last = col;
133 for (col_shift, size, hint_col, _) in self.offset_size_iter() {
134 let shifted_start = hint_col + col_shift;
135 let shifted_end = shifted_start + size;
136 if col >= shifted_start {
137 if col >= shifted_end {
138 last = col - col_shift - size;
139 } else {
140 last = hint_col;
141 }
142 }
143 }
144 last
145 }
146
147 pub fn combine_with_text<'a>(&self, text: &'a str) -> Cow<'a, str> {
149 let mut text = Cow::Borrowed(text);
150 let mut col_shift = 0;
151
152 for phantom in self.text.iter() {
153 let location = phantom.col + col_shift;
154
155 if text.get(location..).is_none() {
157 return text;
158 }
159
160 let mut text_o = text.into_owned();
161 text_o.insert_str(location, &phantom.text);
162 text = Cow::Owned(text_o);
163
164 col_shift += phantom.text.len();
165 }
166
167 text
168 }
169
170 pub fn offset_size_iter(
174 &self,
175 ) -> impl Iterator<Item = (usize, usize, usize, &PhantomText)> + '_ {
176 let mut col_shift = 0;
177
178 self.text.iter().map(move |phantom| {
179 let pre_col_shift = col_shift;
180 col_shift += phantom.text.len();
181 (
182 pre_col_shift,
183 col_shift - pre_col_shift,
184 phantom.col,
185 phantom,
186 )
187 })
188 }
189
190 pub fn apply_attr_styles(&self, default: Attrs, attrs_list: &mut AttrsList) {
191 for (offset, size, col, phantom) in self.offset_size_iter() {
192 let start = col + offset;
193 let end = start + size;
194
195 let mut attrs = default.clone();
196 if let Some(fg) = phantom.fg {
197 attrs = attrs.color(fg);
198 }
199 if let Some(phantom_font_size) = phantom.font_size {
200 let font_size = attrs.font_size;
201 attrs = attrs.font_size((phantom_font_size as f32).min(font_size));
202 }
203
204 attrs_list.add_span(start..end, attrs);
205 }
206 }
207}