To: vim_dev@googlegroups.com Subject: Patch 9.0.0179 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0179 Problem: Cursor position wrong with wrapping virtual text in empty line. Solution: Adjust handling of an empty line. (closes #10875) Files: src/charset.c, src/proto/charset.pro, src/misc1.c, src/testdir/test_textprop.vim, src/testdir/dumps/Test_prop_with_text_empty_line_1.dump, src/testdir/dumps/Test_prop_with_text_empty_line_2.dump, src/testdir/dumps/Test_prop_with_text_empty_line_3.dump, src/testdir/dumps/Test_prop_with_text_empty_line_4.dump, src/testdir/dumps/Test_prop_with_text_empty_line_5.dump *** ../vim-9.0.0178/src/charset.c 2022-08-09 16:55:19.296912275 +0100 --- src/charset.c 2022-08-09 18:18:32.677908662 +0100 *************** *** 759,764 **** --- 759,772 ---- 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,787 **** 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); clear_chartabsize_arg(&cts); return (int)cts.cts_vcol; } /* * Return TRUE if 'c' is a normal identifier character: * Letters and characters from the 'isident' option. --- 780,810 ---- chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); ! 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,1137 **** size = win_chartabsize(wp, s, vcol); # ifdef FEAT_PROP_POPUP ! if (cts->cts_has_prop_with_text && *line != NUL) { int tab_size = size; ! int charlen = mb_ptr2len(s); int i; int col = (int)(s - line); garray_T *gap = &wp->w_buffer->b_textprop_text; --- 1151,1160 ---- size = win_chartabsize(wp, s, vcol); # ifdef FEAT_PROP_POPUP ! if (cts->cts_has_prop_with_text) { int tab_size = size; ! 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,1417 **** --- 1435,1443 ---- 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,1517 **** --- 1538,1548 ---- 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,1551 **** else { #ifdef FEAT_PROP_POPUP ! if ((State & MODE_INSERT) == 0) ! // cursor is after inserted text vcol += cts.cts_cur_text_width; #endif *cursor = vcol + head; // cursor at start --- 1575,1582 ---- else { #ifdef FEAT_PROP_POPUP ! 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 *** ../vim-9.0.0178/src/proto/charset.pro 2022-08-04 15:03:16.253142105 +0100 --- src/proto/charset.pro 2022-08-09 17:49:19.598186450 +0100 *************** *** 18,23 **** --- 18,24 ---- 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); *** ../vim-9.0.0178/src/misc1.c 2022-08-06 13:47:16.212982332 +0100 --- src/misc1.c 2022-08-09 17:50:31.858044240 +0100 *************** *** 399,409 **** char_u *s; long col; int width; 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); /* * If list mode is on, then the '$' at the end of the line may take up one --- 399,417 ---- char_u *s; long col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, FALSE); ! 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 *** ../vim-9.0.0178/src/testdir/test_textprop.vim 2022-08-09 16:55:19.296912275 +0100 --- src/testdir/test_textprop.vim 2022-08-09 18:19:48.653960452 +0100 *************** *** 2557,2562 **** --- 2557,2588 ---- 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 *** ../vim-9.0.0178/src/testdir/dumps/Test_prop_with_text_empty_line_1.dump 2022-08-09 18:21:40.686014551 +0100 --- src/testdir/dumps/Test_prop_with_text_empty_line_1.dump 2022-08-09 18:10:25.025112203 +0100 *************** *** 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| *** ../vim-9.0.0178/src/testdir/dumps/Test_prop_with_text_empty_line_2.dump 2022-08-09 18:21:40.690014553 +0100 --- src/testdir/dumps/Test_prop_with_text_empty_line_2.dump 2022-08-09 18:19:55.353964392 +0100 *************** *** 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| *** ../vim-9.0.0178/src/testdir/dumps/Test_prop_with_text_empty_line_3.dump 2022-08-09 18:21:40.694014554 +0100 --- src/testdir/dumps/Test_prop_with_text_empty_line_3.dump 2022-08-09 18:19:56.505965061 +0100 *************** *** 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| *** ../vim-9.0.0178/src/testdir/dumps/Test_prop_with_text_empty_line_4.dump 2022-08-09 18:21:40.698014556 +0100 --- src/testdir/dumps/Test_prop_with_text_empty_line_4.dump 2022-08-09 18:19:57.653965722 +0100 *************** *** 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| *** ../vim-9.0.0178/src/testdir/dumps/Test_prop_with_text_empty_line_5.dump 2022-08-09 18:21:40.702014558 +0100 --- src/testdir/dumps/Test_prop_with_text_empty_line_5.dump 2022-08-09 18:19:58.805966386 +0100 *************** *** 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| *** ../vim-9.0.0178/src/version.c 2022-08-09 16:55:19.296912275 +0100 --- src/version.c 2022-08-09 17:26:48.956583494 +0100 *************** *** 737,738 **** --- 737,740 ---- { /* Add new patch number below this line */ + /**/ + 179, /**/ -- A KNIGHT rides into shot and hacks him to the ground. He rides off. We stay for a moment on the glade. A MIDDLE-AGED LADY in a C. & A. twin-set emerges from the trees and looks in horror at the body of her HUSBAND. MRS HISTORIAN: FRANK! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///