1use super::cursor::{CursorMove, next_word_start, prev_word_start};
9use super::history::Edit;
10use super::{TextArea, byte_of_char, char_count, split_lines};
11
12impl TextArea {
13 fn replace_range(&mut self, start: (usize, usize), end: (usize, usize), text: &str) -> bool {
18 let start = self.clamp_position(start);
19 let end = self.clamp_position(end);
20 if start == end && text.is_empty() {
21 return false;
22 }
23 let inserted = split_lines(text).join("\n");
26 let cursor_before = self.cursor;
27 let removed = self.remove_raw(start, end);
28 let cursor_after = self.insert_raw(start, &inserted);
29 self.cursor = cursor_after;
30 self.selection_anchor = None;
31 self.history.push(Edit {
32 start,
33 removed,
34 inserted,
35 cursor_before,
36 cursor_after,
37 });
38 true
39 }
40
41 fn remove_raw(&mut self, (sr, sc): (usize, usize), (er, ec): (usize, usize)) -> String {
44 if (sr, sc) == (er, ec) {
45 return String::new();
46 }
47 let sb = byte_of_char(&self.lines[sr], sc);
48 let eb = byte_of_char(&self.lines[er], ec);
49 if sr == er {
50 let removed = self.lines[sr][sb..eb].to_string();
51 self.lines[sr].replace_range(sb..eb, "");
52 return removed;
53 }
54 let mut removed = self.lines[sr][sb..].to_string();
55 for line in &self.lines[sr + 1..er] {
56 removed.push('\n');
57 removed.push_str(line);
58 }
59 removed.push('\n');
60 removed.push_str(&self.lines[er][..eb]);
61
62 let tail = self.lines[er][eb..].to_string();
63 self.lines[sr].truncate(sb);
64 self.lines[sr].push_str(&tail);
65 self.lines.drain(sr + 1..=er);
66 removed
67 }
68
69 fn insert_raw(&mut self, (row, col): (usize, usize), text: &str) -> (usize, usize) {
72 if text.is_empty() {
73 return (row, col);
74 }
75 let byte = byte_of_char(&self.lines[row], col);
76 let tail = self.lines[row].split_off(byte);
77 let mut chunks = split_lines(text);
78 let first = chunks.remove(0);
79 self.lines[row].push_str(&first);
80
81 if chunks.is_empty() {
82 let end = (row, col + char_count(&first));
83 self.lines[row].push_str(&tail);
84 return end;
85 }
86
87 let end_row = row + chunks.len();
88 let end_col = char_count(chunks.last().expect("chunks is non-empty"));
89 let last = chunks.len() - 1;
90 chunks[last].push_str(&tail);
91 for (offset, line) in chunks.into_iter().enumerate() {
92 self.lines.insert(row + 1 + offset, line);
93 }
94 (end_row, end_col)
95 }
96
97 fn edit_range(&self) -> ((usize, usize), (usize, usize)) {
100 self.selection().unwrap_or((self.cursor, self.cursor))
101 }
102
103 pub fn insert_char(&mut self, c: char) -> bool {
105 let mut buf = [0u8; 4];
106 let (start, end) = self.edit_range();
107 self.replace_range(start, end, c.encode_utf8(&mut buf))
108 }
109
110 pub fn insert_str(&mut self, text: &str) -> bool {
113 let (start, end) = self.edit_range();
114 self.replace_range(start, end, text)
115 }
116
117 pub fn replace_before_cursor(&mut self, count: usize, text: &str) -> bool {
120 let (row, col) = self.cursor;
121 self.replace_range((row, col.saturating_sub(count)), (row, col), text)
122 }
123
124 pub fn insert_newline(&mut self) -> bool {
126 let (start, end) = self.edit_range();
127 self.replace_range(start, end, "\n")
128 }
129
130 pub fn insert_tab(&mut self) -> bool {
132 let width = self.tab_len - (self.cursor.1 % self.tab_len);
133 let spaces = " ".repeat(width);
134 self.insert_str(&spaces)
135 }
136
137 pub fn delete_prev_char(&mut self) -> bool {
139 if let Some((start, end)) = self.selection() {
140 return self.replace_range(start, end, "");
141 }
142 let Some(prev) = self.position_before(self.cursor) else {
143 return false;
144 };
145 self.replace_range(prev, self.cursor, "")
146 }
147
148 pub fn delete_next_char(&mut self) -> bool {
150 if let Some((start, end)) = self.selection() {
151 return self.replace_range(start, end, "");
152 }
153 let Some(next) = self.position_after(self.cursor) else {
154 return false;
155 };
156 self.replace_range(self.cursor, next, "")
157 }
158
159 pub fn delete_prev_word(&mut self) -> bool {
161 if let Some((start, end)) = self.selection() {
162 return self.yank_and_replace(start, end);
163 }
164 let (row, col) = self.cursor;
165 if col == 0 {
166 return self.delete_prev_char();
167 }
168 let chars: Vec<char> = self.lines[row].chars().collect();
169 let target = prev_word_start(&chars, col);
170 self.yank_and_replace((row, target), self.cursor)
171 }
172
173 pub fn delete_next_word(&mut self) -> bool {
175 if let Some((start, end)) = self.selection() {
176 return self.yank_and_replace(start, end);
177 }
178 let (row, col) = self.cursor;
179 let chars: Vec<char> = self.lines[row].chars().collect();
180 if col >= chars.len() {
181 return self.delete_next_char();
182 }
183 let target = next_word_start(&chars, col);
184 self.yank_and_replace(self.cursor, (row, target))
185 }
186
187 pub fn delete_line_by_end(&mut self) -> bool {
191 if let Some((start, end)) = self.selection() {
192 return self.yank_and_replace(start, end);
193 }
194 let (row, col) = self.cursor;
195 let len = self.line_len(row);
196 if col < len {
197 return self.yank_and_replace(self.cursor, (row, len));
198 }
199 if row + 1 < self.lines.len() {
200 return self.replace_range(self.cursor, (row + 1, 0), "");
201 }
202 false
203 }
204
205 pub fn delete_line_by_head(&mut self) -> bool {
209 if let Some((start, end)) = self.selection() {
210 return self.yank_and_replace(start, end);
211 }
212 let (row, col) = self.cursor;
213 if col > 0 {
214 return self.yank_and_replace((row, 0), self.cursor);
215 }
216 if row > 0 {
217 let prev_len = self.line_len(row - 1);
218 return self.replace_range((row - 1, prev_len), self.cursor, "");
219 }
220 false
221 }
222
223 pub fn delete_selection(&mut self) -> bool {
225 match self.selection() {
226 Some((start, end)) => self.replace_range(start, end, ""),
227 None => false,
228 }
229 }
230
231 pub fn copy(&mut self) -> bool {
233 let Some((start, end)) = self.selection() else {
234 return false;
235 };
236 self.yank = self.text_in_range(start, end);
237 self.selection_anchor = None;
238 true
239 }
240
241 pub fn cut(&mut self) -> bool {
243 match self.selection() {
244 Some((start, end)) => self.yank_and_replace(start, end),
245 None => false,
246 }
247 }
248
249 pub fn paste(&mut self) -> bool {
251 if self.yank.is_empty() {
252 return false;
253 }
254 let yank = std::mem::take(&mut self.yank);
255 let inserted = self.insert_str(&yank);
256 self.yank = yank;
257 inserted
258 }
259
260 pub fn undo(&mut self) -> bool {
262 let Some(edit) = self.history.pop_undo() else {
263 return false;
264 };
265 let inverse = edit.inverted();
266 self.apply_without_recording(&inverse);
267 self.history.push_redo(edit);
268 true
269 }
270
271 pub fn redo(&mut self) -> bool {
273 let Some(edit) = self.history.pop_redo() else {
274 return false;
275 };
276 self.apply_without_recording(&edit);
277 self.history.push_undo(edit);
278 true
279 }
280
281 fn apply_without_recording(&mut self, edit: &Edit) {
282 let end = self.position_advanced(edit.start, &edit.removed);
283 self.remove_raw(edit.start, end);
284 self.insert_raw(edit.start, &edit.inserted);
285 self.cursor = self.clamp_position(edit.cursor_after);
286 self.selection_anchor = None;
287 }
288
289 fn position_advanced(&self, (row, col): (usize, usize), text: &str) -> (usize, usize) {
291 if text.is_empty() {
292 return (row, col);
293 }
294 let mut lines = text.split('\n');
295 let first = lines.next().unwrap_or("");
296 let rest: Vec<&str> = lines.collect();
297 if rest.is_empty() {
298 (row, col + char_count(first))
299 } else {
300 (
301 row + rest.len(),
302 char_count(rest.last().expect("rest is non-empty")),
303 )
304 }
305 }
306
307 fn yank_and_replace(&mut self, start: (usize, usize), end: (usize, usize)) -> bool {
308 let text = self.text_in_range(start, end);
309 if text.is_empty() {
310 return false;
311 }
312 self.yank = text;
313 self.replace_range(start, end, "")
314 }
315
316 fn text_in_range(&self, (sr, sc): (usize, usize), (er, ec): (usize, usize)) -> String {
318 if sr == er {
319 let line = &self.lines[sr];
320 let sb = byte_of_char(line, sc);
321 let eb = byte_of_char(line, ec);
322 return line[sb..eb].to_string();
323 }
324 let mut out = self.lines[sr][byte_of_char(&self.lines[sr], sc)..].to_string();
325 for line in &self.lines[sr + 1..er] {
326 out.push('\n');
327 out.push_str(line);
328 }
329 out.push('\n');
330 out.push_str(&self.lines[er][..byte_of_char(&self.lines[er], ec)]);
331 out
332 }
333
334 fn position_before(&self, (row, col): (usize, usize)) -> Option<(usize, usize)> {
337 if col > 0 {
338 Some((row, col - 1))
339 } else if row > 0 {
340 Some((row - 1, self.line_len(row - 1)))
341 } else {
342 None
343 }
344 }
345
346 fn position_after(&self, (row, col): (usize, usize)) -> Option<(usize, usize)> {
349 if col < self.line_len(row) {
350 Some((row, col + 1))
351 } else if row + 1 < self.lines.len() {
352 Some((row + 1, 0))
353 } else {
354 None
355 }
356 }
357
358 pub fn move_cursor(&mut self, movement: CursorMove) -> bool {
360 self.move_cursor_inner(movement, false)
361 }
362
363 pub fn move_cursor_selecting(&mut self, movement: CursorMove) -> bool {
365 self.move_cursor_inner(movement, true)
366 }
367
368 fn move_cursor_inner(&mut self, movement: CursorMove, selecting: bool) -> bool {
369 if selecting {
370 if self.selection_anchor.is_none() {
371 self.selection_anchor = Some(self.cursor);
372 }
373 } else {
374 self.selection_anchor = None;
375 }
376 let Some(target) = self.position_for(movement) else {
377 return false;
378 };
379 let changed = target != self.cursor;
380 self.cursor = target;
381 changed
382 }
383
384 fn position_for(&self, movement: CursorMove) -> Option<(usize, usize)> {
387 let (row, col) = self.cursor;
388 let page = self.viewport.get().height.max(1) as usize;
389 Some(match movement {
390 CursorMove::Forward => self.position_after((row, col))?,
391 CursorMove::Back => self.position_before((row, col))?,
392 CursorMove::Up if self.wraps_now() => self.visual_step(false)?,
393 CursorMove::Down if self.wraps_now() => self.visual_step(true)?,
394 CursorMove::Up => {
395 let target = row.checked_sub(1)?;
396 (target, col.min(self.line_len(target)))
397 }
398 CursorMove::Down => {
399 let target = row + 1;
400 if target >= self.lines.len() {
401 return None;
402 }
403 (target, col.min(self.line_len(target)))
404 }
405 CursorMove::Head => (row, 0),
406 CursorMove::End => (row, self.line_len(row)),
407 CursorMove::Top => (0, 0),
408 CursorMove::Bottom => {
409 let last = self.lines.len() - 1;
410 (last, self.line_len(last))
411 }
412 CursorMove::WordForward => {
413 let chars: Vec<char> = self.lines[row].chars().collect();
414 if col >= chars.len() {
415 self.position_after((row, col))?
416 } else {
417 (row, next_word_start(&chars, col))
418 }
419 }
420 CursorMove::WordBack => {
421 if col == 0 {
422 self.position_before((row, col))?
423 } else {
424 let chars: Vec<char> = self.lines[row].chars().collect();
425 (row, prev_word_start(&chars, col))
426 }
427 }
428 CursorMove::UpBy(n) => {
429 let n = if n == 0 { page } else { n };
430 let target = row.saturating_sub(n);
431 (target, col.min(self.line_len(target)))
432 }
433 CursorMove::DownBy(n) => {
434 let n = if n == 0 { page } else { n };
435 let target = (row + n).min(self.lines.len() - 1);
436 (target, col.min(self.line_len(target)))
437 }
438 CursorMove::Jump(r, c) => self.clamp_position((r, c)),
439 })
440 }
441}