From 3acf85f2679a548236796a594b3871799957318d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 26 Sep 2021 10:45:23 +1000 Subject: [PATCH] Restore diff based terminal output optimization when synchronized terminal_synchronized ui_option now also controls this behaviour, update out of date documentation for ui_options as well. As discussed in #4317 --- doc/pages/options.asciidoc | 9 ++-- src/main.cc | 6 ++- src/terminal_ui.cc | 86 ++++++++++++++++++++++++++++++-------- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/doc/pages/options.asciidoc b/doc/pages/options.asciidoc index 0944e84fda..4457abc50b 100644 --- a/doc/pages/options.asciidoc +++ b/doc/pages/options.asciidoc @@ -349,10 +349,6 @@ are exclusively available to built-in options. *terminal_enable_mouse*::: boolean option that enables mouse support - *terminal_change_colors*::: - boolean option that can disable color palette changing if the - terminfo enables it but the terminal does not support it. - *terminal_shift_function_key*::: Function key from which shifted function key start, if the terminal sends F13 for , this should be set to 12. @@ -367,8 +363,9 @@ are exclusively available to built-in options. padding line (defaults to *false*) *terminal_synchronized*::: - if *yes* or *true*, emit iterm2 synchronized output escape sequences - during redraw to reduce flickering (defaults to *false*) + if *yes* or *true*, emit synchronized output escape sequences and + reduce terminal output with sequences that could trigger flickering + if unsynchronized (defaults to *false*) [[startup-info]] *startup_info_version* `int`:: diff --git a/src/main.cc b/src/main.cc index 5b8e1b1877..011c35b0f8 100644 --- a/src/main.cc +++ b/src/main.cc @@ -545,9 +545,11 @@ void register_options() " terminal_status_on_top bool\n" " terminal_set_title bool\n" " terminal_enable_mouse bool\n" - " terminal_change_colors bool\n" + " terminal_synchronized bool\n" " terminal_wheel_scroll_amount int\n" - " terminal_shift_function_key int\n", + " terminal_shift_function_key int\n" + " terminal_padding_char codepoint\n" + " terminal_padding_fill bool\n", UserInterface::Options{}); reg.declare_option("modelinefmt", "format string used to generate the modeline", "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str); diff --git a/src/terminal_ui.cc b/src/terminal_ui.cc index e9519cceba..3ba87b3a7f 100644 --- a/src/terminal_ui.cc +++ b/src/terminal_ui.cc @@ -284,10 +284,6 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer) if (not lines) return; - // iTerm2 "begin synchronized update" sequence - if (synchronized) - writer.write("\033[?2026h"); - if (force) { std::fill_n(hashes.get(), (size_t)size.line, 0); @@ -299,17 +295,9 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer) return (hash_value(line.atoms) << 1) | 1; // ensure non-zero }; - for (int line = 0; line < (int)size.line; ++line) - { - auto hash = hash_line(lines[line]); - if (hash == hashes[line]) - continue; - hashes[line] = hash; - - format_with(writer, "\033[{}H", line + 1); - + auto output_line = [&](const Line& line) { ColumnCount pending_move = 0; - for (auto& [text, skip, face] : lines[line].atoms) + for (auto& [text, skip, face] : line.atoms) { if (text.empty() and skip == 0) continue; @@ -329,11 +317,75 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer) else if (skip > 0) writer.write(String{' ', skip}); } - } + }; - // iTerm2 "end synchronized update" sequence if (synchronized) - writer.write("\033[?2026l"); + { + writer.write("\033[?2026h"); // begin synchronized update + + struct Change { int keep; int add; int del; }; + Vector changes{Change{}}; + auto new_hashes = ArrayView{lines.get(), (size_t)size.line} | transform(hash_line); + for_each_diff(hashes.get(), (int)size.line, + new_hashes.begin(), (int)size.line, + [&changes](DiffOp op, int len) mutable { + switch (op) + { + case DiffOp::Keep: + changes.push_back({len, 0, 0}); + break; + case DiffOp::Add: + changes.back().add += len; + break; + case DiffOp::Remove: + changes.back().del += len; + break; + } + }); + std::copy(new_hashes.begin(), new_hashes.end(), hashes.get()); + + int line = 0; + for (auto& change : changes) + { + line += change.keep; + if (int del = change.del - change.add; del > 0) + { + format_with(writer, "\033[{}H\033[{}M", line + 1, del); + line -= del; + } + line += change.del; + } + + line = 0; + for (auto& change : changes) + { + line += change.keep; + for (int i = 0; i < change.add; ++i) + { + if (int add = change.add - change.del; i == 0 and add > 0) + format_with(writer, "\033[{}H\033[{}L", line + 1, add); + else + format_with(writer, "\033[{}H", line + 1); + + output_line(lines[line++]); + } + } + + writer.write("\033[?2026l"); // end synchronized update + } + else + { + for (int line = 0; line < (int)size.line; ++line) + { + auto hash = hash_line(lines[line]); + if (hash == hashes[line]) + continue; + hashes[line] = hash; + + format_with(writer, "\033[{}H", line + 1); + output_line(lines[line]); + } + } } constexpr int TerminalUI::default_shift_function_key;