Use Unicode symbols for rendering control characters in pager

The history pager will show multiline commands in single-line cells.
We escape newline characters as \\n but that looks awkward if the next line
starts with a letter. Let's render control characters using their corresponding
symbol from the Control Pictures Unicode block.

This means there is also no need to escape backslashes, which further improves
the history pager - now the rendering has exactly as many backslashes as
the eventual command.

This means that (multiline) commands in the history pager will be rendered
with the same amount of characters as are in the actual command (unless
they contain funny nonprintables).  This makes it easy for the next commit
to highlight multiline commands correctly in the history pager.

The font size for these symbols (for example ␉) is quite small, but that's
okay since for the proposed uses it's not so important that they readable.
The important thing is that the stand out from surrounding text.
This commit is contained in:
Johannes Altmanninger 2022-07-25 19:18:53 +02:00
parent 02fcc50b9a
commit b64cec1d7e
3 changed files with 37 additions and 18 deletions

View file

@ -883,6 +883,8 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring
const bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED);
const bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE);
const bool no_qmark = feature_test(features_t::qmark_noglob);
const bool symbolic = static_cast<bool>(flags & ESCAPE_SYMBOLIC) && (MB_CUR_MAX > 1);
assert((!symbolic || !escape_printables) && "symbolic implies escape-no-printables");
bool need_escape = false;
bool need_complex_escape = false;
@ -911,47 +913,57 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring
wchar_t c = *in;
switch (c) {
case L'\t': {
out += L'\\';
out += L't';
if (symbolic)
out += L'';
else
out += L"\\t";
need_escape = need_complex_escape = true;
break;
}
case L'\n': {
out += L'\\';
out += L'n';
if (symbolic)
out += L'';
else
out += L"\\n";
need_escape = need_complex_escape = true;
break;
}
case L'\b': {
out += L'\\';
out += L'b';
if (symbolic)
out += L'';
else
out += L"\\b";
need_escape = need_complex_escape = true;
break;
}
case L'\r': {
out += L'\\';
out += L'r';
if (symbolic)
out += L'';
else
out += L"\\r";
need_escape = need_complex_escape = true;
break;
}
case L'\x1B': {
out += L'\\';
out += L'e';
if (symbolic)
out += L'';
else
out += L"\\e";
need_escape = need_complex_escape = true;
break;
}
case L'\x7F': {
out += L'\\';
out += L'x';
out += L'7';
out += L'f';
if (symbolic)
out += L'';
else
out += L"\\x7f";
need_escape = need_complex_escape = true;
break;
}
case L'\\':
case L'\'': {
need_escape = need_complex_escape = true;
if (escape_printables || c == L'\\') out += L'\\';
if (escape_printables || (c == L'\\' && !symbolic)) out += L'\\';
out += *in;
break;
}
@ -1001,6 +1013,11 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring
if (*in >= 0 && *in < 32) {
need_escape = need_complex_escape = true;
if (symbolic) {
out += L'\u2400' + *in;
break;
}
if (*in < 27 && *in != 0) {
out += L'\\';
out += L'c';

View file

@ -148,7 +148,9 @@ enum {
/// string.
ESCAPE_NO_QUOTED = 1 << 1,
/// Do not escape tildes.
ESCAPE_NO_TILDE = 1 << 2
ESCAPE_NO_TILDE = 1 << 2,
/// Replace nonprintable control characters with Unicode symbols.
ESCAPE_SYMBOLIC = 1 << 3
};
typedef unsigned int escape_flags_t;

View file

@ -313,8 +313,8 @@ static comp_info_list_t process_completions_into_infos(const completion_list_t &
comp_t *comp_info = &result.at(i);
// Append the single completion string. We may later merge these into multiple.
comp_info->comp.push_back(
escape_string(comp.completion, ESCAPE_NO_PRINTABLES | ESCAPE_NO_QUOTED));
comp_info->comp.push_back(escape_string(
comp.completion, ESCAPE_NO_PRINTABLES | ESCAPE_NO_QUOTED | ESCAPE_SYMBOLIC));
// Append the mangled description.
comp_info->desc = comp.description;