1use crate::grid::{Grid, GridCell, GridCellFlags, GridLineFlags};
2use crate::input::mode;
3use crate::input::{CellState, InputEndType, ScreenWriter, COLOUR_DEFAULT};
4use crate::{TerminalPaletteIndex, TerminalPassthrough};
5
6use super::{SavedGrid, Screen, TITLE_STACK_MAX};
7
8fn cursor_backward_tab_steps(n: u32, current: u32) -> u32 {
9 n.max(1).min(current.saturating_add(1))
10}
11
12impl ScreenWriter for Screen {
13 fn collect_add(&mut self, ch: char, cell: &CellState) {
14 self.write_char(ch, cell, false);
15 }
16
17 fn collect_add_with_charset(&mut self, ch: char, cell: &CellState, acs: bool) {
18 self.write_char(ch, cell, acs);
19 }
20
21 fn collect_add_ascii_run(&mut self, bytes: &[u8], cell: &CellState, acs: bool) {
22 if !self.write_plain_ascii_run(bytes, cell, acs) {
23 for &byte in bytes {
24 self.write_char(char::from(byte), cell, acs);
25 }
26 }
27 }
28
29 fn cursor_up(&mut self, n: u32) {
30 self.clear_pending_wrap();
31 let minimum_y = if self.cursor_y >= self.rupper {
34 self.rupper
35 } else {
36 0
37 };
38 self.cursor_y = self.cursor_y.saturating_sub(n).max(minimum_y);
39 }
40
41 fn cursor_down(&mut self, n: u32) {
42 self.clear_pending_wrap();
43 let maximum_y = if self.cursor_y <= self.rlower {
46 self.rlower
47 } else {
48 self.grid.sy().saturating_sub(1)
49 };
50 self.cursor_y = self.cursor_y.saturating_add(n).min(maximum_y);
51 }
52
53 fn cursor_left(&mut self, n: u32) {
54 self.clear_pending_wrap();
55 self.cursor_x = self.cursor_column().saturating_sub(n);
56 }
57
58 fn cursor_right(&mut self, n: u32) {
59 self.clear_pending_wrap();
60 self.cursor_x = self
61 .cursor_column()
62 .saturating_add(n)
63 .min(self.max_cursor_x());
64 }
65
66 fn cursor_move(&mut self, col: i32, row: i32, origin_mode: bool) {
67 self.clear_pending_wrap();
68 let max_x = self.grid.sx().saturating_sub(1);
69 let (min_y, max_y) = if origin_mode && (self.mode & mode::MODE_ORIGIN) != 0 {
70 (self.rupper, self.rlower)
71 } else {
72 (0, self.grid.sy().saturating_sub(1))
73 };
74
75 if col >= 0 {
76 self.cursor_x = (col as u32).min(max_x);
77 }
78 if row >= 0 {
79 self.cursor_y = min_y.saturating_add(row as u32).min(max_y);
80 }
81 }
82
83 fn insert_line(&mut self, n: u32, bg: i32) {
84 self.clear_pending_wrap();
85 if self.cursor_y < self.rupper || self.cursor_y > self.rlower {
86 return;
87 }
88 self.clear_selected_cells();
89
90 let upper = self.cursor_y;
91 let lower = self.rlower;
92 let lines = n.max(1).min(lower.saturating_sub(upper).saturating_add(1));
93 self.grid.insert_lines(upper, lower, lines, bg);
94 }
95
96 fn delete_line(&mut self, n: u32, bg: i32) {
97 self.clear_pending_wrap();
98 if self.cursor_y < self.rupper || self.cursor_y > self.rlower {
99 return;
100 }
101 self.clear_selected_cells();
102
103 let upper = self.cursor_y;
104 let lower = self.rlower;
105 let lines = n.max(1).min(lower.saturating_sub(upper).saturating_add(1));
106 for _ in 0..lines {
107 self.grid.scroll_region_up(upper, lower, bg, false);
108 }
109 }
110
111 fn scroll_up(&mut self, n: u32, bg: i32) {
112 self.clear_pending_wrap();
113 self.clear_selected_cells();
114 let lines = n
115 .max(1)
116 .min(self.rlower.saturating_sub(self.rupper).saturating_add(1));
117 for _ in 0..lines {
118 self.grid
119 .scroll_region_up(self.rupper, self.rlower, bg, self.rupper == 0);
120 }
121 }
122
123 fn scroll_down(&mut self, n: u32, bg: i32) {
124 self.clear_pending_wrap();
125 self.clear_selected_cells();
126 let lines = n
127 .max(1)
128 .min(self.rlower.saturating_sub(self.rupper).saturating_add(1));
129 for _ in 0..lines {
130 self.grid.scroll_region_down(self.rupper, self.rlower, bg);
131 }
132 }
133
134 fn linefeed(&mut self, wrapped: bool, bg: i32) {
135 self.pending_wrap = false;
136 if wrapped {
137 if let Some(line) = self.current_line_mut() {
138 line.set_wrapped(true);
139 }
140 }
141
142 if self.cursor_y == self.rlower {
143 self.clear_selected_cells();
144 self.grid
145 .scroll_region_up(self.rupper, self.rlower, bg, self.rupper == 0);
146 } else if self.cursor_y < self.grid.sy().saturating_sub(1) {
147 self.cursor_y += 1;
148 }
149 }
150
151 fn reverse_index(&mut self, bg: i32) {
152 self.clear_pending_wrap();
153 if self.cursor_y == self.rupper {
154 self.clear_selected_cells();
155 self.grid.scroll_region_down(self.rupper, self.rlower, bg);
156 } else if self.cursor_y > 0 {
157 self.cursor_y -= 1;
158 }
159 }
160
161 fn carriage_return(&mut self) {
162 self.pending_wrap = false;
163 self.cursor_x = 0;
164 }
165
166 fn backspace(&mut self) {
167 self.clear_pending_wrap();
168 let cx = self.cursor_column();
169 if cx > 0 {
170 self.cursor_x = cx - 1;
171 return;
172 }
173 if self.cursor_y == 0 {
174 return;
175 }
176 if self
177 .grid
178 .visible_line(self.cursor_y - 1)
179 .is_some_and(|line| line.flags().contains(GridLineFlags::WRAPPED))
180 {
181 self.cursor_y -= 1;
182 self.cursor_x = self.max_cursor_x();
183 }
184 }
185
186 fn insert_character(&mut self, n: u32, bg: i32) {
187 self.clear_pending_wrap();
188 self.clear_selected_cells();
189 let sx = self.grid.sx();
190 let x = self.logical_insert_column();
191 if x >= sx {
192 self.pending_wrap = (self.mode & crate::input::mode::MODE_WRAP) != 0;
193 return;
194 }
195 self.cursor_x = x;
196 let count = n.max(1).min(sx.saturating_sub(x));
197 let blank = self.blank_cell(bg);
198 if let Some(line) = self.current_line_mut() {
199 line.insert_cells(x, count, &blank);
200 Self::repair_wide_cells_on_line(line, sx, bg);
201 line.touch();
202 }
203 }
204
205 fn delete_character(&mut self, n: u32, bg: i32) {
206 self.clear_pending_wrap();
207 self.clear_selected_cells();
208 let x = self.cursor_column();
209 let sx = self.grid.sx();
210 let count = n.max(1).min(sx.saturating_sub(x));
211 let blank = self.blank_cell(bg);
212 let clears_whole_line = x == 0 && count == sx;
213 if clears_whole_line {
214 self.grid.break_wrap_before_visible_line(self.cursor_y);
215 }
216 if let Some(line) = self.current_line_mut() {
217 line.delete_cells(x, count, &blank);
218 Self::repair_wide_cells_on_line(line, sx, bg);
219 if clears_whole_line {
220 line.set_wrapped(false);
221 }
222 line.touch();
223 }
224 }
225
226 fn clear_character(&mut self, n: u32, bg: i32) {
227 self.clear_pending_wrap();
228 let x = self.cursor_column();
229 let end = x
230 .saturating_add(n.max(1))
231 .saturating_sub(1)
232 .min(self.grid.sx().saturating_sub(1));
233 self.clear_line_range(self.cursor_y, x, end, bg);
234 }
235
236 fn clear_end_of_screen(&mut self, bg: i32) {
237 if self.cursor_y == 0 && self.cursor_column() == 0 {
238 self.clear_selected_cells();
239 self.grid.clear_visible_to_history(COLOUR_DEFAULT);
240 return;
241 }
242 let x = self.cursor_column();
243 if self.cursor_y < self.grid.sy() {
244 self.clear_line_range(self.cursor_y, x, self.grid.sx().saturating_sub(1), bg);
245 }
246 if self.cursor_y + 1 < self.grid.sy() {
247 self.clear_screen_region(
248 self.cursor_y + 1,
249 self.grid.sy().saturating_sub(1),
250 COLOUR_DEFAULT,
251 );
252 }
253 }
254
255 fn clear_start_of_screen(&mut self, bg: i32) {
256 if self.cursor_y > 0 {
257 self.clear_screen_region(0, self.cursor_y - 1, COLOUR_DEFAULT);
258 }
259 self.clear_line_range(self.cursor_y, 0, self.cursor_column(), bg);
260 }
261
262 fn clear_screen(&mut self, _bg: i32) {
263 self.clear_selected_cells();
264 self.grid.clear_visible_to_history(COLOUR_DEFAULT);
265 }
266
267 fn clear_history(&mut self) {
268 self.clear_selected_cells();
269 self.grid.clear_history();
270 }
271
272 fn clear_end_of_line(&mut self, bg: i32) {
273 self.clear_line_range(
274 self.cursor_y,
275 self.cursor_column(),
276 self.grid.sx().saturating_sub(1),
277 bg,
278 );
279 }
280
281 fn clear_start_of_line(&mut self, bg: i32) {
282 self.clear_line_range(self.cursor_y, 0, self.cursor_column(), bg);
283 }
284
285 fn clear_line(&mut self, bg: i32) {
286 self.clear_line_range(self.cursor_y, 0, self.grid.sx().saturating_sub(1), bg);
287 }
288
289 fn mode_set(&mut self, mode_bits: u32) {
290 self.mode |= mode_bits;
291 }
292
293 fn mode_clear(&mut self, mode_bits: u32) {
294 self.mode &= !mode_bits;
295 if (self.mode & mode::MODE_WRAP) == 0 {
296 self.clear_pending_wrap();
297 }
298 }
299
300 fn set_scroll_region(&mut self, top: u32, bottom: u32) {
301 let max_y = self.grid.sy().saturating_sub(1);
302 let top = top.min(max_y);
303 let bottom = bottom.min(max_y);
304 if top >= bottom {
305 return;
306 }
307 self.pending_wrap = false;
308 self.cursor_x = 0;
309 self.cursor_y = 0;
310 self.rupper = top;
311 self.rlower = bottom;
312 }
313
314 fn alternate_on(&mut self, bg: i32, save_cursor: bool) {
315 if !self.alternate_screen_enabled {
316 return;
317 }
318 if self.is_alternate() {
319 return;
320 }
321 self.clear_selected_cells();
322
323 let mut saved_grid = Grid::new(self.grid.size(), 0);
324 saved_grid.replace_visible(self.grid.visible_lines());
325 self.saved_grid = Some(SavedGrid {
326 grid: saved_grid,
327 history_enabled: self.grid.history_enabled(),
328 });
329 if save_cursor {
330 self.saved_cursor_x = Some(self.cursor_x);
331 self.saved_cursor_y = Some(self.cursor_y);
332 self.saved_cursor_pending_wrap = self.pending_wrap;
333 self.saved_state.cx = self.cursor_column();
334 self.saved_state.cy = self.cursor_y;
335 }
336
337 self.grid.clear_visible(bg);
338 self.grid.set_history_enabled(false);
339 self.pending_wrap = false;
340 if !save_cursor || !self.preserve_alternate_screen_cursor {
341 self.cursor_x = 0;
342 self.cursor_y = 0;
343 }
344 }
345
346 fn alternate_off(&mut self, _bg: i32, restore_cursor: bool) {
347 self.clear_selected_cells();
348 let saved_cursor = if restore_cursor {
349 self.saved_cursor_x
350 .zip(self.saved_cursor_y)
351 .map(|(x, y)| (x, y, self.saved_cursor_pending_wrap))
352 } else {
353 None
354 };
355
356 let Some(saved) = self.saved_grid.take() else {
357 if let Some((x, y, pending_wrap)) = saved_cursor {
358 self.restore_cursor_position(x, y, pending_wrap);
359 }
360 return;
361 };
362
363 let current_size = self.grid.size();
364 let saved_size = saved.grid.size();
365 let alternate_cursor = (self.cursor_x, self.cursor_y, self.pending_wrap);
366 self.grid
367 .restore_visible_at_size(saved_size, saved.grid.visible_lines(), COLOUR_DEFAULT);
368 self.grid.set_history_enabled(saved.history_enabled);
369 if let Some((x, y, pending_wrap)) = saved_cursor {
370 self.restore_cursor_position(x, y, pending_wrap);
371 } else {
372 self.restore_cursor_position(
373 alternate_cursor.0,
374 alternate_cursor.1,
375 alternate_cursor.2,
376 );
377 }
378 self.resize(current_size);
379 }
380
381 fn tab(&mut self) {
382 self.clear_pending_wrap();
383 let start = self.cursor_column();
384 let next = ((start + 1) as usize..self.tabs.len())
385 .find(|index| self.tabs[*index])
386 .map(|index| index as u32)
387 .unwrap_or_else(|| self.grid.sx().saturating_sub(1));
388 self.cursor_x = next;
389 }
390
391 fn cursor_backward_tab(&mut self, n: u32) {
392 self.clear_pending_wrap();
393 let mut current = self.cursor_column();
394 for _ in 0..cursor_backward_tab_steps(n, current) {
395 if current == 0 {
396 break;
397 }
398 let previous = (0..current as usize)
399 .rev()
400 .find(|index| self.tabs[*index])
401 .map(|index| index as u32)
402 .unwrap_or(0);
403 current = previous;
404 }
405 self.cursor_x = current;
406 }
407
408 fn set_tab_stop(&mut self) {
409 let column = self.cursor_column() as usize;
410 if let Some(tab) = self.tabs.get_mut(column) {
411 *tab = true;
412 }
413 }
414
415 fn clear_tab_stop(&mut self) {
416 let column = self.cursor_column() as usize;
417 if let Some(tab) = self.tabs.get_mut(column) {
418 *tab = false;
419 }
420 }
421
422 fn clear_all_tab_stops(&mut self) {
423 self.tabs.fill(false);
424 }
425
426 fn set_title(&mut self, title: &str) {
427 if self.title_rename_enabled {
428 Screen::set_title(self, title);
429 }
430 }
431
432 fn set_window_name(&mut self, name: &str) {
433 if self.title_rename_enabled && self.window_name != name {
434 self.window_name = name.to_owned();
435 self.bump_metadata_revision();
436 }
437 }
438
439 fn set_path(&mut self, path: &str) {
440 if self.path != path {
441 self.path = path.to_owned();
442 self.bump_metadata_revision();
443 }
444 }
445
446 fn save_cursor(&mut self) {
447 self.saved_cursor_x = Some(self.cursor_x);
448 self.saved_cursor_y = Some(self.cursor_y);
449 self.saved_cursor_pending_wrap = self.pending_wrap;
450 }
451
452 fn restore_cursor(&mut self) {
453 if let (Some(x), Some(y)) = (self.saved_cursor_x, self.saved_cursor_y) {
454 self.restore_cursor_position(x, y, self.saved_cursor_pending_wrap);
455 }
456 }
457
458 fn alignment_test(&mut self) {
459 self.clear_selected_cells();
460 self.rupper = 0;
461 self.rlower = self.grid.sy().saturating_sub(1);
462 let sx = self.grid.sx();
463 for y in 0..self.grid.sy() {
464 if let Some(line) = self.grid.visible_line_mut(y) {
465 for x in 0..sx {
466 if let Some(cell) = line.cell_mut(x) {
467 *cell = GridCell::from_state(
468 'E',
469 1,
470 &CellState::default(),
471 GridCellFlags::default(),
472 );
473 }
474 }
475 line.set_wrapped(false);
476 line.touch();
477 }
478 }
479 }
480
481 fn full_reset(&mut self) {
482 self.clear_selected_cells();
483 if self.is_alternate() {
484 self.alternate_off(COLOUR_DEFAULT, false);
485 }
486 self.cursor_x = 0;
487 self.cursor_y = 0;
488 self.pending_wrap = false;
489 self.rupper = 0;
490 self.rlower = self.grid.sy().saturating_sub(1);
491 self.mode = mode::MODE_CURSOR | mode::MODE_WRAP | (self.mode & mode::MODE_CRLF);
492 self.grid.clear_visible(COLOUR_DEFAULT);
493 self.reset_tabs();
494 self.title_stack.clear();
495 self.active_hyperlink = 0;
496 self.hyperlinks.reset();
497 self.bump_metadata_revision();
498 }
499
500 fn start_sync(&mut self) {
501 self.mode |= mode::MODE_SYNC;
502 }
503
504 fn stop_sync(&mut self) {
505 self.mode &= !mode::MODE_SYNC;
506 }
507
508 fn set_cursor_style(&mut self, n: u32) {
509 self.cursor_style = n;
510 }
511
512 fn osc_hyperlink(&mut self, data: &str) {
513 let (internal_id, uri) = Self::parse_hyperlink(data);
514 if uri.is_empty() {
515 self.active_hyperlink = 0;
516 self.bump_metadata_revision();
517 return;
518 }
519 self.active_hyperlink = self.hyperlinks.put(&uri, internal_id.as_deref());
520 self.bump_metadata_revision();
521 }
522
523 fn current_hyperlink_id(&self) -> u32 {
524 self.active_hyperlink
525 }
526
527 fn bell(&mut self) {
528 self.bell_count = self.bell_count.saturating_add(1);
529 }
530
531 fn apc_passthrough(&mut self, data: &[u8]) {
532 self.push_terminal_passthrough(TerminalPassthrough::kitty_graphics(
533 self.cursor_x,
534 self.cursor_y,
535 data.to_vec(),
536 ));
537 }
538
539 fn dcs_passthrough(&mut self, data: &[u8]) {
540 self.push_terminal_passthrough(TerminalPassthrough::raw(
541 self.cursor_x,
542 self.cursor_y,
543 data.to_vec(),
544 ));
545 }
546
547 fn sixel_passthrough(&mut self, data: &[u8]) {
548 self.push_terminal_passthrough(TerminalPassthrough::sixel(
549 self.cursor_x,
550 self.cursor_y,
551 data.to_vec(),
552 ));
553 }
554
555 fn screen_size_x(&self) -> u32 {
556 self.grid.sx()
557 }
558
559 fn screen_size_y(&self) -> u32 {
560 self.grid.sy()
561 }
562
563 fn cursor_x(&self) -> u32 {
564 self.cursor_x
565 }
566
567 fn cursor_y(&self) -> u32 {
568 self.cursor_y
569 }
570
571 fn current_mode(&self) -> u32 {
572 self.mode
573 }
574
575 fn push_title(&mut self) {
576 if !self.title_rename_enabled {
577 return;
578 }
579 if self.title_stack.len() >= TITLE_STACK_MAX {
580 let excess = self.title_stack.len() + 1 - TITLE_STACK_MAX;
581 self.title_stack.drain(0..excess);
582 }
583 self.title_stack.push(self.title.clone());
584 self.bump_metadata_revision();
585 }
586
587 fn pop_title(&mut self) {
588 if !self.title_rename_enabled {
589 return;
590 }
591 if let Some(title) = self.title_stack.pop() {
592 self.title = title;
593 self.bump_metadata_revision();
594 }
595 }
596
597 fn osc_palette(&mut self, data: &str, _end: InputEndType) {
598 let mut fields = data.split(';');
605 while let Some(index) = fields.next() {
606 let Some(value) = fields.next() else {
607 break;
608 };
609 if value != "?" {
610 continue;
611 }
612 let Some(index) = TerminalPaletteIndex::parse(index) else {
613 continue;
614 };
615 self.push_terminal_passthrough(TerminalPassthrough::palette_query(index));
616 }
617 }
618 fn osc_notification(&mut self, _data: &str) {}
619 fn osc_fg_colour(&mut self, _data: &str, _end: InputEndType) {}
620 fn osc_bg_colour(&mut self, _data: &str, _end: InputEndType) {}
621 fn osc_cursor_colour(&mut self, _data: &str, _end: InputEndType) {}
622 fn osc_clipboard(&mut self, data: &str, end: InputEndType) {
623 let mut sequence = Vec::with_capacity(data.len() + 7);
624 sequence.extend_from_slice(b"\x1b]52;");
625 sequence.extend_from_slice(data.as_bytes());
626 match end {
627 InputEndType::Bel => sequence.push(b'\x07'),
628 InputEndType::St => sequence.extend_from_slice(b"\x1b\\"),
629 }
630 if let Some((selection, payload)) = data.split_once(';') {
631 if payload == "?" {
632 self.push_terminal_passthrough(TerminalPassthrough::clipboard_query(
633 crate::TerminalClipboardQuery::new(selection, end),
634 sequence,
635 ));
636 return;
637 }
638 }
639 self.push_terminal_passthrough(TerminalPassthrough::clipboard(sequence));
640 }
641 fn osc_reset_palette(&mut self, _data: &str) {}
642 fn osc_reset_fg(&mut self) {}
643 fn osc_reset_bg(&mut self) {}
644 fn osc_reset_cursor(&mut self) {}
645 fn osc_shell_integration(&mut self, _data: &str) {}
646}
647
648#[cfg(test)]
649mod tests {
650 #[test]
651 fn cursor_backward_tab_steps_are_bounded_by_cursor_column() {
652 assert_eq!(super::cursor_backward_tab_steps(0, 0), 1);
653 assert_eq!(super::cursor_backward_tab_steps(1, 7), 1);
654 assert_eq!(super::cursor_backward_tab_steps(u32::MAX, 7), 8);
655 }
656}