# HG changeset patch # User Bram Moolenaar # Date 1660066203 -7200 # Node ID fc0f93590fd404fb8b865693e28a45988e6e0bf5 # Parent d8ad7b1e9116968fd86d6815cd049fe44562cd2d patch 9.0.0179: cursor pos wrong with wrapping virtual text in empty line Commit: https://github.com/vim/vim/commit/49a90792d950c51608d0459ef8699fe921070718 Author: Bram Moolenaar Date: Tue Aug 9 18:25:23 2022 +0100 patch 9.0.0179: cursor pos wrong with wrapping virtual text in empty line Problem: Cursor position wrong with wrapping virtual text in empty line. Solution: Adjust handling of an empty line. (closes https://github.com/vim/vim/issues/10875) diff --git a/src/charset.c b/src/charset.c --- a/src/charset.c +++ b/src/charset.c @@ -759,6 +759,14 @@ linetabsize_col(int startcol, char_u *s) init_chartabsize_arg(&cts, curwin, 0, startcol, s, s); while (*cts.cts_ptr != NUL) cts.cts_vcol += lbr_chartabsize_adv(&cts); +#ifdef FEAT_PROP_POPUP + if (cts.cts_has_prop_with_text && cts.cts_ptr == cts.cts_line) + { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width; + } +#endif clear_chartabsize_arg(&cts); return (int)cts.cts_vcol; } @@ -772,16 +780,31 @@ win_linetabsize(win_T *wp, linenr_T lnum chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); -#ifdef FEAT_PROP_POPUP - cts.cts_with_trailing = len == MAXCOL; -#endif - for ( ; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (int)cts.cts_vcol; } + void +win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ +#ifdef FEAT_PROP_POPUP + cts->cts_with_trailing = len == MAXCOL; +#endif + for ( ; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); +#ifdef FEAT_PROP_POPUP + // check for a virtual text on an empty line + if (cts->cts_has_prop_with_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) + { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width; + } +#endif +} + /* * Return TRUE if 'c' is a normal identifier character: * Letters and characters from the 'isident' option. @@ -1128,10 +1151,10 @@ win_lbr_chartabsize( size = win_chartabsize(wp, s, vcol); # ifdef FEAT_PROP_POPUP - if (cts->cts_has_prop_with_text && *line != NUL) + if (cts->cts_has_prop_with_text) { int tab_size = size; - int charlen = mb_ptr2len(s); + int charlen = *s == NUL ? 1 : mb_ptr2len(s); int i; int col = (int)(s - line); garray_T *gap = &wp->w_buffer->b_textprop_text; @@ -1412,6 +1435,9 @@ getvcol( int ts = wp->w_buffer->b_p_ts; int c; chartabsize_T cts; +#ifdef FEAT_PROP_POPUP + int on_NUL = FALSE; +#endif vcol = 0; line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); @@ -1512,6 +1538,11 @@ getvcol( if (*cts.cts_ptr == NUL) { incr = 1; // NUL at end of line only takes one column +#ifdef FEAT_PROP_POPUP + if (cts.cts_cur_text_width > 0) + incr = cts.cts_cur_text_width; + on_NUL = TRUE; +#endif break; } @@ -1544,8 +1575,8 @@ getvcol( else { #ifdef FEAT_PROP_POPUP - if ((State & MODE_INSERT) == 0) - // cursor is after inserted text + if ((State & MODE_INSERT) == 0 && !on_NUL) + // cursor is after inserted text, unless on the NUL vcol += cts.cts_cur_text_width; #endif *cursor = vcol + head; // cursor at start diff --git a/src/misc1.c b/src/misc1.c --- a/src/misc1.c +++ b/src/misc1.c @@ -399,11 +399,19 @@ plines_win_nofold(win_T *wp, linenr_T ln char_u *s; long col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, FALSE); - if (*s == NUL) // empty line - return 1; - col = win_linetabsize(wp, lnum, s, (colnr_T)MAXCOL); + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL +#ifdef FEAT_PROP_POPUP + && !cts.cts_has_prop_with_text +#endif + ) + return 1; // be quick for an empty line + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (int)cts.cts_vcol; /* * If list mode is on, then the '$' at the end of the line may take up one diff --git a/src/proto/charset.pro b/src/proto/charset.pro --- a/src/proto/charset.pro +++ b/src/proto/charset.pro @@ -18,6 +18,7 @@ int chartabsize(char_u *p, colnr_T col); int linetabsize(char_u *s); int linetabsize_col(int startcol, char_u *s); int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len); +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len); int vim_isIDc(int c); int vim_isNormalIDc(int c); int vim_iswordc(int c); diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_1.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_1.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_empty_line_1.dump @@ -0,0 +1,8 @@ +>X+0&#ffff4012@59 +|a+0&#ffffff0@2| @56 +|X+0&#ffff4012@59 +@1| +0&#ffffff0@58 +|b@5| @53 +|~+0#4040ff13&| @58 +|~| @58 +| +0#0000000&@41|1|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_2.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_2.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_empty_line_2.dump @@ -0,0 +1,8 @@ +>X+0&#ffff4012@59 +|a+0&#ffffff0@2| @56 +|X+0&#ffff4012@59 +@1| +0&#ffffff0@58 +|b@5| @53 +|~+0#4040ff13&| @58 +|~| @58 +| +0#0000000&@41|1|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_3.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_3.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_empty_line_3.dump @@ -0,0 +1,8 @@ +|X+0&#ffff4012@59 +|a+0&#ffffff0@1>a| @56 +|X+0&#ffff4012@59 +@1| +0&#ffffff0@58 +|b@5| @53 +|~+0#4040ff13&| @58 +|~| @58 +| +0#0000000&@41|2|,|3| @10|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_4.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_4.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_empty_line_4.dump @@ -0,0 +1,8 @@ +|X+0&#ffff4012@59 +|a+0&#ffffff0@2| @56 +>X+0&#ffff4012@59 +@1| +0&#ffffff0@58 +|b@5| @53 +|~+0#4040ff13&| @58 +|~| @58 +| +0#0000000&@41|3|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_5.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_5.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_empty_line_5.dump @@ -0,0 +1,8 @@ +|X+0&#ffff4012@59 +|a+0&#ffffff0@2| @56 +|X+0&#ffff4012@59 +@1| +0&#ffffff0@58 +|b@4>b| @53 +|~+0#4040ff13&| @58 +|~| @58 +| +0#0000000&@41|4|,|6| @10|A|l@1| diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -2557,6 +2557,32 @@ func Test_props_with_text_after_truncate call delete('XscriptPropsWithTextAfterTrunc') endfunc +func Test_props_with_text_empty_line() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, ['', 'aaa', '', 'bbbbbb']) + call prop_type_add('prop1', #{highlight: 'Search'}) + call prop_add(1, 1, #{type: 'prop1', text_wrap: 'wrap', text: repeat('X', &columns)}) + call prop_add(3, 1, #{type: 'prop1', text_wrap: 'wrap', text: repeat('X', &columns + 1)}) + normal gg0 + END + call writefile(lines, 'XscriptPropsWithTextEmptyLine') + let buf = RunVimInTerminal('-S XscriptPropsWithTextEmptyLine', #{rows: 8, cols: 60}) + call VerifyScreenDump(buf, 'Test_prop_with_text_empty_line_1', {}) + call term_sendkeys(buf, "$") + call VerifyScreenDump(buf, 'Test_prop_with_text_empty_line_2', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_prop_with_text_empty_line_3', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_prop_with_text_empty_line_4', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_prop_with_text_empty_line_5', {}) + + call StopVimInTerminal(buf) + call delete('XscriptPropsWithTextEmptyLine') +endfunc + func Test_props_with_text_after_wraps() CheckRunVimInTerminal diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -736,6 +736,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 179, +/**/ 178, /**/ 177,