Skip to content

Commit 02b4d9b

Browse files
committed
patch 8.2.2607: strcharpart() cannot include composing characters
Problem: strcharpart() cannot include composing characters. Solution: Add the {skipcc} argument.
1 parent 70ce8a1 commit 02b4d9b

4 files changed

Lines changed: 49 additions & 13 deletions

File tree

runtime/doc/eval.txt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,8 @@ byte under the cursor: >
11871187

11881188
In Vim9 script:
11891189
If expr8 is a String this results in a String that contains the expr1'th
1190-
single character from expr8. To use byte indexes use |strpart()|.
1190+
single character (including any composing characters) from expr8. To use byte
1191+
indexes use |strpart()|.
11911192

11921193
Index zero gives the first byte or character. Careful: text column numbers
11931194
start with one!
@@ -1217,8 +1218,9 @@ In legacy Vim script the indexes are byte indexes. This doesn't recognize
12171218
multibyte encodings, see |byteidx()| for computing the indexes. If expr8 is
12181219
a Number it is first converted to a String.
12191220

1220-
In Vim9 script the indexes are character indexes. To use byte indexes use
1221-
|strpart()|.
1221+
In Vim9 script the indexes are character indexes and include composing
1222+
characters. To use byte indexes use |strpart()|. To use character indexes
1223+
without including composing characters use |strcharpart()|.
12221224

12231225
The item at index expr1b is included, it is inclusive. For an exclusive index
12241226
use the |slice()| function.
@@ -2924,7 +2926,7 @@ str2list({expr} [, {utf8}]) List convert each character of {expr} to
29242926
str2nr({expr} [, {base} [, {quoted}]])
29252927
Number convert String to Number
29262928
strcharlen({expr}) Number character length of the String {expr}
2927-
strcharpart({str}, {start} [, {len}])
2929+
strcharpart({str}, {start} [, {len} [, {skipcc}]])
29282930
String {len} characters of {str} at
29292931
character {start}
29302932
strchars({expr} [, {skipcc}]) Number character count of the String {expr}
@@ -9919,7 +9921,7 @@ slice({expr}, {start} [, {end}]) *slice()*
99199921
Similar to using a |slice| "expr[start : end]", but "end" is
99209922
used exclusive. And for a string the indexes are used as
99219923
character indexes instead of byte indexes, like in
9922-
|vim9script|.
9924+
|vim9script|. Also, composing characters are not counted.
99239925
When {end} is omitted the slice continues to the last item.
99249926
When {end} is -1 the last item is omitted.
99259927

@@ -10290,12 +10292,16 @@ strcharlen({expr}) *strcharlen()*
1029010292
GetText()->strcharlen()
1029110293

1029210294

10293-
strcharpart({src}, {start} [, {len}]) *strcharpart()*
10295+
strcharpart({src}, {start} [, {len} [, {skipcc}]]) *strcharpart()*
1029410296
Like |strpart()| but using character index and length instead
10295-
of byte index and length. Composing characters are counted
10296-
separately.
10297+
of byte index and length.
10298+
When {skipcc} is omitted or zero, composing characters are
10299+
counted separately.
10300+
When {skipcc} set to 1, Composing characters are ignored,
10301+
similar to |slice()|.
1029710302
When a character index is used where a character does not
10298-
exist it is assumed to be one character. For example: >
10303+
exist it is omitted and counted as one character. For
10304+
example: >
1029910305
strcharpart('abc', -1, 2)
1030010306
< results in 'a'.
1030110307

@@ -10309,7 +10315,7 @@ strchars({expr} [, {skipcc}]) *strchars()*
1030910315
When {skipcc} is omitted or zero, composing characters are
1031010316
counted separately.
1031110317
When {skipcc} set to 1, Composing characters are ignored.
10312-
|strcharlen()| does the same.
10318+
|strcharlen()| always does this.
1031310319

1031410320
Also see |strlen()|, |strdisplaywidth()| and |strwidth()|.
1031510321

src/evalfunc.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,7 +1575,7 @@ static funcentry_T global_functions[] =
15751575
ret_number, f_str2nr},
15761576
{"strcharlen", 1, 1, FEARG_1, NULL,
15771577
ret_number, f_strcharlen},
1578-
{"strcharpart", 2, 3, FEARG_1, NULL,
1578+
{"strcharpart", 2, 4, FEARG_1, NULL,
15791579
ret_string, f_strcharpart},
15801580
{"strchars", 1, 2, FEARG_1, NULL,
15811581
ret_number, f_strchars},
@@ -9316,6 +9316,7 @@ f_strcharpart(typval_T *argvars, typval_T *rettv)
93169316
int nchar;
93179317
int nbyte = 0;
93189318
int charlen;
9319+
int skipcc = FALSE;
93199320
int len = 0;
93209321
int slen;
93219322
int error = FALSE;
@@ -9326,10 +9327,24 @@ f_strcharpart(typval_T *argvars, typval_T *rettv)
93269327
nchar = (int)tv_get_number_chk(&argvars[1], &error);
93279328
if (!error)
93289329
{
9330+
if (argvars[2].v_type != VAR_UNKNOWN
9331+
&& argvars[3].v_type != VAR_UNKNOWN)
9332+
{
9333+
skipcc = tv_get_bool(&argvars[3]);
9334+
if (skipcc < 0 || skipcc > 1)
9335+
{
9336+
semsg(_(e_using_number_as_bool_nr), skipcc);
9337+
return;
9338+
}
9339+
}
9340+
93299341
if (nchar > 0)
93309342
while (nchar > 0 && nbyte < slen)
93319343
{
9332-
nbyte += MB_CPTR2LEN(p + nbyte);
9344+
if (skipcc)
9345+
nbyte += mb_ptr2len(p + nbyte);
9346+
else
9347+
nbyte += MB_CPTR2LEN(p + nbyte);
93339348
--nchar;
93349349
}
93359350
else
@@ -9344,7 +9359,12 @@ f_strcharpart(typval_T *argvars, typval_T *rettv)
93449359
if (off < 0)
93459360
len += 1;
93469361
else
9347-
len += MB_CPTR2LEN(p + off);
9362+
{
9363+
if (skipcc)
9364+
len += mb_ptr2len(p + off);
9365+
else
9366+
len += MB_CPTR2LEN(p + off);
9367+
}
93489368
--charlen;
93499369
}
93509370
}

src/testdir/test_expr_utf8.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ func Test_strcharpart()
3131
call assert_equal('a', strcharpart('àxb', 0, 1))
3232
call assert_equal('̀', strcharpart('àxb', 1, 1))
3333
call assert_equal('x', strcharpart('àxb', 2, 1))
34+
35+
36+
call assert_equal('a', strcharpart('àxb', 0, 1, 0))
37+
call assert_equal('', strcharpart('àxb', 0, 1, 1))
38+
call assert_equal('x', strcharpart('àxb', 1, 1, 1))
39+
40+
call assert_fails("let v = strcharpart('abc', 0, 0, [])", 'E745:')
41+
call assert_fails("let v = strcharpart('abc', 0, 0, 2)", 'E1023:')
3442
endfunc
3543

3644
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2607,
753755
/**/
754756
2606,
755757
/**/

0 commit comments

Comments
 (0)