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
This commit is contained in:
Maxime Coste 2021-09-26 10:45:23 +10:00
parent 1456431951
commit 3acf85f267
3 changed files with 76 additions and 25 deletions

View File

@ -349,10 +349,6 @@ are exclusively available to built-in options.
*terminal_enable_mouse*::: *terminal_enable_mouse*:::
boolean option that enables mouse support 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*::: *terminal_shift_function_key*:::
Function key from which shifted function key start, if the Function key from which shifted function key start, if the
terminal sends F13 for <s-F1>, this should be set to 12. terminal sends F13 for <s-F1>, this should be set to 12.
@ -367,8 +363,9 @@ are exclusively available to built-in options.
padding line (defaults to *false*) padding line (defaults to *false*)
*terminal_synchronized*::: *terminal_synchronized*:::
if *yes* or *true*, emit iterm2 synchronized output escape sequences if *yes* or *true*, emit synchronized output escape sequences and
during redraw to reduce flickering (defaults to *false*) reduce terminal output with sequences that could trigger flickering
if unsynchronized (defaults to *false*)
[[startup-info]] [[startup-info]]
*startup_info_version* `int`:: *startup_info_version* `int`::

View File

@ -545,9 +545,11 @@ void register_options()
" terminal_status_on_top bool\n" " terminal_status_on_top bool\n"
" terminal_set_title bool\n" " terminal_set_title bool\n"
" terminal_enable_mouse bool\n" " terminal_enable_mouse bool\n"
" terminal_change_colors bool\n" " terminal_synchronized bool\n"
" terminal_wheel_scroll_amount int\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{}); UserInterface::Options{});
reg.declare_option("modelinefmt", "format string used to generate the modeline", 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); "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str);

View File

@ -284,10 +284,6 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
if (not lines) if (not lines)
return; return;
// iTerm2 "begin synchronized update" sequence
if (synchronized)
writer.write("\033[?2026h");
if (force) if (force)
{ {
std::fill_n(hashes.get(), (size_t)size.line, 0); 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 return (hash_value(line.atoms) << 1) | 1; // ensure non-zero
}; };
for (int line = 0; line < (int)size.line; ++line) auto output_line = [&](const Line& line) {
{
auto hash = hash_line(lines[line]);
if (hash == hashes[line])
continue;
hashes[line] = hash;
format_with(writer, "\033[{}H", line + 1);
ColumnCount pending_move = 0; 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) if (text.empty() and skip == 0)
continue; continue;
@ -329,11 +317,75 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
else if (skip > 0) else if (skip > 0)
writer.write(String{' ', skip}); writer.write(String{' ', skip});
} }
} };
// iTerm2 "end synchronized update" sequence
if (synchronized) if (synchronized)
writer.write("\033[?2026l"); {
writer.write("\033[?2026h"); // begin synchronized update
struct Change { int keep; int add; int del; };
Vector<Change> 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; constexpr int TerminalUI::default_shift_function_key;