vim-patch:8.2.4861: it is not easy to restore saved mappings

Problem:    It is not easy to restore saved mappings.
Solution:   Make mapset() accept a dict argument. (Ernie Rael, closes vim/vim#10295)

51d04d16f2

Co-authored-by: Ernie Rael <errael@raelity.com>
This commit is contained in:
zeertzjq
2023-11-09 19:48:50 +08:00
parent d4dbfb092b
commit f748a73a35
9 changed files with 350 additions and 41 deletions

View File

@@ -123,6 +123,8 @@ static const char e_mapping_already_exists_for_str[]
= N_("E227: Mapping already exists for %s");
static const char e_entries_missing_in_mapset_dict_argument[]
= N_("E460: Entries missing in mapset() dict argument");
static const char e_illegal_map_mode_string_str[]
= N_("E1276: Illegal map mode string: '%s'");
/// Get the start of the hashed map list for "state" and first character "c".
mapblock_T *get_maphash_list(int state, int c)
@@ -2070,11 +2072,12 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
///
/// @param mp The maphash that contains the mapping information
/// @param buffer_value The "buffer" value
/// @param abbr True if abbreviation
/// @param compatible True for compatible with old maparg() dict
///
/// @return A Dictionary.
static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt,
const int buffer_value, const bool compatible)
const int buffer_value, const bool abbr, const bool compatible)
FUNC_ATTR_NONNULL_ARG(1)
{
Dictionary dict = ARRAY_DICT_INIT;
@@ -2121,6 +2124,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs
PUT(dict, "replace_keycodes", INTEGER_OBJ(1));
}
PUT(dict, "mode", CSTR_AS_OBJ(mapmode));
PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0));
return dict;
}
@@ -2193,7 +2197,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
Dictionary dict = mapblock_fill_dict(mp,
did_simplify ? keys_simplified : NULL,
buffer_local, true);
buffer_local, abbr, true);
(void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL);
api_free_dictionary(dict);
} else {
@@ -2206,21 +2210,99 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
xfree(alt_keys_buf);
}
/// Get the mapping mode from the mode string.
/// It may contain multiple characters, eg "nox", or "!", or ' '
/// Return 0 if there is an error.
static int get_map_mode_string(const char *const mode_string, const bool abbr)
{
const char *p = mode_string;
const int MASK_V = MODE_VISUAL | MODE_SELECT;
const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING;
const int MASK_BANG = MODE_INSERT | MODE_CMDLINE;
if (*p == NUL) {
p = " "; // compatibility
}
int mode = 0;
int modec;
while ((modec = (uint8_t)(*p++))) {
int tmode;
switch (modec) {
case 'i':
tmode = MODE_INSERT; break;
case 'l':
tmode = MODE_LANGMAP; break;
case 'c':
tmode = MODE_CMDLINE; break;
case 'n':
tmode = MODE_NORMAL; break;
case 'x':
tmode = MODE_VISUAL; break;
case 's':
tmode = MODE_SELECT; break;
case 'o':
tmode = MODE_OP_PENDING; break;
case 't':
tmode = MODE_TERMINAL; break;
case 'v':
tmode = MASK_V; break;
case '!':
tmode = MASK_BANG; break;
case ' ':
tmode = MASK_MAP; break;
default:
return 0; // error, unknown mode character
}
mode |= tmode;
}
if ((abbr && (mode & ~MASK_BANG) != 0)
|| (!abbr && (mode & (mode - 1)) != 0 // more than one bit set
&& (
// false if multiple bits set in mode and mode is fully
// contained in one mask
!(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0)
|| ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0))))) {
return 0;
}
return mode;
}
/// "mapset()" function
void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *which;
char buf[NUMBUFLEN];
const char *which = tv_get_string_buf_chk(&argvars[0], buf);
if (which == NULL) {
return;
}
const int mode = get_map_mode((char **)&which, 0);
const bool is_abbr = tv_get_number(&argvars[1]) != 0;
int is_abbr;
dict_T *d;
if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
// If first arg is a dict, then that's the only arg permitted.
const bool dict_only = argvars[0].v_type == VAR_DICT;
if (dict_only) {
d = argvars[0].vval.v_dict;
which = tv_dict_get_string(d, "mode", false);
is_abbr = (int)tv_dict_get_bool(d, "abbr", -1);
if (which == NULL || is_abbr < 0) {
emsg(_(e_entries_missing_in_mapset_dict_argument));
return;
}
} else {
which = tv_get_string_buf_chk(&argvars[0], buf);
if (which == NULL) {
return;
}
is_abbr = (int)tv_get_bool(&argvars[1]);
if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
return;
}
d = argvars[2].vval.v_dict;
}
const int mode = get_map_mode_string(which, is_abbr);
if (mode == 0) {
semsg(_(e_illegal_map_mode_string_str), which);
return;
}
dict_T *d = argvars[2].vval.v_dict;
// Get the values in the same order as above in get_maparg().
char *lhs = tv_dict_get_string(d, "lhs", false);
@@ -2322,7 +2404,7 @@ void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
Dictionary dict = mapblock_fill_dict(mp,
did_simplify ? keys_buf : NULL,
buffer_local, true);
buffer_local, abbr, true);
typval_T d = TV_INITIAL_VALUE;
(void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL);
assert(d.v_type == VAR_DICT);
@@ -2751,7 +2833,8 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
ADD(mappings,
DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, false)));
DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL,
buffer_value, false, false)));
}
}
}