summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormityu <mityu.mail@gmail.com>2021-07-19 20:07:21 +0200
committerBram Moolenaar <Bram@vim.org>2021-07-19 20:07:21 +0200
commit6106504e9edc8500131f7a36e59bc146f90180fa (patch)
tree69249dd7c9375375cb414ea31e3b5523e8947344
parent98c2eaed27579602e05e7b96aa3a60428a8b9cb2 (diff)
downloadvim-git-6106504e9edc8500131f7a36e59bc146f90180fa.tar.gz
patch 8.2.3184: cannot add a digraph with a leading spacev8.2.3184
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)
-rw-r--r--runtime/doc/digraph.txt3
-rw-r--r--runtime/doc/eval.txt107
-rw-r--r--runtime/doc/usr_41.txt4
-rw-r--r--src/digraph.c388
-rw-r--r--src/errors.h12
-rw-r--r--src/evalfunc.c15
-rw-r--r--src/ex_docmd.c2
-rw-r--r--src/globals.h2
-rw-r--r--src/proto/digraph.pro6
-rw-r--r--src/testdir/test_digraph.vim79
-rw-r--r--src/version.c2
11 files changed, 583 insertions, 37 deletions
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index 79c7cf042..9715a8846 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -38,6 +38,9 @@ An alternative is using the 'keymap' option.
< Avoid defining a digraph with '_' (underscore) as the
first character, it has a special meaning in the
future.
+ NOTE: This command cannot add a digraph that starts
+ with a white space. If you want to add such digraph,
+ you can use |setdigraph()| instead.
Vim is normally compiled with the |+digraphs| feature. If the feature is
disabled, the ":digraph" command will display an error message.
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index e5f9b4651..f4ab159a7 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2620,6 +2620,8 @@ getcompletion({pat}, {type} [, {filtered}])
getcurpos([{winnr}]) List position of the cursor
getcursorcharpos([{winnr}]) List character position of the cursor
getcwd([{winnr} [, {tabnr}]]) String get the current working directory
+getdigraph({chars}) String get the digraph of {chars}
+getdigraphlist([{listall}]) List get all |digraph|s
getenv({name}) String return environment variable
getfontname([{name}]) String name of font being used
getfperm({fname}) String file permissions of file {fname}
@@ -2886,6 +2888,8 @@ setcharpos({expr}, {list}) Number set the {expr} position to {list}
setcharsearch({dict}) Dict set character search from {dict}
setcmdpos({pos}) Number set cursor position in command-line
setcursorcharpos({list}) Number move cursor to position in {list}
+setdigraph({chars}, {digraph}) Boolean register |digraph|
+setdigraphlist({digraphlist}) Boolean register multiple |digraph|s
setenv({name}, {val}) none set environment variable
setfperm({fname}, {mode}) Number set {fname} file permissions to {mode}
setline({lnum}, {line}) Number set line {lnum} to {line}
@@ -5572,6 +5576,61 @@ getcwd([{winnr} [, {tabnr}]])
< Can also be used as a |method|: >
GetWinnr()->getcwd()
<
+ *getdigraph()* *E1214*
+getdigraph({chars})
+ Return the digraph of {chars}. This should be a string with
+ exactly two characters. If {chars} are not just two
+ characters, or the digraph of {chars} does not exist, an error
+ is given and an empty string is returned.
+
+ The character will be converted from Unicode to 'encoding'
+ when needed. This does require the conversion to be
+ available, it might fail.
+
+ Also see |getdigraphlist()|.
+
+ Examples: >
+ " Get a built-in digraph
+ :echo getdigraph('00') " Returns '∞'
+
+ " Get a user-defined digraph
+ :call setdigraph('aa', 'あ')
+ :echo getdigraph('aa') " Returns 'あ'
+<
+ Can also be used as a |method|: >
+ GetChars()->getdigraph()
+<
+ This function works only when compiled with the |+digraphs|
+ feature. If this feature is disabled, this function will
+ display an error message.
+
+
+getdigraphlist([{listall}]) *getdigraphlist()*
+ Return a list of digraphs. If the {listall} argument is given
+ and it is TRUE, return all digraphs, including the default
+ digraphs. Otherwise, return only user-defined digraphs.
+
+ The characters will be converted from Unicode to 'encoding'
+ when needed. This does require the conservation to be
+ available, it might fail.
+
+ Also see |getdigraph()|.
+
+ Examples: >
+ " Get user-defined digraphs
+ :echo getdigraphlist()
+
+ " Get all the digraphs, including default digraphs
+ :echo digraphlist(1)
+<
+ Can also be used as a |method|: >
+ GetNumber()->getdigraphlist()
+<
+ This function works only when compiled with the |+digraphs|
+ feature. If this feature is disabled, this function will
+ display an error message.
+
+
getenv({name}) *getenv()*
Return the value of environment variable {name}.
When the variable does not exist |v:null| is returned. That
@@ -9490,6 +9549,54 @@ setcursorcharpos({list})
Can also be used as a |method|: >
GetCursorPos()->setcursorcharpos()
+
+setdigraph({chars}, {digraph}) *setdigraph()* *E1205*
+ Add digraph {chars} to the list. {chars} must be a string
+ with two characters. {digraph} is a string with one utf-8
+ encoded character. Be careful, composing characters are NOT
+ ignored. This function is similar to |:digraphs| command, but
+ useful to add digraphs start with a white space.
+
+ The function result is v:true if |digraph| is registered. If
+ this fails an error message is given and v:false is returned.
+
+ If you want to define multiple digraphs at once, you can use
+ |setdigraphlist()|.
+
+ Example: >
+ call setdigraph(' ', 'あ')
+<
+ Can be used as a |method|: >
+ GetString()->setdigraph('あ')
+<
+ This function works only when compiled with the |+digraphs|
+ feature. If this feature is disabled, this function will
+ display an error message.
+
+
+setdigraphlist({digraphlist}) *setdigraphlist()*
+ Similar to |setdigraph()| but this function can add multiple
+ digraphs at once. {digraphlist} is a list composed of lists,
+ where each list contains two strings with {chars} and
+ {digraph} as in |setdigraph()|.
+ Example: >
+ call setdigraphlist([['aa', 'あ'], ['ii', 'い']])
+<
+ It is similar to the following: >
+ for [chars, digraph] in [['aa', 'あ'], ['ii', 'い']]
+ call setdigraph(chars, digraph)
+ endfor
+< Except that the function returns after the first error,
+ following digraphs will not be added.
+
+ Can be used as a |method|: >
+ GetList()->setdigraphlist()
+<
+ This function works only when compiled with the |+digraphs|
+ feature. If this feature is disabled, this function will
+ display an error message.
+
+
setenv({name}, {val}) *setenv()*
Set environment variable {name} to {val}.
When {val} is |v:null| the environment variable is deleted.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 54e5e2930..df9ebe639 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1000,6 +1000,10 @@ Mappings and Menus: *mapping-functions*
mapset() restore a mapping
menu_info() get information about a menu item
wildmenumode() check if the wildmode is active
+ getdigraph() get |digraph|
+ getdigraphlist() get all |digraph|s
+ setdigraph() register |digraph|
+ setdigraphlist() register multiple |digraph|s
Testing: *test-functions*
assert_equal() assert that two expressions values are equal
diff --git a/src/digraph.c b/src/digraph.c
index fd78a16ec..9b1836eef 100644
--- a/src/digraph.c
+++ b/src/digraph.c
@@ -1993,6 +1993,65 @@ getdigraph(int char1, int char2, int meta_char)
}
/*
+ * 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 97e7a1259..98a10987f 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 2964d2391..1a7f42781 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 62d50b2d9..9eb423e0d 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 7829f2a4e..5f1b774cc 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 06b33412d..3b4fe878a 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 22c133075..2cf07510e 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\<Esc> 100"', 'E104:')
call assert_fails('exe "digraph \<Esc>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 a1f79b6ba..c6e3fafc5 100644
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3184,
+/**/
3183,
/**/
3182,