6 #include "ElasticTabs.h"
8 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
14 cursor_enabled(true), use_bracketed_paste(false),
15 application_cursor_keys(false),
18 at_end_of_line
= true;
20 first_line
= last_line
= first_line_index
= 0;
23 lines
= new Line
*[capacity
];
24 for (int64_t i
= 0; i
< capacity
; ++i
)
26 lines
[0] = new Line();
29 alternate_screen_top_line
= -1;
30 current_elastic_tabs
= nullptr;
31 g0_character_set
= 'B';
33 auto_wrap
= settings
.default_auto_wrap
;
34 characters_per_line
= 80;
40 for (int i
= 0; i
< capacity
; ++i
)
44 if (current_elastic_tabs
)
45 current_elastic_tabs
->release();
49 void History::set_lines_on_screen(int new_lines_on_screen
)
51 if (is_in_alternate_screen()) {
52 // We assume the "alternate screen" is a "full screen" mode. Make sure
53 // it's got the exact correct number of lines.
54 if (new_lines_on_screen
> lines_on_screen
) {
56 int delta
= new_lines_on_screen
- lines_on_screen
;
57 for (int i
= delta
; i
> 0; --i
)
59 if (bottom_margin
>= 0)
60 bottom_margin
+= delta
;
63 // (Potentially) deleting lines.
64 last_line
-= lines_on_screen
- new_lines_on_screen
;
65 if (current_line
> last_line
)
66 current_line
= last_line
;
70 lines_on_screen
= new_lines_on_screen
;
74 void History::set_characters_per_line(int new_characters_per_line
)
76 characters_per_line
= new_characters_per_line
;
80 int64_t History::num_lines()
86 int History::add_input(const char* input
, int length
)
88 const char* p
= input
;
89 const char* end
= input
+ length
;
93 const char* run_start
= p
;
98 case '\x11': // DC1 / XON
100 case '\x13': // DC3 / XOFF
102 // Ignore all of these.
110 if (c
>= 0x40 && c
<= 0x5F) {
111 // "Fe" escape sequence.
112 const char* parse_end
;
115 parse_end
= parse_csi(p
, end
);
116 if (parse_end
== nullptr)
121 parse_end
= parse_dcs(p
, end
);
122 if (parse_end
== nullptr)
127 parse_end
= parse_osc(p
, end
);
128 if (parse_end
== nullptr)
136 parse_end
= parse_st_string(p
, end
);
137 if (parse_end
== nullptr)
140 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
141 printf("- Unimplemented escape: %c%.*s\n", c
, (int) (parse_end
- p
), p
);
147 if (current_line
== calc_screen_top_line() + top_margin
)
155 // We assume only one character follows the ESC.
156 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
157 printf("- Unimplemented escape: %c.\n", c
);
162 else if (c
>= 0x60 && c
<= 0x7E) {
163 // "Fs" escape sequence.
164 // Not currently implemented. We assume only one character
167 else if (c
>= 0x30 && c
<= 0x3F) {
168 // "Fp" escape sequence ("private use").
169 // We assume only one character follows the ESC.
171 else if (c
>= 0x20 && c
<= 0x2F) {
172 // "nF" escape sequence.
177 if (c
>= 0x30 && c
<= 0x7E)
179 else if (c
< 0x20 || c
> 0x2F) {
180 // Not valid. We'll just terminate the escape sequence there.
184 run_start
+= 1; // Skip the ESC.
185 switch (run_start
[0]) {
187 if (p
> run_start
+ 1) {
188 g0_character_set
= run_start
[1];
189 current_style
.line_drawing
= (g0_character_set
== '0');
194 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
195 printf("- Unimplemented escape: %.*s\n", (int) (p
- run_start
), run_start
);
204 at_end_of_line
= false;
212 if (current_column
> 0) {
214 at_end_of_line
= false;
220 Line
* cur_line
= line(current_line
);
221 if (at_end_of_line
) {
222 cur_line
->append_tab(current_style
);
223 // Just need to make sure "current_elastic_tabs" gets enough
228 cur_line
->replace_character_with_tab(current_column
, current_style
);
229 // Could be splitting a column. Trigger a full recalculation of
231 characters_deleted();
241 // Normal run of characters.
242 // TODO: Don't consume partial UTF8 character at end.
244 unsigned char c
= *p
;
245 if (c
< ' ' || c
== '\x7F')
249 // Add to current line.
250 if (g0_character_set
== '0') {
251 // DEC Special Character and Line Drawing Set.
252 std::string translated_chars
= translate_line_drawing_chars(run_start
, p
);
254 translated_chars
.data(),
255 translated_chars
.data() + translated_chars
.size());
258 add_characters(run_start
, p
);
262 return run_start
- input
;
271 void History::add_characters(const char* start
, const char* end
)
274 int num_new_characters
= UTF8::num_characters(start
, end
- start
);
275 while (current_column
+ num_new_characters
> characters_per_line
) {
276 int chars_to_add
= characters_per_line
- current_column
;
277 if (at_end_of_line
|| !insert_mode
) {
278 int num_bytes
= UTF8::bytes_for_n_characters(start
, end
- start
, chars_to_add
);
279 add_to_current_line(start
, start
+ num_bytes
);
283 update_at_end_of_line();
284 num_new_characters
-= chars_to_add
;
288 // TODO: split the line.
289 // Instead, for now, we just insert the characters.
290 add_to_current_line(start
, end
);
297 add_to_current_line(start
, end
);
301 void History::add_to_current_line(const char* start
, const char* end
)
303 Line
* cur_line
= line(current_line
);
305 cur_line
->append_characters(start
, end
- start
, current_style
);
306 else if (insert_mode
) {
307 cur_line
->insert_characters(
308 current_column
, start
, end
- start
, current_style
);
311 cur_line
->replace_characters(
312 current_column
, start
, end
- start
, current_style
);
314 current_column
+= UTF8::num_characters(start
, end
- start
);
317 update_at_end_of_line();
321 void History::next_line()
324 (top_margin
> 0 && current_line
>= last_line
) ||
325 (bottom_margin
>= 0 &&
326 current_line
== calc_screen_top_line() + bottom_margin
);
328 int64_t screen_top
= calc_screen_top_line();
329 scroll_up(screen_top
+ top_margin
, screen_top
+ bottom_margin
, 1);
330 update_at_end_of_line();
332 else if (current_line
>= last_line
)
336 Line
* cur_line
= line(current_line
);
337 if (cur_line
->elastic_tabs
)
338 cur_line
->elastic_tabs
->release();
339 cur_line
->elastic_tabs
= current_elastic_tabs
;
340 if (current_elastic_tabs
)
341 current_elastic_tabs
->acquire();
342 update_at_end_of_line();
347 void History::new_line()
350 current_line
= last_line
;
351 line(current_line
)->elastic_tabs
= current_elastic_tabs
;
352 if (current_elastic_tabs
)
353 current_elastic_tabs
->acquire();
354 at_end_of_line
= true;
358 void History::allocate_new_line()
361 if (last_line
>= capacity
) {
362 // History is full, we'll recycle the previous first line if necessary.
363 // (It might not be necessary because window resizing can delete lines
365 int last_line_index
= line_index(last_line
);
366 Line
* line
= lines
[last_line_index
];
369 // If we took "first_line", update that.
370 if (last_line_index
== first_line_index
) {
372 first_line_index
+= 1;
373 if (first_line_index
>= capacity
)
374 first_line_index
= 0;
378 // We may not have allocated the Line yet.
379 if (lines
[last_line
] == nullptr)
380 lines
[last_line
] = new Line();
382 lines
[last_line
]->fully_clear();
387 void History::ensure_current_line()
389 while (last_line
< current_line
)
394 void History::ensure_current_column()
396 // If we moved beyond the end of the line, add some spaces.
397 Line
* cur_line
= line(current_line
);
398 int cur_length
= cur_line
->num_characters();
399 if (current_column
> cur_length
) {
401 cur_line
->append_spaces(current_column
- cur_length
, default_style
);
402 at_end_of_line
= true;
404 else if (current_column
== cur_length
)
405 at_end_of_line
= true;
409 void History::update_at_end_of_line()
411 at_end_of_line
= (current_column
>= line(current_line
)->num_characters());
415 const char* History::parse_csi(const char* p
, const char* end
)
417 // This, like the other parse_*() functions, returns NULL if the escape
418 // sequence is incomplete. Otherwise, it returns a pointer to the byte
419 // after the escape sequence.
422 #if defined(PRINT_UNIMPLEMENTED_ESCAPES) || defined(DUMP_CSIS)
423 const char* escape_start
= p
;
428 p
= args
.parse(p
, end
);
432 // "Intermediate bytes".
438 if (c
>= 0x20 && c
<= 0x2F)
444 // Last character tells us what to do.
448 if (args
.private_code_type
== 0)
451 // Insert blank characters (ICH).
453 int num_blanks
= args
.args
[0] ? args
.args
[0] : 1;
454 std::string
blanks(num_blanks
, ' ');
455 line(current_line
)->insert_characters(
456 current_column
, blanks
.data(), num_blanks
, current_style
);
457 at_end_of_line
= false;
464 // Cursor up (CUU) / Cursor Prev Line (CPL).
466 current_line
-= args
.args
[0] ? args
.args
[0] : 1;
467 int64_t screen_top_line
= calc_screen_top_line();
468 if (is_in_alternate_screen()) {
469 if (current_line
< screen_top_line
)
470 current_line
= screen_top_line
;
473 if (current_line
< first_line
)
474 current_line
= first_line
;
478 update_at_end_of_line();
485 // Cursor down (CUD) / Cursor Next Line (CNL) / Line Position Relative
488 current_line
+= args
.args
[0] ? args
.args
[0] : 1;
489 int64_t screen_bottom_line
= calc_screen_bottom_line();
490 if (current_line
> screen_bottom_line
)
491 current_line
= screen_bottom_line
;
494 ensure_current_line();
495 update_at_end_of_line();
502 current_column
+= args
.args
[0] ? args
.args
[0] : 1;
503 ensure_current_column();
509 current_column
-= args
.args
[0] ? args
.args
[0] : 1;
510 if (current_column
< 0)
512 at_end_of_line
= false;
516 // Cursor Character Absolute (CHA).
517 current_column
= args
.args
[0] ? args
.args
[0] - 1 : 0;
518 ensure_current_column();
519 update_at_end_of_line();
525 calc_screen_top_line() + (args
.args
[0] ? args
.args
[0] - 1 : 0);
526 current_column
= args
.args
[1] ? args
.args
[1] - 1 : 0;
527 ensure_current_line();
528 ensure_current_column();
529 update_at_end_of_line();
534 if (args
.args
[0] == 0)
535 clear_to_end_of_screen();
536 else if (args
.args
[0] == 1)
537 clear_to_beginning_of_screen();
538 else if (args
.args
[0] == 2 || args
.args
[0] == 3)
540 update_at_end_of_line();
546 Line
* cur_line
= line(current_line
);
547 if (args
.args
[0] == 0) {
548 cur_line
->clear_to_end_from(current_column
);
549 at_end_of_line
= true;
551 else if (args
.args
[0] == 1) {
552 cur_line
->clear_from_beginning_to(current_column
);
553 cur_line
->prepend_spaces(current_column
, current_style
);
554 update_at_end_of_line();
556 else if (args
.args
[0] == 2) {
558 if (current_column
> 0)
559 cur_line
->prepend_spaces(current_column
, current_style
);
560 at_end_of_line
= true;
562 characters_deleted();
567 // Insert blank lines (IL).
568 insert_lines(args
.args
[0] ? args
.args
[0] : 1);
572 // Delete lines (DL).
573 delete_lines(args
.args
[0] ? args
.args
[0] : 1);
577 // Delete Character (DCH).
578 line(current_line
)->delete_characters(current_column
, args
.args
[0] ? args
.args
[0] : 1);
579 update_at_end_of_line();
580 characters_deleted();
585 // This scrolls the whole screen (or at least the scrolling region).
587 int effective_bottom_margin
= bottom_margin
;
588 if (effective_bottom_margin
< 0)
589 effective_bottom_margin
= lines_on_screen
- 1;
590 int64_t top_line
= calc_screen_top_line();
592 top_line
+ top_margin
, top_line
+ effective_bottom_margin
,
593 args
.args
[0] ? args
.args
[0] : 1);
600 args
.args
[0] ? args
.args
[0] : 1,
601 calc_screen_top_line() + top_margin
);
605 // Erase Character(s) (ECH).
606 // This appears to mean replacing them with spaces, unlike DCH which
607 // actually deletes characters.
609 int num_blanks
= args
.args
[0] ? args
.args
[0] : 1;
610 std::string
blanks(num_blanks
, ' ');
611 line(current_line
)->replace_characters(
612 current_column
, blanks
.data(), num_blanks
, current_style
);
613 at_end_of_line
= false;
614 characters_deleted();
619 // Line Position Absolute (VPA).
622 calc_screen_top_line() + (args
.args
[0] ? args
.args
[0] - 1 : 0);
623 int64_t top_line
= calc_screen_top_line();
624 if (current_line
< top_line
)
625 current_line
= top_line
;
627 int64_t bottom_line
= calc_screen_bottom_line();
628 if (current_line
> bottom_line
)
629 current_line
= bottom_line
;
631 ensure_current_line();
632 ensure_current_column();
633 update_at_end_of_line();
638 switch (args
.args
[0]) {
648 switch (args
.args
[0]) {
658 // Select Graphic Rendition (SGR).
659 if (args
.num_args
== 0) {
660 // Default to at least one arg (which will have the default value
664 for (int which_arg
= 0; which_arg
< args
.num_args
; ++which_arg
) {
665 switch (args
.args
[which_arg
]) {
667 current_style
.reset();
668 if (g0_character_set
== '0')
669 current_style
.line_drawing
= true;
672 current_style
.bold
= true;
675 current_style
.italic
= true;
678 current_style
.underlined
= true;
681 current_style
.inverse
= true;
684 current_style
.invisible
= true;
687 current_style
.crossed_out
= true;
690 current_style
.doubly_underlined
= true;
693 current_style
.bold
= false;
696 current_style
.italic
= false;
699 current_style
.underlined
= current_style
.doubly_underlined
= false;
702 current_style
.inverse
= false;
705 current_style
.invisible
= false;
708 current_style
.crossed_out
= false;
710 case 30: case 31: case 32: case 33:
711 case 34: case 35: case 36: case 37:
712 // Set foreground color.
713 current_style
.foreground_color
= args
.args
[which_arg
] - 30;
715 case 90: case 91: case 92: case 93:
716 case 94: case 95: case 96: case 97:
717 // Set high-intensity foreground color.
718 current_style
.foreground_color
= args
.args
[which_arg
] - 90 + 8;
721 // Set foreground color.
723 if (args
.args
[which_arg
] == 5) {
725 current_style
.foreground_color
= args
.args
[which_arg
];
727 else if (args
.args
[which_arg
] == 2) {
728 current_style
.foreground_color
=
729 Colors::true_color_bit
|
730 args
.args
[which_arg
+ 1] << 16 |
731 args
.args
[which_arg
+ 2] << 8 |
732 args
.args
[which_arg
+ 3];
736 case 40: case 41: case 42: case 43:
737 case 44: case 45: case 46: case 47:
738 // Set background color.
739 current_style
.background_color
= args
.args
[which_arg
] - 40;
741 case 100: case 101: case 102: case 103:
742 case 104: case 105: case 106: case 107:
743 // Set high-intensity background color.
744 current_style
.background_color
= args
.args
[which_arg
] - 100 + 8;
747 // Set background color.
749 if (args
.args
[which_arg
] == 5) {
751 current_style
.background_color
= args
.args
[which_arg
];
753 else if (args
.args
[which_arg
] == 2) {
754 current_style
.background_color
=
755 Colors::true_color_bit
|
756 args
.args
[which_arg
+ 1] << 16 |
757 args
.args
[which_arg
+ 2] << 8 |
758 args
.args
[which_arg
+ 3];
767 if (args
.args
[0] == 6) {
768 // Device Status Report (DSR).
771 report
, "\x1B[%d;%dR",
772 (int) (current_line
- calc_screen_top_line() + 1),
774 terminal
->send(report
);
781 // Set scroll margins (DECSTBM).
782 top_margin
= args
.args
[0] ? args
.args
[0] - 1 : 0;
783 bottom_margin
= args
.args
[1] ? args
.args
[1] - 1 : -1;
784 if (top_margin
>= bottom_margin
) {
785 // Invalid; reset them.
793 // This is either unimplemented or invalid.
794 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
795 printf("- Unimplemented CSI: %.*s\n", (int) (p
- escape_start
), escape_start
);
801 else if (args
.private_code_type
== '?') {
805 set_private_modes(&args
, true);
810 set_private_modes(&args
, false);
814 // This is either unimplemented or invalid.
815 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
816 printf("- Unimplemented CSI: %.*s\n", (int) (p
- escape_start
), escape_start
);
823 printf("- %.*s\n", (int) (p
- escape_start
), escape_start
);
829 const char* History::parse_dcs(const char* p
, const char* end
)
831 const char* sequence_end
= parse_st_string(p
, end
);
832 if (sequence_end
== nullptr)
836 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
837 printf("- Unimplemented DCS: %.*s\n", (int) (sequence_end
- p
), p
);
843 const char* History::parse_osc(const char* p
, const char* end
)
845 const char* sequence_end
= parse_st_string(p
, end
, true);
846 if (sequence_end
== nullptr)
850 #ifdef PRINT_UNIMPLEMENTED_ESCAPES
851 printf("- Unimplemented OSC: %.*s\n", (int) (sequence_end
- p
), p
);
857 const char* History::parse_st_string(const char* p
, const char* end
, bool can_end_with_bel
)
865 // Got ST; string is complete.
869 else if (c
== '\a' && can_end_with_bel
)
873 // Incomplete string.
878 void History::set_private_modes(Arguments
* args
, bool set
)
880 for (int i
= 0; i
< args
->num_args
; ++i
) {
881 switch (args
->args
[i
]) {
884 application_cursor_keys
= set
;
893 // We don't support this currently, so we're ignoring it.
897 // Show cursor (DECTCEM).
898 cursor_enabled
= set
;
904 enter_alternate_screen();
906 exit_alternate_screen();
910 use_bracketed_paste
= set
;
915 start_elastic_tabs();
921 start_elastic_tabs(args
->args
[++i
]);
923 end_elastic_tabs(args
->args
[++i
] == 1);
927 printf("- Unimplemented set mode: ?%d\n", args
->args
[i
]);
934 int64_t History::calc_screen_top_line()
936 int screen_top_line
= last_line
- lines_on_screen
+ 1;
937 if (screen_top_line
< 0)
939 return screen_top_line
;
943 int64_t History::calc_screen_bottom_line()
945 // Usually "last_line", except early on...
946 if (last_line
< lines_on_screen
)
947 return lines_on_screen
- 1;
952 void History::clear_to_end_of_screen()
954 line(current_line
)->clear_to_end_from(current_column
);
955 for (int64_t which_line
= current_line
+ 1; which_line
<= last_line
; ++which_line
)
956 line(which_line
)->clear();
960 void History::clear_to_beginning_of_screen()
962 Line
* cur_line
= line(current_line
);
963 cur_line
->clear_from_beginning_to(current_column
);
964 cur_line
->prepend_spaces(current_column
, current_style
);
965 for (int64_t which_line
= calc_screen_top_line(); which_line
< current_line
; ++which_line
)
966 line(which_line
)->clear();
970 void History::clear_screen()
972 for (int64_t which_line
= calc_screen_top_line(); which_line
<= last_line
; ++which_line
)
973 line(which_line
)->clear();
977 void History::insert_lines(int num_lines
)
979 scroll_down(num_lines
, current_line
);
983 void History::scroll_down(int num_lines
, int64_t top_scroll_line
)
985 int64_t bottom_scroll_line
=
987 calc_screen_bottom_line() :
988 calc_screen_top_line() + bottom_margin
;
989 int max_scroll
= bottom_scroll_line
- top_scroll_line
+ 1;
990 if (num_lines
> max_scroll
)
991 num_lines
= max_scroll
;
993 // Move and erase the lines.
994 for (int dest_line
= bottom_scroll_line
; dest_line
>= top_scroll_line
; --dest_line
) {
995 int dest_index
= line_index(dest_line
);
996 int64_t src_line
= dest_line
- num_lines
;
997 if (src_line
< top_scroll_line
) {
998 if (lines
[dest_index
])
999 lines
[dest_index
]->clear();
1002 int src_index
= line_index(src_line
);
1003 delete lines
[dest_index
];
1004 lines
[dest_index
] = lines
[src_index
];
1005 lines
[src_index
] = new Line();
1009 update_at_end_of_line();
1013 void History::delete_lines(int num_lines
)
1015 if (is_in_alternate_screen() || bottom_margin
>= 0) {
1016 // Scrolling a particular area.
1017 int effective_bottom_margin
= bottom_margin
;
1018 if (effective_bottom_margin
< 0) {
1019 // Must be in alternate screen.
1020 effective_bottom_margin
= lines_on_screen
- 1;
1023 current_line
, calc_screen_top_line() + effective_bottom_margin
,
1027 scroll_up(current_line
, last_line
, num_lines
);
1028 last_line
-= num_lines
;
1031 update_at_end_of_line();
1035 void History::scroll_up(int64_t top_scroll_line
, int64_t bottom_scroll_line
, int num_lines
)
1037 // Move and erase the lines.
1038 for (int dest_line
= top_scroll_line
; dest_line
<= bottom_scroll_line
; ++dest_line
) {
1039 int dest_index
= line_index(dest_line
);
1040 int64_t src_line
= dest_line
+ num_lines
;
1041 if (src_line
> bottom_scroll_line
) {
1042 if (lines
[dest_index
])
1043 lines
[dest_index
]->clear();
1046 int src_index
= line_index(src_line
);
1047 delete lines
[dest_index
];
1048 lines
[dest_index
] = lines
[src_index
];
1049 lines
[src_index
] = new Line();
1055 void History::enter_alternate_screen()
1057 if (alternate_screen_top_line
>= 0)
1060 // mlterm replaces the bottom lines with the alternate screen, but we add
1061 // new lines instead. This allows you to see the entire main screen when
1062 // scrolling back. We don't save the alternate screen after exiting it.
1065 main_screen_current_line
= current_line
;
1066 main_screen_current_column
= current_column
;
1067 main_screen_top_margin
= top_margin
;
1068 main_screen_bottom_margin
= bottom_margin
;
1070 // Create the new lines.
1071 alternate_screen_top_line
= last_line
+ 1;
1073 for (int i
= 0; i
< lines_on_screen
; ++i
)
1074 allocate_new_line();
1077 current_line
= alternate_screen_top_line
;
1081 update_at_end_of_line();
1085 void History::exit_alternate_screen()
1087 if (alternate_screen_top_line
< 0)
1090 // Delete the last screen's lines.
1091 last_line
= alternate_screen_top_line
- 1;
1094 current_line
= main_screen_current_line
;
1095 current_column
= main_screen_current_column
;
1096 top_margin
= main_screen_top_margin
;
1097 bottom_margin
= main_screen_bottom_margin
;
1098 alternate_screen_top_line
= -1;
1099 update_at_end_of_line();
1104 void History::start_elastic_tabs(int num_right_columns
)
1108 current_elastic_tabs
= new ElasticTabs(num_right_columns
);
1109 current_elastic_tabs
->acquire();
1110 Line
* cur_line
= line(current_line
);
1111 if (cur_line
->elastic_tabs
)
1112 cur_line
->elastic_tabs
->release();
1113 cur_line
->elastic_tabs
= current_elastic_tabs
;
1114 current_elastic_tabs
->acquire();
1118 void History::end_elastic_tabs(bool include_current_line
)
1120 if (current_elastic_tabs
== nullptr)
1123 // The current cursor line will not be part of the group of elastic tabbed
1124 // lines (unless include_current_line is true).
1125 if (!include_current_line
) {
1126 Line
* cur_line
= line(current_line
);
1127 if (cur_line
->elastic_tabs
== current_elastic_tabs
) {
1128 current_elastic_tabs
->release();
1129 cur_line
->elastic_tabs
= nullptr;
1132 current_elastic_tabs
->release();
1133 current_elastic_tabs
= nullptr;
1137 void History::characters_added()
1139 if (current_elastic_tabs
== nullptr)
1142 current_elastic_tabs
->is_dirty
= true;
1143 if (current_line
< current_elastic_tabs
->first_dirty_line
)
1144 current_elastic_tabs
->first_dirty_line
= current_line
;
1148 void History::characters_deleted()
1150 if (current_elastic_tabs
== nullptr)
1153 current_elastic_tabs
->is_dirty
= true;
1154 current_elastic_tabs
->first_dirty_line
= 0;
1158 const char* History::Arguments::parse(const char* p
, const char* end
)
1164 for (int i
= 0; i
< max_args
; ++i
)
1166 private_code_type
= 0;
1169 bool arg_started
= false;
1174 if (c
>= '0' && c
<= '9') {
1175 if (num_args
< max_args
) {
1176 args
[num_args
] *= 10;
1177 args
[num_args
] += c
- '0';
1182 else if (c
== ';') {
1184 arg_started
= false;
1187 else if (c
== '?' || c
== '<' || c
== '=' || c
== '>') {
1188 private_code_type
= c
;
1191 else if (c
>= 0x30 && c
<= 0x3F) {
1192 // Valid, but we ignore it.
1205 std::string
History::translate_line_drawing_chars(const char* start
, const char* end
)
1207 static const char* translation
[] = {
1208 "\u2518", "\u2510", "\u250C", "\u2514", "\u253C", "",
1209 "", "\u2500", "", "", "\u251C", "\u2524", "\u2534", "\u252C", "\u2502",
1212 std::stringstream result
;
1213 for (const char* p
= start
; p
< end
; ++p
) {
1215 if (c
>= 0x6A && c
<= 0x78)
1216 result
<< translation
[c
- 0x6A];
1220 return result
.str();