summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-06-01 20:26:55 +0100
committerBram Moolenaar <Bram@vim.org>2023-06-01 20:26:55 +0100
commit47eec6716b8621fd43bac8ecc9c19089df26705e (patch)
tree8f929992e3e732a51f3e5ad89d99c260a8da4300
parent8509014adda188ee8bdf6a2e123fbf15a91b29d2 (diff)
patch 9.0.1598: screenchar() and others are wrong with DBCS 'encoding'v9.0.1598
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)
-rw-r--r--src/evalfunc.c45
-rw-r--r--src/screen.c8
-rw-r--r--src/testdir/test_functions.vim25
-rw-r--r--src/testdir/test_utf8.vim13
-rw-r--r--src/version.c2
5 files changed, 58 insertions, 35 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 8f20ebee17..62e9a40624 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -8934,7 +8934,6 @@ f_screenchar(typval_T *argvars, typval_T *rettv)
{
int row;
int col;
- int off;
int c;
if (in_vim9script()
@@ -8948,11 +8947,9 @@ f_screenchar(typval_T *argvars, typval_T *rettv)
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_T *rettv)
{
int row;
int col;
- int off;
int c;
int i;
@@ -8982,18 +8978,18 @@ f_screenchars(typval_T *argvars, typval_T *rettv)
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_T *rettv)
{
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_T *rettv)
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 *rettv)
/*
* 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
index e9bc79267b..7966f3b4d1 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1199,8 +1199,9 @@ screen_putchar(int c, int row, int col, int attr)
}
/*
- * 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 *bytes, int *attrp)
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
index 17a87d85e0..715da0b834 100644
--- 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
index 610566fd65..3bb7459797 100644
--- 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
index 11b962d7e0..320add580b 100644
--- 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,