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 #[cfg(test)]
225 pub fn delete_selection(&mut self) -> bool {
226 match self.selection() {
227 Some((start, end)) => self.replace_range(start, end, ""),
228 None => false,
229 }
230 }
231
232 pub fn copy(&mut self) -> bool {
234 let Some((start, end)) = self.selection() else {
235 return false;
236 };
237 self.yank = self.text_in_range(start, end);
238 self.selection_anchor = None;
239 true
240 }
241
242 pub fn cut(&mut self) -> bool {
244 match self.selection() {
245 Some((start, end)) => self.yank_and_replace(start, end),
246 None => false,
247 }
248 }
249
250 pub fn paste(&mut self) -> bool {
252 if self.yank.is_empty() {
253 return false;
254 }
255 let yank = std::mem::take(&mut self.yank);
256 let inserted = self.insert_str(&yank);
257 self.yank = yank;
258 inserted
259 }
260
261 pub fn undo(&mut self) -> bool {
263 let Some(edit) = self.history.pop_undo() else {
264 return false;
265 };
266 let inverse = edit.inverted();
267 self.apply_without_recording(&inverse);
268 self.history.push_redo(edit);
269 true
270 }
271
272 pub fn redo(&mut self) -> bool {
274 let Some(edit) = self.history.pop_redo() else {
275 return false;
276 };
277 self.apply_without_recording(&edit);
278 self.history.push_undo(edit);
279 true
280 }
281
282 fn apply_without_recording(&mut self, edit: &Edit) {
283 let end = self.position_advanced(edit.start, &edit.removed);
284 self.remove_raw(edit.start, end);
285 self.insert_raw(edit.start, &edit.inserted);
286 self.cursor = self.clamp_position(edit.cursor_after);
287 self.selection_anchor = None;
288 }
289
290 fn position_advanced(&self, (row, col): (usize, usize), text: &str) -> (usize, usize) {
292 if text.is_empty() {
293 return (row, col);
294 }
295 let mut lines = text.split('\n');
296 let first = lines.next().unwrap_or("");
297 let rest: Vec<&str> = lines.collect();
298 if rest.is_empty() {
299 (row, col + char_count(first))
300 } else {
301 (
302 row + rest.len(),
303 char_count(rest.last().expect("rest is non-empty")),
304 )
305 }
306 }
307
308 fn yank_and_replace(&mut self, start: (usize, usize), end: (usize, usize)) -> bool {
309 let text = self.text_in_range(start, end);
310 if text.is_empty() {
311 return false;
312 }
313 self.yank = text;
314 self.replace_range(start, end, "")
315 }
316
317 fn text_in_range(&self, (sr, sc): (usize, usize), (er, ec): (usize, usize)) -> String {
319 if sr == er {
320 let line = &self.lines[sr];
321 let sb = byte_of_char(line, sc);
322 let eb = byte_of_char(line, ec);
323 return line[sb..eb].to_string();
324 }
325 let mut out = self.lines[sr][byte_of_char(&self.lines[sr], sc)..].to_string();
326 for line in &self.lines[sr + 1..er] {
327 out.push('\n');
328 out.push_str(line);
329 }
330 out.push('\n');
331 out.push_str(&self.lines[er][..byte_of_char(&self.lines[er], ec)]);
332 out
333 }
334
335 fn position_before(&self, (row, col): (usize, usize)) -> Option<(usize, usize)> {
338 if col > 0 {
339 Some((row, col - 1))
340 } else if row > 0 {
341 Some((row - 1, self.line_len(row - 1)))
342 } else {
343 None
344 }
345 }
346
347 fn position_after(&self, (row, col): (usize, usize)) -> Option<(usize, usize)> {
350 if col < self.line_len(row) {
351 Some((row, col + 1))
352 } else if row + 1 < self.lines.len() {
353 Some((row + 1, 0))
354 } else {
355 None
356 }
357 }
358
359 pub fn move_cursor(&mut self, movement: CursorMove) -> bool {
361 self.move_cursor_inner(movement, false)
362 }
363
364 pub fn move_cursor_selecting(&mut self, movement: CursorMove) -> bool {
366 self.move_cursor_inner(movement, true)
367 }
368
369 fn move_cursor_inner(&mut self, movement: CursorMove, selecting: bool) -> bool {
370 if selecting {
371 if self.selection_anchor.is_none() {
372 self.selection_anchor = Some(self.cursor);
373 }
374 } else {
375 self.selection_anchor = None;
376 }
377 let Some(target) = self.position_for(movement) else {
378 return false;
379 };
380 let changed = target != self.cursor;
381 self.cursor = target;
382 changed
383 }
384
385 fn position_for(&self, movement: CursorMove) -> Option<(usize, usize)> {
388 let (row, col) = self.cursor;
389 let page = self.viewport.get().height.max(1) as usize;
390 Some(match movement {
391 CursorMove::Forward => self.position_after((row, col))?,
392 CursorMove::Back => self.position_before((row, col))?,
393 CursorMove::Up if self.wraps_now() => self.visual_step(false)?,
394 CursorMove::Down if self.wraps_now() => self.visual_step(true)?,
395 CursorMove::Up => {
396 let target = row.checked_sub(1)?;
397 (target, col.min(self.line_len(target)))
398 }
399 CursorMove::Down => {
400 let target = row + 1;
401 if target >= self.lines.len() {
402 return None;
403 }
404 (target, col.min(self.line_len(target)))
405 }
406 CursorMove::Head => (row, 0),
407 CursorMove::End => (row, self.line_len(row)),
408 CursorMove::Top => (0, 0),
409 CursorMove::Bottom => {
410 let last = self.lines.len() - 1;
411 (last, self.line_len(last))
412 }
413 CursorMove::WordForward => {
414 let chars: Vec<char> = self.lines[row].chars().collect();
415 if col >= chars.len() {
416 self.position_after((row, col))?
417 } else {
418 (row, next_word_start(&chars, col))
419 }
420 }
421 CursorMove::WordBack => {
422 if col == 0 {
423 self.position_before((row, col))?
424 } else {
425 let chars: Vec<char> = self.lines[row].chars().collect();
426 (row, prev_word_start(&chars, col))
427 }
428 }
429 CursorMove::UpBy(n) => {
430 let n = if n == 0 { page } else { n };
431 let target = row.saturating_sub(n);
432 (target, col.min(self.line_len(target)))
433 }
434 CursorMove::DownBy(n) => {
435 let n = if n == 0 { page } else { n };
436 let target = (row + n).min(self.lines.len() - 1);
437 (target, col.min(self.line_len(target)))
438 }
439 #[cfg(test)]
440 CursorMove::Jump(r, c) => self.clamp_position((r, c)),
441 })
442 }
443}