aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/digraph.c
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
committerJosh Rahm <rahm@google.com>2022-07-18 19:37:18 +0000
commit308e1940dcd64aa6c344c403d4f9e0dda58d9c5c (patch)
tree35fe43e01755e0f312650667004487a44d6b7941 /src/nvim/digraph.c
parent96a00c7c588b2f38a2424aeeb4ea3581d370bf2d (diff)
parente8c94697bcbe23a5c7b07c292b90a6b70aadfa87 (diff)
downloadrneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.gz
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.bz2
rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.zip
Merge remote-tracking branch 'upstream/master' into rahm
Diffstat (limited to 'src/nvim/digraph.c')
-rw-r--r--src/nvim/digraph.c358
1 files changed, 278 insertions, 80 deletions
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 8eda173cac..355900c93f 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -12,11 +12,13 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/digraph.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -34,6 +36,12 @@ typedef struct digraph {
result_T result;
} digr_T;
+static char e_digraph_must_be_just_two_characters_str[]
+ = N_("E1214: Digraph must be just two characters: %s");
+static char e_digraph_argument_must_be_one_character_str[]
+ = N_("E1215: Digraph must be one character: %s");
+static char e_digraph_setlist_argument_must_be_list_of_lists_with_two_items[]
+ = N_("E1216: digraph_setlist() argument must be a list of lists with two items");
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "digraph.c.generated.h"
@@ -1458,7 +1466,7 @@ int do_digraph(int c)
backspaced = -1;
} else if (p_dg) {
if (backspaced >= 0) {
- c = getdigraph(backspaced, c, false);
+ c = digraph_get(backspaced, c, false);
}
backspaced = -1;
@@ -1475,17 +1483,16 @@ int do_digraph(int c)
char_u *get_digraph_for_char(int val_arg)
{
const int val = val_arg;
- digr_T *dp;
+ const digr_T *dp;
static char_u r[3];
for (int use_defaults = 0; use_defaults <= 1; use_defaults++) {
if (use_defaults == 0) {
- dp = (digr_T *)user_digraphs.ga_data;
+ dp = (const digr_T *)user_digraphs.ga_data;
} else {
dp = digraphdefault;
}
- for (int i = 0;
- use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) {
+ for (int i = 0; use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) {
if (dp->result == val) {
r[0] = dp->char1;
r[1] = dp->char2;
@@ -1506,10 +1513,11 @@ char_u *get_digraph_for_char(int val_arg)
/// @returns composed character, or NUL when ESC was used.
int get_digraph(bool cmdline)
{
- int cc;
no_mapping++;
+ allow_keys++;
int c = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (c != ESC) {
// ESC cancels CTRL-K
@@ -1526,12 +1534,14 @@ int get_digraph(bool cmdline)
add_to_showcmd(c);
}
no_mapping++;
- cc = plain_vgetc();
+ allow_keys++;
+ int cc = plain_vgetc();
no_mapping--;
+ allow_keys--;
if (cc != ESC) {
// ESC cancels CTRL-K
- return getdigraph(c, cc, true);
+ return digraph_get(c, cc, true);
}
}
return NUL;
@@ -1546,6 +1556,7 @@ int get_digraph(bool cmdline)
/// @return If no match, return "char2". If "meta_char" is true and "char1"
// is a space, return "char2" | 0x80.
static int getexactdigraph(int char1, int char2, bool meta_char)
+ FUNC_ATTR_PURE
{
int retval = 0;
@@ -1554,25 +1565,25 @@ static int getexactdigraph(int char1, int char2, bool meta_char)
}
// Search user digraphs first.
- digr_T *dp = (digr_T *)user_digraphs.ga_data;
- for (int i = 0; i < user_digraphs.ga_len; ++i) {
+ const digr_T *dp = (const digr_T *)user_digraphs.ga_data;
+ for (int i = 0; i < user_digraphs.ga_len; i++) {
if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
retval = dp->result;
break;
}
- ++dp;
+ dp++;
}
// Search default digraphs.
if (retval == 0) {
dp = digraphdefault;
- for (int i = 0; dp->char1 != 0; ++i) {
+ for (int i = 0; dp->char1 != 0; i++) {
if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) {
retval = dp->result;
break;
}
- ++dp;
+ dp++;
}
}
@@ -1595,7 +1606,8 @@ static int getexactdigraph(int char1, int char2, bool meta_char)
/// @param meta_char
///
/// @return The digraph.
-int getdigraph(int char1, int char2, bool meta_char)
+int digraph_get(int char1, int char2, bool meta_char)
+ FUNC_ATTR_PURE
{
int retval;
@@ -1608,60 +1620,72 @@ int getdigraph(int char1, int char2, bool meta_char)
return retval;
}
+/// Add a digraph to the digraph table.
+static void registerdigraph(int char1, int char2, int n)
+{
+ // If the digraph already exists, replace "result".
+ digr_T *dp = (digr_T *)user_digraphs.ga_data;
+ for (int 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.
+ dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs);
+ dp->char1 = (char_u)char1;
+ dp->char2 = (char_u)char2;
+ dp->result = n;
+}
+
+/// Check the characters are valid for a digraph.
+/// If they are valid, returns true; otherwise, give an error message and
+/// returns false.
+bool check_digraph_chars_valid(int char1, int char2)
+{
+ if (char2 == 0) {
+ char_u msg[MB_MAXBYTES + 1];
+ msg[utf_char2bytes(char1, (char *)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 ...
///
/// @param str
void putdigraph(char_u *str)
{
- char_u char1, char2;
- digr_T *dp;
-
while (*str != NUL) {
- str = skipwhite(str);
+ str = (char_u *)skipwhite((char *)str);
if (*str == NUL) {
return;
}
- char1 = *str++;
- char2 = *str++;
+ char_u char1 = *str++;
+ char_u char2 = *str++;
- if (char2 == 0) {
- emsg(_(e_invarg));
+ if (!check_digraph_chars_valid(char1, char2)) {
return;
}
- if ((char1 == ESC) || (char2 == ESC)) {
- emsg(_("E104: Escape not allowed in digraph"));
- return;
- }
- str = skipwhite(str);
+ str = (char_u *)skipwhite((char *)str);
if (!ascii_isdigit(*str)) {
emsg(_(e_number_exp));
return;
}
- int n = getdigits_int(&str, true, 0);
+ int n = getdigits_int((char **)&str, true, 0);
- // If the digraph already exists, replace the result.
- dp = (digr_T *)user_digraphs.ga_data;
-
- int i;
- 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) {
- dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs);
- dp->char1 = char1;
- dp->char2 = char2;
- dp->result = n;
- }
+ registerdigraph(char1, char2, n);
}
}
@@ -1677,14 +1701,13 @@ static void digraph_header(const char *msg)
void listdigraphs(bool use_headers)
{
- digr_T *dp;
result_T previous = 0;
msg_putchar('\n');
- dp = digraphdefault;
+ const digr_T *dp = digraphdefault;
- for (int i = 0; dp->char1 != NUL && !got_int; ++i) {
+ for (int i = 0; dp->char1 != NUL && !got_int; i++) {
digr_T tmp;
// May need to convert the result to 'encoding'.
@@ -1692,15 +1715,14 @@ void listdigraphs(bool use_headers)
tmp.char2 = dp->char2;
tmp.result = getexactdigraph(tmp.char1, tmp.char2, false);
- if ((tmp.result != 0)
- && (tmp.result != tmp.char2)) {
+ if (tmp.result != 0 && tmp.result != tmp.char2) {
printdigraph(&tmp, use_headers ? &previous : NULL);
}
dp++;
fast_breakcheck();
}
- dp = (digr_T *)user_digraphs.ga_data;
+ dp = (const digr_T *)user_digraphs.ga_data;
for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) {
if (previous >= 0 && use_headers) {
digraph_header(_("Custom"));
@@ -1712,6 +1734,50 @@ void listdigraphs(bool use_headers)
}
}
+static void digraph_getlist_appendpair(const digr_T *dp, list_T *l)
+{
+ list_T *l2 = tv_list_alloc(2);
+ tv_list_append_list(l, l2);
+
+ char_u buf[30];
+ buf[0] = dp->char1;
+ buf[1] = dp->char2;
+ buf[2] = NUL;
+ tv_list_append_string(l2, (char *)buf, -1);
+
+ char_u *p = buf;
+ p += utf_char2bytes(dp->result, (char *)p);
+ *p = NUL;
+ tv_list_append_string(l2, (char *)buf, -1);
+}
+
+void digraph_getlist_common(bool list_all, typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, (int)sizeof(digraphdefault) + user_digraphs.ga_len);
+
+ const digr_T *dp;
+
+ if (list_all) {
+ dp = digraphdefault;
+ for (int i = 0; dp->char1 != NUL && !got_int; i++) {
+ 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) {
+ digraph_getlist_appendpair(&tmp, rettv->vval.v_list);
+ }
+ dp++;
+ }
+ }
+
+ dp = (const digr_T *)user_digraphs.ga_data;
+ for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) {
+ digraph_getlist_appendpair(dp, rettv->vval.v_list);
+ dp++;
+ }
+}
+
struct dg_header_entry {
int dg_start;
const char *dg_header;
@@ -1749,11 +1815,7 @@ static void printdigraph(const digr_T *dp, result_T *previous)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u buf[30];
- char_u *p;
-
- int list_width;
-
- list_width = 13;
+ int list_width = 13;
if (dp->result != 0) {
if (previous != NULL) {
@@ -1771,7 +1833,6 @@ static void printdigraph(const digr_T *dp, result_T *previous)
msg_putchar('\n');
}
-
// Make msg_col a multiple of list_width by using spaces.
if (msg_col % list_width != 0) {
int spaces = (msg_col / list_width + 1) * list_width - msg_col;
@@ -1780,19 +1841,19 @@ static void printdigraph(const digr_T *dp, result_T *previous)
}
}
- p = &buf[0];
+ char_u *p = &buf[0];
*p++ = dp->char1;
*p++ = dp->char2;
*p++ = ' ';
*p = NUL;
- msg_outtrans(buf);
+ msg_outtrans((char *)buf);
p = buf;
// add a space to draw a composing char on
if (utf_iscomposing(dp->result)) {
*p++ = ' ';
}
- p += utf_char2bytes(dp->result, p);
+ p += utf_char2bytes(dp->result, (char *)p);
*p = NUL;
msg_outtrans_attr(buf, HL_ATTR(HLF_8));
@@ -1802,10 +1863,151 @@ static void printdigraph(const digr_T *dp, result_T *previous)
}
assert(p >= buf);
vim_snprintf((char *)p, sizeof(buf) - (size_t)(p - buf), " %3d", dp->result);
- msg_outtrans(buf);
+ msg_outtrans((char *)buf);
}
}
+/// Get the two digraph characters from a typval.
+/// @return OK or FAIL.
+static int get_digraph_chars(const typval_T *arg, int *char1, int *char2)
+{
+ char buf_chars[NUMBUFLEN];
+ const char *chars = tv_get_string_buf_chk(arg, buf_chars);
+ const char_u *p = (const char_u *)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 bool digraph_set_common(const typval_T *argchars, const typval_T *argdigraph)
+{
+ int char1, char2;
+ if (get_digraph_chars(argchars, &char1, &char2) == FAIL) {
+ return false;
+ }
+
+ char buf_digraph[NUMBUFLEN];
+ const char *digraph = tv_get_string_buf_chk(argdigraph, buf_digraph);
+ if (digraph == NULL) {
+ return false;
+ }
+ const char_u *p = (const char_u *)digraph;
+ int n = mb_cptr2char_adv(&p);
+ if (*p != NUL) {
+ semsg(_(e_digraph_argument_must_be_one_character_str), digraph);
+ return false;
+ }
+
+ registerdigraph(char1, char2, n);
+ return true;
+}
+
+/// "digraph_get()" function
+void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL; // Return empty string for failure
+ const char *digraphs = tv_get_string_chk(&argvars[0]);
+
+ if (digraphs == NULL) {
+ return;
+ }
+ if (STRLEN(digraphs) != 2) {
+ semsg(_(e_digraph_must_be_just_two_characters_str), digraphs);
+ return;
+ }
+ int code = digraph_get(digraphs[0], digraphs[1], false);
+
+ char_u buf[NUMBUFLEN];
+ buf[utf_char2bytes(code, (char *)buf)] = NUL;
+ rettv->vval.v_string = (char *)vim_strsave(buf);
+}
+
+/// "digraph_getlist()" function
+void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ bool flag_list_all;
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ flag_list_all = false;
+ } else {
+ bool error = false;
+ varnumber_T flag = tv_get_number_chk(&argvars[0], &error);
+ if (error) {
+ return;
+ }
+ flag_list_all = flag != 0;
+ }
+
+ digraph_getlist_common(flag_list_all, rettv);
+}
+
+/// "digraph_set()" function
+void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_bool = kBoolVarFalse;
+
+ if (!digraph_set_common(&argvars[0], &argvars[1])) {
+ return;
+ }
+
+ rettv->vval.v_bool = kBoolVarTrue;
+}
+
+/// "digraph_setlist()" function
+void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_BOOL;
+ rettv->vval.v_bool = kBoolVarFalse;
+
+ if (argvars[0].v_type != VAR_LIST) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ list_T *pl = argvars[0].vval.v_list;
+ if (pl == NULL) {
+ // Empty list always results in success.
+ rettv->vval.v_bool = kBoolVarTrue;
+ return;
+ }
+
+ TV_LIST_ITER_CONST(pl, pli, {
+ if (TV_LIST_ITEM_TV(pli)->v_type != VAR_LIST) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ list_T *l = TV_LIST_ITEM_TV(pli)->vval.v_list;
+ if (l == NULL || tv_list_len(l) != 2) {
+ emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items));
+ return;
+ }
+
+ if (!digraph_set_common(TV_LIST_ITEM_TV(tv_list_first(l)),
+ TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))))) {
+ return;
+ }
+ });
+
+ rettv->vval.v_bool = kBoolVarTrue;
+}
+
/// structure used for b_kmap_ga.ga_data
typedef struct {
char_u *from;
@@ -1814,7 +2016,6 @@ typedef struct {
#define KMAP_MAXLEN 20 // maximum length of "from" or "to"
-
/// Set up key mapping tables for the 'keymap' option.
///
/// @return NULL if OK, an error message for failure. This only needs to be
@@ -1863,13 +2064,11 @@ char *keymap_init(void)
/// @param eap
void ex_loadkeymap(exarg_T *eap)
{
- char_u *line;
- char_u *p;
char_u *s;
#define KMAP_LLEN 200 // max length of "to" and "from" together
char_u buf[KMAP_LLEN + 11];
- char_u *save_cpo = p_cpo;
+ char *save_cpo = p_cpo;
if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
emsg(_("E105: Using :loadkeymap not in a sourced file"));
@@ -1883,23 +2082,23 @@ void ex_loadkeymap(exarg_T *eap)
ga_init(&curbuf->b_kmap_ga, (int)sizeof(kmap_T), 20);
// Set 'cpoptions' to "C" to avoid line continuation.
- p_cpo = (char_u *)"C";
+ p_cpo = "C";
// Get each line of the sourced file, break at the end.
for (;;) {
- line = eap->getline(0, eap->cookie, 0, true);
+ char *line = eap->getline(0, eap->cookie, 0, true);
if (line == NULL) {
break;
}
- p = skipwhite(line);
+ char_u *p = (char_u *)skipwhite(line);
if ((*p != '"') && (*p != NUL)) {
kmap_T *kp = GA_APPEND_VIA_PTR(kmap_T, &curbuf->b_kmap_ga);
s = skiptowhite(p);
kp->from = vim_strnsave(p, (size_t)(s - p));
- p = skipwhite(s);
+ p = (char_u *)skipwhite((char *)s);
s = skiptowhite(p);
kp->to = vim_strnsave(p, (size_t)(s - p));
@@ -1911,7 +2110,7 @@ void ex_loadkeymap(exarg_T *eap)
}
xfree(kp->from);
xfree(kp->to);
- --curbuf->b_kmap_ga.ga_len;
+ curbuf->b_kmap_ga.ga_len--;
}
}
xfree(line);
@@ -1922,7 +2121,7 @@ void ex_loadkeymap(exarg_T *eap)
vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s %s",
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from,
((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to);
- (void)do_map(0, buf, LANGMAP, false);
+ (void)do_map(0, buf, MODE_LANGMAP, false);
}
p_cpo = save_cpo;
@@ -1944,23 +2143,22 @@ void keymap_ga_clear(garray_T *kmap_ga)
/// Stop using 'keymap'.
static void keymap_unload(void)
{
- char_u buf[KMAP_MAXLEN + 10];
- char_u *save_cpo = p_cpo;
- kmap_T *kp;
+ char buf[KMAP_MAXLEN + 10];
+ char *save_cpo = p_cpo;
if (!(curbuf->b_kmap_state & KEYMAP_LOADED)) {
return;
}
// Set 'cpoptions' to "C" to avoid line continuation.
- p_cpo = (char_u *)"C";
+ p_cpo = "C";
// clear the ":lmap"s
- kp = (kmap_T *)curbuf->b_kmap_ga.ga_data;
+ kmap_T *kp = (kmap_T *)curbuf->b_kmap_ga.ga_data;
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
- vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s", kp[i].from);
- (void)do_map(1, buf, LANGMAP, false);
+ vim_snprintf(buf, sizeof(buf), "<buffer> %s", kp[i].from);
+ (void)do_map(1, (char_u *)buf, MODE_LANGMAP, false);
}
keymap_ga_clear(&curbuf->b_kmap_ga);