From: Hirohito Higashi Date: Sun, 31 May 2026 18:43:42 +0000 (+0000) Subject: patch 9.2.0572: lines disappear with wrapping virtual text after a double-width char X-Git-Tag: v9.2.0572^0 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=09f7fc60d3d76504cf46871277e471d034776583;p=thirdparty%2Fvim.git 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 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) Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/textprop.txt b/runtime/doc/textprop.txt index 9fd5ff6686..ce28677913 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 3ebb56a173..0b912e1eae 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 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 0000000000..36dfed3ff2 --- /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 0000000000..50136517f4 --- /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 a57493bc81..7d868ec064 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 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) .. "\t", + '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 f90d719715..a4c2d05d45 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 = {} + 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 bd4c0bbc32..ff5cf76a0e 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 a90dc1cb9b..ac58fbdf80 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, /**/