From 7a53bd65d70fad73dbd6832497965fa11b2a357b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 25 Nov 2013 19:46:05 +0000 Subject: [PATCH] Fix interaction of insch() with selection highlights. Previously I had unthinkingly called the general-purpose check_selection() routine to indicate that I was going to mess with n character cells right of the cursor position, causing the selection highlight to be removed if it intersected that region. This is all wrong, since actually the whole region from cursor to EOL is modified by any character insertion or deletion, so if we were going to call check_selection it should be on that whole region. (Quick demo: select part of the line to the right of the cursor, then emit ESC[P or ESC[@ and see the text move left or right while the highlight stays put.) So we could just call check_selection() on that larger affected region, and that would be correct. However, we can do something slightly more elegant in the case where the selection is contained entirely within the subregion that moves to one side (as opposed to the characters that actually vanish at one or other end): we can move the selection highlight with the text under it, to preserve the visual reminder of which text was selected for as long as possible. [originally from svn r10097] --- terminal.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/terminal.c b/terminal.c index d6a754c9..bd77f239 100644 --- a/terminal.c +++ b/terminal.c @@ -2380,16 +2380,51 @@ static void insch(Terminal *term, int n) { int dir = (n < 0 ? -1 : +1); int m, j; - pos cursplus; + pos eol; termline *ldata; n = (n < 0 ? -n : n); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; m = term->cols - term->curs.x - n; - cursplus.y = term->curs.y; - cursplus.x = term->curs.x + n; - check_selection(term, term->curs, cursplus); + + /* + * We must de-highlight the selection if it overlaps any part of + * the region affected by this operation, i.e. the region from the + * current cursor position to end-of-line, _unless_ the entirety + * of the selection is going to be moved to the left or right by + * this operation but otherwise unchanged, in which case we can + * simply move the highlight with the text. + */ + eol.y = term->curs.y; + eol.x = term->cols; + if (poslt(term->curs, term->selend) && poslt(term->selstart, eol)) { + pos okstart = term->curs; + pos okend = eol; + if (dir > 0) { + /* Insertion: n characters at EOL will be splatted. */ + okend.x -= n; + } else { + /* Deletion: n characters at cursor position will be splatted. */ + okstart.x += n; + } + if (posle(okstart, term->selstart) && posle(term->selend, okend)) { + /* Selection is contained entirely in the interval + * [okstart,okend), so we need only adjust the selection + * bounds. */ + term->selstart.x += dir * n; + term->selend.x += dir * n; + assert(term->selstart.x >= term->curs.x); + assert(term->selstart.x < term->cols); + assert(term->selend.x > term->curs.x); + assert(term->selend.x <= term->cols); + } else { + /* Selection is not wholly contained in that interval, so + * we must unhighlight it. */ + deselect(term); + } + } + check_boundary(term, term->curs.x, term->curs.y); if (dir < 0) check_boundary(term, term->curs.x + n, term->curs.y); -- 2.45.2