Let visible length work with CR and LF

Because we are, ultimately, interested in how many cells a string
occupies, we *have* to handle carriage return (`\r`) and line
feed (`\n`).

A carriage return sets the current tally to 0, and only the longest
tally is kept. The idea here is that the last position is the same as
the last position of the longest string. So:

abcdef\r123

ends up looking like

123def

which is the same width as abcdef, 6.

A line feed meanwhile means we flush the current tally and start a new
one. Every line is printed separately, even if it's given as one.

That's because, well, counting the width over multiple lines
doesn't *help*.

As a sidenote: This is necessarily imperfect, because, while we may
know the width of the terminal ($COLUMNS), we don't know the current
cursor position. So we can only give the width, and the user can then
figure something out on their own.

But for the common case of figuring out how wide the prompt is, this
should do.
This commit is contained in:
Fabian Homborg 2021-07-29 20:49:14 +02:00
parent a05fc52fc8
commit 2087a3ca63
2 changed files with 56 additions and 9 deletions

View File

@ -768,15 +768,37 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, cons
int nnonempty = 0;
arg_iterator_t aiter(argv, optind, streams);
while (const wcstring *arg = aiter.nextstr()) {
size_t n = opts.visible ? width_without_escapes(*arg) : arg->length();
if (n > 0) {
nnonempty++;
}
if (!opts.quiet) {
streams.out.append(to_string(n));
streams.out.append(L'\n');
} else if (nnonempty > 0) {
return STATUS_CMD_OK;
if (opts.visible) {
// Visible length only makes sense line-wise.
for (auto &line : split_string(*arg, L'\n')) {
size_t max = 0;
// Carriage-return returns us to the beginning,
// the longest string stays.
for (auto &reset : split_string(line, L'\r')) {
size_t n = width_without_escapes(reset);
if (n > max) max = n;
}
if (max > 0) {
nnonempty++;
}
if (!opts.quiet) {
streams.out.append(to_string(max));
streams.out.append(L'\n');
} else if (nnonempty > 0) {
return STATUS_CMD_OK;
}
}
} else {
size_t n = arg->length();
if (n > 0) {
nnonempty++;
}
if (!opts.quiet) {
streams.out.append(to_string(n));
streams.out.append(L'\n');
} else if (nnonempty > 0) {
return STATUS_CMD_OK;
}
}
}

View File

@ -87,6 +87,31 @@ string pad -c_ --width 5 longer-than-width-param x
string pad -c ab -w4 .
# CHECKERR: string pad: Padding should be a character 'ab'
# Visible length. Let's start off simple, colors are ignored:
string length --visible (set_color red)abc
# CHECK: 3
begin
set -l fish_emoji_width 2
# This should print the emoji width
string length --visible . 🐟
# CHECK: 1
# CHECK: 2
set -l fish_emoji_width 1
string length --visible . 🐟
# CHECK: 1
# CHECK: 1
end
# Only the longest run between carriage returns is kept because the rest is overwritten.
string length --visible (set_color normal)abcdef\rfooba(set_color red)raaa
# (foobaraaa)
# CHECK: 9
# Visible length is *always* split by line
string length --visible a(set_color blue)b\ncde
# CHECK: 2
# CHECK: 3
string sub --length 2 abcde
# CHECK: ab