From d2b02a8a61bce815f9c21a7de9ac9ae1efe526dc Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 10 May 2019 17:21:06 +0200 Subject: [PATCH] src/screen: Only check for combining marks if necessary line_shared_prefix explains in its comment that > If the prefix ends on a combining character, do not include the previous character in the prefix. But that's not what it does. Instead, what it appears to do is to return idx for *every* combining mark. This seems wrong to begin with, and it also requires checking wcwidth for *every* character. So instead we don't do that. If we find the mismatch, we check if it's a combining mark, and then go back to the previous character (i.e. the one before the one that the combining mark is for). My tests found no issues with this, other than a 20% reduction in pasting time. --- src/screen.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/screen.cpp b/src/screen.cpp index 341752eae..8c40a80fc 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -552,14 +552,25 @@ static size_t line_shared_prefix(const line_t &a, const line_t &b) { size_t idx, max = std::min(a.size(), b.size()); for (idx = 0; idx < max; idx++) { wchar_t ac = a.char_at(idx), bc = b.char_at(idx); - if (fish_wcwidth(ac) < 1 || fish_wcwidth(bc) < 1) { - // Possible combining mark, return one index prior. - if (idx > 0) idx--; - break; - } // We're done if the text or colors are different. - if (ac != bc || a.color_at(idx) != b.color_at(idx)) break; + if (ac != bc || a.color_at(idx) != b.color_at(idx)) { + if (idx > 0) { + const line_t *c = nullptr; + // Possible combining mark, go back until we hit _two_ printable characters or idx of 0. + if (fish_wcwidth(a.char_at(idx)) < 1) { + c = &a; + } else if (fish_wcwidth(b.char_at(idx)) < 1) { + c = &b; + } + + if (c) { + while (idx > 1 && (fish_wcwidth(c->char_at(idx - 1)) < 1 || fish_wcwidth(c->char_at(idx)) < 1)) idx--; + if (idx == 1 && fish_wcwidth(c->char_at(idx)) < 1) idx = 0; + } + } + break; + } } return idx; }