From 6106504e9edc8500131f7a36e59bc146f90180fa Mon Sep 17 00:00:00 2001 From: mityu Date: Mon, 19 Jul 2021 20:07:21 +0200 Subject: patch 8.2.3184: cannot add a digraph with a leading space Problem: Cannot add a digraph with a leading space. It is not easy to list existing digraphs. Solution: Add setdigraph(), setdigraphlist(), getdigraph() and getdigraphlist(). (closes #8580) --- src/digraph.c | 388 +++++++++++++++++++++++++++++++++++++++---- src/errors.h | 12 ++ src/evalfunc.c | 15 ++ src/ex_docmd.c | 2 +- src/globals.h | 2 + src/proto/digraph.pro | 6 + src/testdir/test_digraph.vim | 79 ++++++++- src/version.c | 2 + 8 files changed, 469 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/digraph.c b/src/digraph.c index fd78a16ecd..9b1836eef0 100644 --- a/src/digraph.c +++ b/src/digraph.c @@ -1992,6 +1992,65 @@ getdigraph(int char1, int char2, int meta_char) return retval; } +/* + * Add a digraph to the digraph table. + */ + static void +registerdigraph(int char1, int char2, int n) +{ + int i; + digr_T *dp; + + // If the digraph already exists, replace "result". + dp = (digr_T *)user_digraphs.ga_data; + for (i = 0; i < user_digraphs.ga_len; ++i) + { + if ((int)dp->char1 == char1 && (int)dp->char2 == char2) + { + dp->result = n; + return; + } + ++dp; + } + + // Add a new digraph to the table. + if (ga_grow(&user_digraphs, 1) == OK) + { + dp = (digr_T *)user_digraphs.ga_data + user_digraphs.ga_len; + dp->char1 = char1; + dp->char2 = char2; + dp->result = n; + ++user_digraphs.ga_len; + } +} + +/* + * Check the characters are valid for a digraph. + * If they are valid, returns TRUE; otherwise, give an error message and + * returns FALSE. + */ + int +check_digraph_chars_valid(int char1, int char2) +{ + if (char2 == 0) + { + char_u msg[MB_MAXBYTES + 1]; + + msg[mb_char2bytes(char1, msg)] = NUL; + + semsg(_(e_digraph_must_be_just_two_characters_str), msg); + return FALSE; + } + if (char1 == ESC || char2 == ESC) + { + emsg(_("E104: Escape not allowed in digraph")); + return FALSE; + } + return TRUE; +} + + + /* * Add the digraphs in the argument to the digraph table. * format: {c1}{c2} char {c1}{c2} char ... @@ -2000,8 +2059,6 @@ getdigraph(int char1, int char2, int meta_char) putdigraph(char_u *str) { int char1, char2, n; - int i; - digr_T *dp; while (*str != NUL) { @@ -2010,16 +2067,10 @@ putdigraph(char_u *str) return; char1 = *str++; char2 = *str++; - if (char2 == 0) - { - emsg(_(e_invarg)); - return; - } - if (char1 == ESC || char2 == ESC) - { - emsg(_("E104: Escape not allowed in digraph")); + + if (!check_digraph_chars_valid(char1, char2)) return; - } + str = skipwhite(str); if (!VIM_ISDIGIT(*str)) { @@ -2028,30 +2079,7 @@ putdigraph(char_u *str) } n = getdigits(&str); - // If the digraph already exists, replace the result. - dp = (digr_T *)user_digraphs.ga_data; - for (i = 0; i < user_digraphs.ga_len; ++i) - { - if ((int)dp->char1 == char1 && (int)dp->char2 == char2) - { - dp->result = n; - break; - } - ++dp; - } - - // Add a new digraph to the table. - if (i == user_digraphs.ga_len) - { - if (ga_grow(&user_digraphs, 1) == OK) - { - dp = (digr_T *)user_digraphs.ga_data + user_digraphs.ga_len; - dp->char1 = char1; - dp->char2 = char2; - dp->result = n; - ++user_digraphs.ga_len; - } - } + registerdigraph(char1, char2, n); } } @@ -2114,6 +2142,97 @@ listdigraphs(int use_headers) // wrong, in which case we messed up ScreenLines } + static void +getdigraphlist_appendpair(digr_T *dp, list_T *l) +{ + char_u buf[30]; + char_u *p; + list_T *l2; + listitem_T *li, *li2; + + + li = listitem_alloc(); + if (li == NULL) + return; + list_append(l, li); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = 0; + + l2 = list_alloc(); + li->li_tv.vval.v_list = l2; + if (l2 == NULL) + return; + ++l2->lv_refcount; + + li2 = listitem_alloc(); + if (li2 == NULL) + return; + list_append(l2, li2); + li2->li_tv.v_type = VAR_STRING; + li2->li_tv.v_lock = 0; + + buf[0] = dp->char1; + buf[1] = dp->char2; + buf[2] = NUL; + li2->li_tv.vval.v_string = vim_strsave(&buf[0]); + + li2 = listitem_alloc(); + if (li2 == NULL) + return; + list_append(l2, li2); + li2->li_tv.v_type = VAR_STRING; + li2->li_tv.v_lock = 0; + + p = buf; + if (has_mbyte) + p += (*mb_char2bytes)(dp->result, p); + else + *p++ = (char_u)dp->result; + *p = NUL; + + li2->li_tv.vval.v_string = vim_strsave(buf); +} + + void +getdigraphlist_common(int list_all, typval_T *rettv) +{ + int i; + digr_T *dp; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (list_all) + { + dp = digraphdefault; + for (i = 0; dp->char1 != NUL && !got_int; ++i) + { +#ifdef USE_UNICODE_DIGRAPHS + digr_T tmp; + + tmp.char1 = dp->char1; + tmp.char2 = dp->char2; + tmp.result = getexactdigraph(tmp.char1, tmp.char2, FALSE); + if (tmp.result != 0 && tmp.result != tmp.char2 + && (has_mbyte || tmp.result <= 255)) + getdigraphlist_appendpair(&tmp, rettv->vval.v_list); +#else + if (getexactdigraph(dp->char1, dp->char2, FALSE) == dp->result + && (has_mbyte || dp->result <= 255)) + getdigraphlist_appendpair(dp, rettv->vval.v_list); +#endif + ++dp; + } + } + + dp = (digr_T *)user_digraphs.ga_data; + for (i = 0; i < user_digraphs.ga_len && !got_int; ++i) + { + getdigraphlist_appendpair(dp, rettv->vval.v_list); + ++dp; + } +} + static struct dg_header_entry { int dg_start; char *dg_header; @@ -2210,8 +2329,207 @@ printdigraph(digr_T *dp, result_T *previous) } } +# ifdef FEAT_EVAL +/* + * Get the two digraph characters from a typval. + * Return OK or FAIL. + */ + static int +get_digraph_chars(typval_T *arg, int *char1, int *char2) +{ + char_u buf_chars[NUMBUFLEN]; + char_u *chars = tv_get_string_buf_chk(arg, buf_chars); + char_u *p = chars; + + if (p != NULL) + { + if (*p != NUL) + { + *char1 = mb_cptr2char_adv(&p); + if (*p != NUL) + { + *char2 = mb_cptr2char_adv(&p); + if (*p == NUL) + { + if (check_digraph_chars_valid(*char1, *char2)) + return OK; + return FAIL; + } + } + } + } + semsg(_(e_digraph_must_be_just_two_characters_str), chars); + return FAIL; +} + + static int +setdigraph_common(typval_T *argchars, typval_T *argdigraph) +{ + int char1, char2; + char_u *digraph; + char_u *p; + char_u buf_digraph[NUMBUFLEN]; + varnumber_T n; + + if (get_digraph_chars(argchars, &char1, &char2) == FAIL) + return FALSE; + + digraph = tv_get_string_buf_chk(argdigraph, buf_digraph); + if (digraph == NULL) + return FALSE; + p = digraph; + n = mb_cptr2char_adv(&p); + if (*p != NUL) + { + semsg(_(e_digraph_argument_must_be_one_character_str), digraph); + return FALSE; + } + + registerdigraph(char1, char2, (int)n); + return TRUE; +} +# endif + #endif // FEAT_DIGRAPHS +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * "getdigraph()" function + */ + void +f_getdigraph(typval_T *argvars, typval_T *rettv) +{ +# ifdef FEAT_DIGRAPHS + int code; + char_u buf[NUMBUFLEN]; + char_u *digraphs; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; // Return empty string for failure + digraphs = tv_get_string_chk(&argvars[0]); + + if (digraphs == NULL) + return; + else if (STRLEN(digraphs) != 2) + { + semsg(_(e_digraph_must_be_just_two_characters_str), digraphs); + return; + } + code = getdigraph(digraphs[0], digraphs[1], FALSE); + + if (has_mbyte) + buf[(*mb_char2bytes)(code, buf)] = NUL; + else { + buf[0] = code; + buf[1] = NUL; + } + + rettv->vval.v_string = vim_strsave(buf); +# else + emsg(_(e_no_digraphs_version)); +# endif +} + +/* + * "getdigraphlist()" function + */ + void +f_getdigraphlist(typval_T *argvars, typval_T *rettv) +{ +# ifdef FEAT_DIGRAPHS + int flag_list_all; + + if (argvars[0].v_type == VAR_UNKNOWN) + flag_list_all = FALSE; + else + { + int error = FALSE; + varnumber_T flag = tv_get_number_chk(&argvars[0], &error); + if (error) + return; + flag_list_all = flag ? TRUE : FALSE; + } + + getdigraphlist_common(flag_list_all, rettv); +# else + emsg(_(e_no_digraphs_version)); +# endif +} + +/* + * "setdigraph()" function + */ + void +f_setdigraph(typval_T *argvars, typval_T *rettv) +{ +# ifdef FEAT_DIGRAPHS + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = VVAL_FALSE; + + if (!setdigraph_common(&argvars[0], &argvars[1])) + return; + + rettv->vval.v_number = VVAL_TRUE; +# else + emsg(_(e_no_digraphs_version)); +# endif +} + +/* + * "setdigraphlist()" function + */ + void +f_setdigraphlist(typval_T * argvars, typval_T *rettv) +{ +# ifdef FEAT_DIGRAPHS + list_T *pl, *l; + listitem_T *pli; + + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = VVAL_FALSE; + + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + pl = argvars[0].vval.v_list; + if (pl == NULL) + { + // Empty list always results in success. + rettv->vval.v_number = VVAL_TRUE; + return; + } + + FOR_ALL_LIST_ITEMS(pl, pli) + { + if (pli->li_tv.v_type != VAR_LIST) + { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + l = pli->li_tv.vval.v_list; + if (l == NULL || l->lv_len != 2) + { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + if (!setdigraph_common(&l->lv_first->li_tv, + &l->lv_first->li_next->li_tv)) + return; + } + rettv->vval.v_number = VVAL_TRUE; +# else + emsg(_(e_no_digraphs_version)); +# endif +} + +#endif // FEAT_EVAL + + #if defined(FEAT_KEYMAP) || defined(PROTO) // structure used for b_kmap_ga.ga_data diff --git a/src/errors.h b/src/errors.h index 97e7a12599..98a10987fa 100644 --- a/src/errors.h +++ b/src/errors.h @@ -54,6 +54,10 @@ EXTERN char e_undefined_variable_str[] EXTERN char e_undefined_variable_char_str[] INIT(= N_("E121: Undefined variable: %c:%s")); #endif +#ifndef FEAT_DIGRAPHS +EXTERN char e_no_digraphs_version[] + INIT(= N_("E196: No digraphs in this version")); +#endif EXTERN char e_ambiguous_use_of_user_defined_command[] INIT(= N_("E464: Ambiguous use of user-defined command")); EXTERN char e_invalid_command[] @@ -508,3 +512,11 @@ EXTERN char e_bool_required_for_argument_nr[] INIT(= N_("E1212: Bool required for argument %d")); EXTERN char e_redefining_imported_item_str[] INIT(= N_("E1213: Redefining imported item %s")); +#if defined(FEAT_DIGRAPHS) && defined(FEAT_EVAL) +EXTERN char e_digraph_must_be_just_two_characters_str[] + INIT(= N_("E1214: Digraph must be just two characters: %s")); +EXTERN char e_digraph_argument_must_be_one_character_str[] + INIT(= N_("E1215: Digraph must be one character: %s")); +EXTERN char e_setdigraphlist_argument_must_be_list_of_lists_with_two_items[] + INIT(= N_("E1216: setdigraphlist() argument must be a list of lists with two items")); +#endif diff --git a/src/evalfunc.c b/src/evalfunc.c index 2964d23912..1a7f427815 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -493,6 +493,7 @@ static argcheck_T arg1_chan_or_job[] = {arg_chan_or_job}; static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; static argcheck_T arg2_number[] = {arg_number, arg_number}; static argcheck_T arg2_string[] = {arg_string, arg_string}; +static argcheck_T arg2_string_number[] = {arg_string, arg_number}; static argcheck_T arg2_list_nr[] = {arg_list_number, arg_list_number}; static argcheck_T arg2_nr_string[] = {arg_number, arg_string}; static argcheck_T arg2_dict_string[] = {arg_dict_any, arg_string}; @@ -585,6 +586,12 @@ ret_list_items(int argcount UNUSED, type_T **argtypes UNUSED) { return &t_list_list_any; } + + static type_T * +ret_list_string_items(int argcount UNUSED, type_T **argtypes UNUSED) +{ + return &t_list_list_string; +} static type_T * ret_dict_any(int argcount UNUSED, type_T **argtypes UNUSED) { @@ -1107,6 +1114,10 @@ static funcentry_T global_functions[] = ret_list_number, f_getcursorcharpos}, {"getcwd", 0, 2, FEARG_1, arg2_number, ret_string, f_getcwd}, + {"getdigraph", 1, 1, FEARG_1, arg1_string, + ret_string, f_getdigraph}, + {"getdigraphlist", 0, 1, FEARG_1, arg1_number, + ret_list_string_items, f_getdigraphlist}, {"getenv", 1, 1, FEARG_1, arg1_string, ret_any, f_getenv}, {"getfontname", 0, 1, 0, arg1_string, @@ -1567,6 +1578,10 @@ static funcentry_T global_functions[] = ret_number_bool, f_setcmdpos}, {"setcursorcharpos", 1, 3, FEARG_1, NULL, ret_number_bool, f_setcursorcharpos}, + {"setdigraph", 2, 2, FEARG_1, arg2_string_number, + ret_bool, f_setdigraph}, + {"setdigraphlist", 1, 1, FEARG_1, arg1_list_string, + ret_bool, f_setdigraphlist}, {"setenv", 2, 2, FEARG_2, NULL, ret_void, f_setenv}, {"setfperm", 2, 2, FEARG_1, arg2_string, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 62d50b2d95..9eb423e0d6 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -9315,7 +9315,7 @@ ex_digraphs(exarg_T *eap UNUSED) else listdigraphs(eap->forceit); #else - emsg(_("E196: No digraphs in this version")); + emsg(_(e_no_digraphs_version)); #endif } diff --git a/src/globals.h b/src/globals.h index 7829f2a4e2..5f1b774cc7 100644 --- a/src/globals.h +++ b/src/globals.h @@ -442,6 +442,7 @@ EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL EXTERN type_T t_list_job INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_job, NULL); EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL); EXTERN type_T t_list_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_list_any, NULL); +EXTERN type_T t_list_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_list_string, NULL); EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL); EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL); @@ -1805,6 +1806,7 @@ EXTERN char e_nowhitespace[] INIT(= N_("E274: No white space allowed before pare EXTERN char e_lock_unlock[] INIT(= N_("E940: Cannot lock or unlock variable %s")); #endif + #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) EXTERN char e_alloc_color[] INIT(= N_("E254: Cannot allocate color %s")); #endif diff --git a/src/proto/digraph.pro b/src/proto/digraph.pro index 06b33412d4..3b4fe878a4 100644 --- a/src/proto/digraph.pro +++ b/src/proto/digraph.pro @@ -3,8 +3,14 @@ int do_digraph(int c); char_u *get_digraph_for_char(int val_arg); int get_digraph(int cmdline); int getdigraph(int char1, int char2, int meta_char); +int check_digraph_chars_valid(int char1, int char2); void putdigraph(char_u *str); void listdigraphs(int use_headers); +void getdigraphlist_common(int list_all, typval_T *rettv); +void f_getdigraph(typval_T *argvars, typval_T *rettv); +void f_getdigraphlist(typval_T *argvars, typval_T *rettv); +void f_setdigraph(typval_T *argvars, typval_T *rettv); +void f_setdigraphlist(typval_T *argvars, typval_T *rettv); char *keymap_init(void); void ex_loadkeymap(exarg_T *eap); void keymap_clear(garray_T *kmap); diff --git a/src/testdir/test_digraph.vim b/src/testdir/test_digraph.vim index 22c133075d..2cf07510e9 100644 --- a/src/testdir/test_digraph.vim +++ b/src/testdir/test_digraph.vim @@ -214,7 +214,7 @@ func Test_digraphs() call assert_fails('exe "digraph a\ 100"', 'E104:') call assert_fails('exe "digraph \a 100"', 'E104:') call assert_fails('digraph xy z', 'E39:') - call assert_fails('digraph x', 'E474:') + call assert_fails('digraph x', 'E1214:') bw! endfunc @@ -515,4 +515,81 @@ func Test_entering_digraph() call StopVimInTerminal(buf) endfunc +func Test_setdigraph_function() + new + call setdigraph('aa', 'あ') + call Put_Dig('aa') + call assert_equal('あ', getline('$')) + call setdigraph(' i', 'い') + call Put_Dig(' i') + call assert_equal('い', getline('$')) + call setdigraph(' ', 'う') + call Put_Dig(' ') + call assert_equal('う', getline('$')) + + eval 'aa'->setdigraph('え') + call Put_Dig('aa') + call assert_equal('え', getline('$')) + + call assert_fails('call setdigraph("aaa", "あ")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call setdigraph("b", "あ")', 'E1214: Digraph must be just two characters: b') + call assert_fails('call setdigraph("あ", "あ")', 'E1214: Digraph must be just two characters: あ') + call assert_fails('call setdigraph("aa", "ああ")', 'E1215: Digraph must be one character: ああ') + call assert_fails('call setdigraph("aa", "か" .. nr2char(0x3099))', 'E1215: Digraph must be one character: か' .. nr2char(0x3099)) + bwipe! +endfunc + +func Test_getdigraph_function() + " Built-in digraphs + call assert_equal('∞', getdigraph('00')) + + " User-defined digraphs + call setdigraph('aa', 'あ') + call setdigraph(' i', 'い') + call setdigraph(' ', 'う') + call assert_equal('あ', getdigraph('aa')) + call assert_equal('あ', 'aa'->getdigraph()) + call assert_equal('い', getdigraph(' i')) + call assert_equal('う', getdigraph(' ')) + call assert_fails('call getdigraph("aaa")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call getdigraph("b")', 'E1214: Digraph must be just two characters: b') +endfunc + +func Test_getdigraph_function_encode() + CheckFeature iconv + let testcases = { + \'00': '∞', + \'aa': 'あ', + \} + for [key, ch] in items(testcases) + call setdigraph(key, ch) + set encoding=japan + call assert_equal(iconv(ch, 'utf-8', 'japan'), getdigraph(key)) + set encoding& + endfor +endfunc + +func Test_setdigraphlist_function() + call setdigraphlist([['aa', 'き'], ['bb', 'く']]) + call assert_equal('き', getdigraph('aa')) + call assert_equal('く', getdigraph('bb')) + + call assert_fails('call setdigraphlist([[]])', 'E1216:') + call assert_fails('call setdigraphlist([["aa", "b", "cc"]])', '1216:') + call assert_fails('call setdigraphlist([["あ", "あ"]])', 'E1214: Digraph must be just two characters: あ') +endfunc + +func Test_getdigraphlist_function() + " Make sure user-defined digraphs are defined + call setdigraphlist([['aa', 'き'], ['bb', 'く']]) + + for pair in getdigraphlist(1) + call assert_equal(getdigraph(pair[0]), pair[1]) + endfor + + " We don't know how many digraphs are registered before, so check the number + " of digraphs returned. + call assert_equal(getdigraphlist()->len(), getdigraphlist(0)->len()) + call assert_notequal((getdigraphlist()->len()), getdigraphlist(1)->len()) +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index a1f79b6ba3..c6e3fafc5a 100644 --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3184, /**/ 3183, /**/ -- cgit v1.2.3