# HG changeset patch # User Bram Moolenaar # Date 1685647803 -7200 # Node ID aa64fdad1f607db24829b5052852958ff5d1969a # Parent 26aa788bed3f6717759b709e86c4aaf1112116ad patch 9.0.1598: screenchar() and others are wrong with DBCS 'encoding' Commit: https://github.com/vim/vim/commit/47eec6716b8621fd43bac8ecc9c19089df26705e Author: zeertzjq Date: Thu Jun 1 20:26:55 2023 +0100 patch 9.0.1598: screenchar() and others are wrong with DBCS 'encoding' Problem: screenchar(), screenchars() and screenstring() do not work properly when 'encoding' is set to a double-byte encoding. Solution: Fix the way the bytes of the characters are obtained. (issue #12469) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -8934,7 +8934,6 @@ f_screenchar(typval_T *argvars, typval_T { int row; int col; - int off; int c; if (in_vim9script() @@ -8948,11 +8947,9 @@ f_screenchar(typval_T *argvars, typval_T c = -1; else { - off = LineOffset[row] + col; - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; - else - c = ScreenLines[off]; + char_u buf[MB_MAXBYTES + 1]; + screen_getbytes(row, col, buf, NULL); + c = (*mb_ptr2char)(buf); } rettv->vval.v_number = c; } @@ -8965,7 +8962,6 @@ f_screenchars(typval_T *argvars, typval_ { int row; int col; - int off; int c; int i; @@ -8982,18 +8978,18 @@ f_screenchars(typval_T *argvars, typval_ if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns) return; - off = LineOffset[row] + col; - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; + char_u buf[MB_MAXBYTES + 1]; + screen_getbytes(row, col, buf, NULL); + int pcc[MAX_MCO]; + if (enc_utf8) + c = utfc_ptr2char(buf, pcc); else - c = ScreenLines[off]; + c = (*mb_ptr2char)(buf); list_append_number(rettv->vval.v_list, (varnumber_T)c); if (enc_utf8) - - for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i) - list_append_number(rettv->vval.v_list, - (varnumber_T)ScreenLinesC[i][off]); + for (i = 0; i < Screen_mco && pcc[i] != 0; ++i) + list_append_number(rettv->vval.v_list, (varnumber_T)pcc[i]); } /* @@ -9024,11 +9020,7 @@ f_screenstring(typval_T *argvars, typval { int row; int col; - int off; - int c; - int i; char_u buf[MB_MAXBYTES + 1]; - int buflen = 0; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -9043,18 +9035,7 @@ f_screenstring(typval_T *argvars, typval if (row < 0 || row >= screen_Rows || col < 0 || col >= screen_Columns) return; - off = LineOffset[row] + col; - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; - else - c = ScreenLines[off]; - buflen += mb_char2bytes(c, buf); - - if (enc_utf8 && ScreenLinesUC[off] != 0) - for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i) - buflen += mb_char2bytes(ScreenLinesC[i][off], buf + buflen); - - buf[buflen] = NUL; + screen_getbytes(row, col, buf, NULL); rettv->vval.v_string = vim_strsave(buf); } @@ -9433,7 +9414,7 @@ f_searchpos(typval_T *argvars, typval_T /* * Set the cursor or mark position. - * If 'charpos' is TRUE, then use the column number as a character offset. + * If "charpos" is TRUE, then use the column number as a character offset. * Otherwise use the column number as a byte offset. */ static void diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -1199,8 +1199,9 @@ screen_putchar(int c, int row, int col, } /* - * Get a single character directly from ScreenLines into "bytes[]". - * Also return its attribute in *attrp; + * Get a single character directly from ScreenLines into "bytes", which must + * have a size of "MB_MAXBYTES + 1". + * If "attrp" is not NULL, return the character's attribute in "*attrp". */ void screen_getbytes(int row, int col, char_u *bytes, int *attrp) @@ -1212,7 +1213,8 @@ screen_getbytes(int row, int col, char_u return; off = LineOffset[row] + col; - *attrp = ScreenAttrs[off]; + if (attrp != NULL) + *attrp = ScreenAttrs[off]; bytes[0] = ScreenLines[off]; bytes[1] = NUL; diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -3217,6 +3217,31 @@ func Test_screen_functions() call assert_equal(-1, screenattr(-1, -1)) call assert_equal(-1, screenchar(-1, -1)) call assert_equal([], screenchars(-1, -1)) + + " Run this in a separate Vim instance to avoid messing up. + let after =<< trim [CODE] + scriptencoding utf-8 + call setline(1, '口') + redraw + call assert_equal(0, screenattr(1, 1)) + call assert_equal(char2nr('口'), screenchar(1, 1)) + call assert_equal([char2nr('口')], screenchars(1, 1)) + call assert_equal('口', screenstring(1, 1)) + call writefile(v:errors, 'Xresult') + qall! + [CODE] + + let encodings = ['utf-8', 'cp932', 'cp936', 'cp949', 'cp950'] + if !has('win32') + let encodings += ['euc-jp'] + endif + for enc in encodings + let msg = 'enc=' .. enc + if RunVim([], after, $'--clean --cmd "set encoding={enc}"') + call assert_equal([], readfile('Xresult'), msg) + endif + call delete('Xresult') + endfor endfunc " Test for getcurpos() and setpos() diff --git a/src/testdir/test_utf8.vim b/src/testdir/test_utf8.vim --- a/src/testdir/test_utf8.vim +++ b/src/testdir/test_utf8.vim @@ -135,6 +135,19 @@ func Test_screenchar_utf8() call assert_equal("B", screenstring(1, 2)) call assert_equal("C\u0308", screenstring(1, 3)) + " 1-cell, with 6 composing characters + set maxcombine=6 + call setline(1, ["ABC" .. repeat("\u0308", 6)]) + redraw + call assert_equal([0x0041], screenchars(1, 1)) + call assert_equal([0x0042], 1->screenchars(2)) + " This should not use uninitialized memory + call assert_equal([0x0043] + repeat([0x0308], 6), screenchars(1, 3)) + call assert_equal("A", screenstring(1, 1)) + call assert_equal("B", screenstring(1, 2)) + call assert_equal("C" .. repeat("\u0308", 6), screenstring(1, 3)) + set maxcombine& + " 2-cells, with composing characters let text = "\u3042\u3044\u3046\u3099" call setline(1, text) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1598, +/**/ 1597, /**/ 1596,