diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 8aaeabf61..d61e109f8 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -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; + } } } diff --git a/tests/checks/string.fish b/tests/checks/string.fish index 8f64da1a1..8f4296b96 100644 --- a/tests/checks/string.fish +++ b/tests/checks/string.fish @@ -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