patch 9.2.0572: lines disappear with wrapping virtual text after a double-width char
Commit: https://github.com/vim/vim/commit/09f7fc60d3d76504cf46871277e471d034776583 Author: Hirohito Higashi <[email protected]> Date: Sun May 31 18:43:42 2026 +0000 patch 9.2.0572: lines disappear with wrapping virtual text after a double-width char Problem: With 'nowrap', when a line ends with a double-width character exactly at the window width and has wrapping "after" virtual text, the lines below disappear and "@@@" is shown. Solution: Detect that the last character fills the rightmost column using its displayed width (win_chartabsize(), so a <Tab> or double-width character is handled like a single-width one), and also when it overflows the last column. Also clarify in the help that "wrap" only takes effect with the 'wrap' option set. fixes: #20384 related: #12213 closes: #20395 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: zeertzjq <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/runtime/doc/textprop.txt b/runtime/doc/textprop.txt index 9fd5ff668..ce2867791 100644 --- a/runtime/doc/textprop.txt +++ b/runtime/doc/textprop.txt @@ -1,4 +1,4 @@ -*textprop.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*textprop.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -177,7 +177,9 @@ prop_add({lnum}, {col}, {props}) When omitted "truncate" is used. Note that this applies to the individual text property, the 'wrap' option sets the overall - behavior + behavior. "wrap" only takes effect when the + 'wrap' option is set; with 'nowrap' the text + is truncated at the right edge of the window. All fields except "type" are optional. It is an error when both "length" and "end_lnum" or "end_col" diff --git a/src/drawline.c b/src/drawline.c index 3ebb56a17..0b912e1ea 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -2479,7 +2479,11 @@ win_line( // displaying that character. // Or when not wrapping and at the rightmost column. - int only_below_follows = !wp->w_p_wrap && wlv.col == wp->w_width - 1; + // Use the displayed width so a double-width or <Tab> last + // character filling the rightmost column is detected too. + int only_below_follows = !wp->w_p_wrap + && wlv.col + win_chartabsize(wp, ptr, wlv.vcol) + >= wp->w_width; int suffix_flags = text_prop_suffix_flags[text_prop_next]; text_prop_follows = (suffix_flags diff --git a/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump b/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump new file mode 100644 index 000000000..36dfed3ff --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump @@ -0,0 +1,8 @@ +>x+0&#ffffff0@42|口*& +|s+&|e|c|o|n|d| |l|i|n|e| @33 +|t|h|i|r|d| |l|i|n|e| @34 +|~+0#4040ff13&| @43 +|~| @43 +|~| @43 +|~| @43 +| +0#0000000&@26|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump b/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump new file mode 100644 index 000000000..50136517f --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump @@ -0,0 +1,8 @@ +>x+0&#ffffff0@38|>+0#4040ff13& +|b+0#0000000&|e|t|w|e@1|n| |l|i|n|e| @27 +|x@31| @7 +|l|a|s|t| |l|i|n|e| @30 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +| +0#0000000&@21|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index a57493bc8..7d868ec06 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -3574,6 +3574,56 @@ func Test_props_with_text_after_nowrap() call StopVimInTerminal(buf) endfunc +func Test_props_with_text_after_wide_char_at_end() + CheckScreendump + CheckRunVimInTerminal + + " The buffer line ends with a double-width character exactly at the window + " width and has wrapping "after" virtual text. This must not leave blank + " lines or "@@@", see issue #20384. + let lines =<< trim END + vim9script + set nowrap + setline(1, [repeat('x', 43) .. '口', 'second line', 'third line']) + prop_type_add('errtype', {highlight: 'WarningMsg', text_wrap: 'wrap'}) + prop_add(1, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + END + call writefile(lines, 'XscriptPropsAfterWideChar', 'D') + let buf = RunVimInTerminal('-S XscriptPropsAfterWideChar', #{rows: 8, cols: 45}) + call VerifyScreenDump(buf, 'Test_prop_with_text_after_wide_char_1', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_props_with_text_after_wide_char_overflow() + CheckScreendump + CheckRunVimInTerminal + + " Like above, but the last character reaches the rightmost column without + " starting on it: a double-width character that does not fit in the last + " column, and a <Tab> that expands up to the window width. Both must be + " detected as filling the line so the wrapping "after" text does not cause + " blank lines, "@@@" or a spurious wrap with 'nowrap'. + let lines =<< trim END + vim9script + set nowrap tabstop=8 noexpandtab + setline(1, [ + repeat('x', 39) .. '口', + 'between line', + repeat('x', 32) .. " ", + 'last line', + ]) + prop_type_add('errtype', {highlight: 'WarningMsg', text_wrap: 'wrap'}) + prop_add(1, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + prop_add(3, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + END + call writefile(lines, 'XscriptPropsAfterWideOverflow', 'D') + let buf = RunVimInTerminal('-S XscriptPropsAfterWideOverflow', #{rows: 8, cols: 40}) + call VerifyScreenDump(buf, 'Test_prop_with_text_after_wide_char_2', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_prop_with_text_below_cul() CheckScreendump CheckRunVimInTerminal diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index f90d71971..a4c2d05d4 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2157,4 +2157,25 @@ def Test_map_legacy_expr() v9.CheckDefAndScriptSuccess(lines) enddef +" :call on a funcref stored in a dict member used to fail with E1017 in Vim9 +" script because get_lval() treated the subscript as a re-declaration. +def Test_call_dict_funcref() + var lines =<< trim END + vim9script + var d: dict<any> = {} + var marker = '' + def F() + marker = 'called' + enddef + d.key = F + d['k2'] = F + call d.key() + assert_equal('called', marker) + marker = '' + call d['k2']() + assert_equal('called', marker) + END + v9.CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c index bd4c0bbc3..ff5cf76a0 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -6273,7 +6273,7 @@ ex_delfunction(exarg_T *eap) int is_global = FALSE; p = eap->arg; - name = trans_function_name_ext(&p, &is_global, eap->skip, 0, &fudi, + name = trans_function_name_ext(&p, &is_global, eap->skip, TFN_NO_DECL, &fudi, NULL, NULL, NULL); vim_free(fudi.fd_newkey); if (name == NULL) @@ -6823,7 +6823,7 @@ ex_call(exarg_T *eap) return; } - tofree = trans_function_name_ext(&arg, NULL, FALSE, TFN_INT, + tofree = trans_function_name_ext(&arg, NULL, FALSE, TFN_INT | TFN_NO_DECL, &fudi, &partial, vim9script ? &type : NULL, &ufunc); if (fudi.fd_newkey != NULL) { diff --git a/src/version.c b/src/version.c index a90dc1cb9..ac58fbdf8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 572, /**/ 571, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wTlOH-00GQdq-Aj%40256bit.org.
