miv_editor/app/editor/
mod.rs1pub mod filetypes;
2pub mod gap_buffer;
3pub mod highlighting;
4pub mod motions;
5
6use std::fmt::{self, Debug};
7use std::fs::write;
8use std::{env, fs, path::PathBuf};
9
10use tracing::info;
11use tree_sitter_highlight::{HighlightConfiguration, Highlighter};
12
13use self::highlighting::HighlightSpan;
14use self::motions::Motion;
15use self::{
16 filetypes::FileType,
17 gap_buffer::GapBuffer,
18 highlighting::{get_highlighting_config, get_highlighting_function, HighlightingFn},
19};
20
21use super::{AppResult, InputMode};
22
23pub struct EditorBuffer {
24 pub cursor_index: usize,
26 pub cursor_line: usize,
28 pub cursor_col: usize,
30 pub gap_buffer: GapBuffer,
32 pub path: Option<PathBuf>,
34 pub filetype: FileType,
35 pub highlighter: Highlighter,
37 pub highlighter_config: HighlightConfiguration,
38 pub highlight_groups: Vec<HighlightSpan>,
39 highlighting_function: HighlightingFn,
40}
41
42impl Debug for EditorBuffer {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 f.debug_struct("EditorBuffer")
45 .field("cursor_index", &self.cursor_index)
46 .field("gap_buffer", &self.gap_buffer)
47 .field("path", &self.path)
48 .field("filetype", &self.filetype)
49 .finish()
50 }
51}
52
53impl Default for EditorBuffer {
54 fn default() -> Self {
55 let highlighter = Highlighter::new();
56 let highlighter_config = get_highlighting_config(FileType::Rust);
57 let highlighter_function = get_highlighting_function(FileType::Rust);
58
59 EditorBuffer {
60 cursor_index: 0,
61 cursor_line: 0,
62 cursor_col: 0,
63 gap_buffer: GapBuffer::with_data(""),
64 path: None,
65 filetype: FileType::Rust,
66 highlighter,
67 highlighter_config,
68 highlight_groups: vec![],
69 highlighting_function: highlighter_function,
70 }
71 }
72}
73
74impl EditorBuffer {
75 pub fn from_file(file: String) -> Self {
76 let cwd_path = env::current_dir().unwrap();
77 let full_path = cwd_path.join(file);
78 let source =
79 fs::read_to_string(&full_path).expect("Should have been able to read the file");
80
81 let mut eb = EditorBuffer {
82 gap_buffer: GapBuffer::with_data(&source),
83 path: Some(full_path),
84 ..Self::default()
85 };
86 eb.calculate_highlights();
87 eb
88 }
89
90 pub fn save(&self) -> AppResult<()> {
91 if let Some(path) = &self.path {
92 write(path, self.gap_buffer.get_text_as_string())?;
93 return Ok(());
94 }
95 Err("Failed to save".into())
96 }
97
98 pub fn insert(&mut self, to_insert: String, mode: InputMode) {
99 self.gap_buffer.insert_at(&to_insert, self.cursor_index);
100
101 self.move_cursor(&Motion::CharForward, mode);
102 self.calculate_highlights();
103 }
104
105 pub fn move_cursor(&mut self, motion: &Motion, mode: InputMode) {
106 match motion {
107 Motion::CharForward => self.move_forward_char(),
108 Motion::CharBackward => self.move_backward_char(),
109 Motion::NextWordStart => self.move_forward_to_word_start(),
110 Motion::NextWordProperStart => self.move_forward_to_word_proper_start(),
111 Motion::NextWordEnd => self.move_forward_to_word_end(),
112 Motion::NextWordProperEnd => self.move_forward_to_word_proper_end(),
113 Motion::LastWordStart => self.move_backward_to_word_start(),
114 Motion::LastWordProperStart => self.move_backward_to_word_proper_start(),
115 Motion::LastWordEnd => self.move_backward_to_word_end(),
116 Motion::LastWordProperEnd => self.move_backward_to_word_proper_end(),
117 Motion::LineStart => self.move_to_line_start(),
118 Motion::LineEnd => self.move_to_line_end(),
119 Motion::NextLineStart => self.move_to_next_line_start(),
120 }
121
122 let final_char = self.gap_buffer.get_at(self.cursor_index);
123 if let InputMode::Normal = mode {
124 if final_char == '\n' {
125 self.cursor_index -= 1
126 }
127 };
128 }
129
130 pub fn delete(&mut self, motion: Motion, mode: InputMode) {
131 let delete_start = self.cursor_index;
132 self.move_cursor(&motion, mode);
133 let candidate_char = self.gap_buffer.get_at(self.cursor_index);
134 info!("{:?}", candidate_char);
135 let delete_end = self.cursor_index;
136 let (amount_to_delete, at) = if delete_end < delete_start {
137 (delete_start - delete_end, delete_end)
138 } else {
139 (delete_end - delete_start, delete_start)
140 };
141
142 self.gap_buffer.delete_at(amount_to_delete, at);
143 self.cursor_index = at;
144 self.calculate_highlights();
145 }
146
147 pub fn calculate_highlights(&mut self) {
148 let content = self.gap_buffer.get_text_as_bytes();
149 self.highlight_groups =
150 (self.highlighting_function)(&content, &mut self.highlighter, &self.highlighter_config);
151 }
152
153 fn move_forward_char(&mut self) {
154 let candidate_index = self.cursor_index + 1;
155
156 let data_length = self.gap_buffer.data_length();
157
158 if candidate_index >= data_length {
159 self.cursor_index = data_length
160 } else {
161 self.cursor_index = candidate_index
162 };
163 }
164
165 fn move_backward_char(&mut self) {
166 let candidate_index = self.cursor_index.saturating_sub(1);
167
168 self.cursor_index = candidate_index;
169 }
170
171 fn move_forward_to_word_start(&mut self) {
172 let start_char = self.gap_buffer.get_at(self.cursor_index);
173 let start_is_alphanumeric = start_char.is_alphanumeric();
174
175 let mut whitspace_seen = start_char.is_whitespace();
176 let mut candidate_index = self.cursor_index + 1;
177 let data_length = self.gap_buffer.data_length();
178
179 while candidate_index < data_length {
180 let candidate_char = self.gap_buffer.get_at(candidate_index);
181 if !candidate_char.is_whitespace() {
182 if whitspace_seen {
183 self.cursor_index = candidate_index;
184 return;
185 };
186
187 if start_is_alphanumeric && !candidate_char.is_alphanumeric() {
188 self.cursor_index = candidate_index;
189 return;
190 };
191
192 if !start_is_alphanumeric && candidate_char.is_alphanumeric() {
193 self.cursor_index = candidate_index;
194 return;
195 };
196 } else {
197 whitspace_seen = true;
198 }
199
200 candidate_index += 1
201 }
202
203 self.cursor_index = data_length - 1;
204 }
205
206 fn move_forward_to_word_proper_start(&mut self) {
207 let mut whitspace_seen = self.gap_buffer.get_at(self.cursor_index).is_whitespace();
208 let mut candidate_index = self.cursor_index + 1;
209 let data_length = self.gap_buffer.data_length();
210
211 while candidate_index < data_length {
212 let candidate_char = self.gap_buffer.get_at(candidate_index);
213
214 if whitspace_seen && !candidate_char.is_whitespace() {
215 self.cursor_index = candidate_index;
216 return;
217 };
218
219 if candidate_char.is_whitespace() {
220 whitspace_seen = true;
221 };
222
223 candidate_index += 1
224 }
225
226 self.cursor_index = data_length - 1;
227 }
228
229 fn move_forward_to_word_end(&mut self) {
230 let mut candidate_index = self.cursor_index + 1;
231 let data_length = self.gap_buffer.data_length();
232
233 while candidate_index < data_length - 1 {
234 let candidate_char = self.gap_buffer.get_at(candidate_index);
235 let candidate_char_is_alphanumeric = candidate_char.is_alphanumeric();
236 let next_index = candidate_index + 1;
237 let next_char = self.gap_buffer.get_at(next_index);
238
239 if !candidate_char.is_whitespace()
240 && ((candidate_char_is_alphanumeric ^ next_char.is_alphanumeric())
241 || next_char.is_whitespace())
242 {
243 self.cursor_index = candidate_index;
244 return;
245 }
246 candidate_index += 1
247 }
248
249 self.cursor_index = data_length - 1;
250 }
251
252 fn move_forward_to_word_proper_end(&mut self) {
253 let mut candidate_index = self.cursor_index + 1;
254 let data_length = self.gap_buffer.data_length();
255
256 while candidate_index < data_length - 1 {
257 let candidate_char = self.gap_buffer.get_at(candidate_index);
258 let next_index = candidate_index + 1;
259 let next_char = self.gap_buffer.get_at(next_index);
260
261 if next_char.is_whitespace() && !candidate_char.is_whitespace() {
262 self.cursor_index = candidate_index;
263 return;
264 };
265
266 candidate_index += 1
267 }
268
269 self.cursor_index = data_length - 1;
270 }
271
272 fn move_backward_to_word_start(&mut self) {
273 let mut candidate_index = self.cursor_index.saturating_sub(1);
274 while candidate_index > 0 {
275 let candidate_char = self.gap_buffer.get_at(candidate_index);
276 let candidate_char_is_alphanumeric = candidate_char.is_alphanumeric();
277 let next_index = candidate_index - 1;
278 let next_char = self.gap_buffer.get_at(next_index);
279
280 if !candidate_char.is_whitespace()
281 && ((candidate_char_is_alphanumeric ^ next_char.is_alphanumeric())
282 || next_char.is_whitespace())
283 {
284 self.cursor_index = candidate_index;
285 return;
286 }
287 candidate_index -= 1
288 }
289
290 self.cursor_index = 0;
291 }
292
293 fn move_backward_to_word_proper_start(&mut self) {
294 let mut candidate_index = self.cursor_index.saturating_sub(1);
295
296 while candidate_index > 0 {
297 let candidate_char = self.gap_buffer.get_at(candidate_index);
298 let next_index = candidate_index - 1;
299 let next_char = self.gap_buffer.get_at(next_index);
300
301 if next_char.is_whitespace() && !candidate_char.is_whitespace() {
302 self.cursor_index = candidate_index;
303 return;
304 };
305
306 candidate_index -= 1
307 }
308
309 self.cursor_index = 0;
310 }
311
312 fn move_backward_to_word_end(&mut self) {
313 let start_char = self.gap_buffer.get_at(self.cursor_index);
314 let start_is_alphanumeric = start_char.is_alphanumeric();
315
316 let mut whitspace_seen = start_char.is_whitespace();
317 let mut candidate_index = self.cursor_index - 1;
318
319 while candidate_index > 0 {
320 let candidate_char = self.gap_buffer.get_at(candidate_index);
321 if !candidate_char.is_whitespace() {
322 if whitspace_seen {
323 self.cursor_index = candidate_index;
324 return;
325 };
326
327 if start_is_alphanumeric && !candidate_char.is_alphanumeric() {
328 self.cursor_index = candidate_index;
329 return;
330 };
331
332 if !start_is_alphanumeric && candidate_char.is_alphanumeric() {
333 self.cursor_index = candidate_index;
334 return;
335 };
336 } else {
337 whitspace_seen = true;
338 }
339
340 candidate_index -= 1
341 }
342
343 self.cursor_index = 0;
344 }
345
346 fn move_backward_to_word_proper_end(&mut self) {
347 let mut whitspace_seen = self.gap_buffer.get_at(self.cursor_index).is_whitespace();
348 let mut candidate_index = self.cursor_index - 1;
349
350 while candidate_index > 0 {
351 let candidate_char = self.gap_buffer.get_at(candidate_index);
352
353 if whitspace_seen && !candidate_char.is_whitespace() {
354 self.cursor_index = candidate_index;
355 return;
356 };
357
358 if candidate_char.is_whitespace() {
359 whitspace_seen = true;
360 };
361
362 candidate_index -= 1
363 }
364
365 self.cursor_index = 0;
366 }
367
368 fn move_to_line_start(&mut self) {
369 let mut candidate_index = self.cursor_index.saturating_sub(1);
370 while candidate_index > 0 {
371 let candidate_char = self.gap_buffer.get_at(candidate_index);
372 if candidate_char == '\n' {
373 self.cursor_index = candidate_index + 1;
374 return;
375 }
376 candidate_index -= 1
377 }
378
379 self.cursor_index = 0;
380 }
381
382 fn move_to_next_line_start(&mut self) {
383 let mut candidate_index = self.cursor_index + 1;
384 let data_length = self.gap_buffer.data_length();
385
386 while candidate_index < data_length {
387 let candidate_char = self.gap_buffer.get_at(candidate_index);
388 if candidate_char == '\n' {
389 self.cursor_index = candidate_index + 1;
390 return;
391 }
392 candidate_index += 1
393 }
394
395 self.cursor_index = data_length - 1;
396 }
397
398 fn move_to_line_end(&mut self) {
399 let mut candidate_index = self.cursor_index + 1;
400 let data_length = self.gap_buffer.data_length();
401
402 while candidate_index < data_length {
403 let candidate_char = self.gap_buffer.get_at(candidate_index);
404 if candidate_char == '\n' {
405 self.cursor_index = candidate_index;
406 return;
407 }
408 candidate_index += 1
409 }
410
411 self.cursor_index = data_length - 1;
412 }
413}