refactor: move some mapping-related code to a separate file (#19061)

This marks the following Vim patches as ported:

vim-patch:8.1.1785: map functionality mixed with character input

Problem:    Map functionality mixed with character input.
Solution:   Move the map functionality to a separate file. (Yegappan
            Lakshmanan, closes vim/vim#4740)  Graduate the +localmap feature.
b66bab381c

vim-patch:8.2.3643: header for source file is outdated

Problem:    Header for source file is outdated.
Solution:   Make the header more accurate. (closes vim/vim#9186)
a3f83feb63

Also cherry-pick a change for <unique> mappings from patch 8.2.0807.
Rename map_clear_mode() to do_mapclear().
This commit is contained in:
zeertzjq
2022-06-23 21:17:11 +08:00
committed by GitHub
parent 05ca14a881
commit 7718b75846
21 changed files with 2705 additions and 2659 deletions

View File

@ -233,6 +233,10 @@ preprocess_patch() {
LC_ALL=C sed -e 's/\( [ab]\/src\)/\1\/nvim/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename evalfunc.c to eval/funcs.c
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/evalfunc\.c/\1\/eval\/funcs\.c/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename userfunc.c to eval/userfunc.c
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"

View File

@ -22,6 +22,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"

View File

@ -21,7 +21,6 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
#include "nvim/lua/executor.h"
@ -441,142 +440,6 @@ Array string_to_array(const String input, bool crlf)
return ret;
}
/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
/// functions like @ref nvim_buf_set_keymap.
///
/// Arguments are handled like @ref nvim_set_keymap unless noted.
/// @param buffer Buffer handle for a specific buffer, or 0 for the current
/// buffer, or -1 to signify global behavior ("all buffers")
/// @param is_unmap When true, removes the mapping that matches {lhs}.
void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs,
String rhs, Dict(keymap) *opts, Error *err)
{
LuaRef lua_funcref = LUA_NOREF;
bool global = (buffer == -1);
if (global) {
buffer = 0;
}
buf_T *target_buf = find_buffer_by_handle(buffer, err);
if (!target_buf) {
return;
}
const sctx_T save_current_sctx = api_set_sctx(channel_id);
if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) {
lua_funcref = opts->callback.data.luaref;
opts->callback.data.luaref = LUA_NOREF;
}
MapArguments parsed_args = MAP_ARGUMENTS_INIT;
if (opts) {
#define KEY_TO_BOOL(name) \
parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \
if (ERROR_SET(err)) { \
goto fail_and_free; \
}
KEY_TO_BOOL(nowait);
KEY_TO_BOOL(noremap);
KEY_TO_BOOL(silent);
KEY_TO_BOOL(script);
KEY_TO_BOOL(expr);
KEY_TO_BOOL(unique);
#undef KEY_TO_BOOL
}
parsed_args.buffer = !global;
set_maparg_lhs_rhs(lhs.data, lhs.size,
rhs.data, rhs.size, lua_funcref,
CPO_TO_CPO_FLAGS, &parsed_args);
if (opts != NULL && opts->desc.type == kObjectTypeString) {
parsed_args.desc = string_to_cstr(opts->desc.data.string);
} else {
parsed_args.desc = NULL;
}
if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) {
api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
goto fail_and_free;
}
if (mode.size > 1) {
api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data);
goto fail_and_free;
}
int mode_val; // integer value of the mapping mode, to be passed to do_map()
char *p = (mode.size) ? mode.data : "m";
if (STRNCMP(p, "!", 2) == 0) {
mode_val = get_map_mode(&p, true); // mapmode-ic
} else {
mode_val = get_map_mode(&p, false);
if (mode_val == (MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING) && mode.size > 0) {
// get_map_mode() treats unrecognized mode shortnames as ":map".
// This is an error unless the given shortname was empty string "".
api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p);
goto fail_and_free;
}
}
if (parsed_args.lhs_len == 0) {
api_set_error(err, kErrorTypeValidation, "Invalid (empty) LHS");
goto fail_and_free;
}
bool is_noremap = parsed_args.noremap;
assert(!(is_unmap && is_noremap));
if (!is_unmap && lua_funcref == LUA_NOREF
&& (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
parsed_args.rhs_is_noop = true;
} else {
abort(); // should never happen
}
} else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
if (parsed_args.rhs_len) {
api_set_error(err, kErrorTypeValidation,
"Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
} else {
api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
}
goto fail_and_free;
}
// buf_do_map() reads noremap/unmap as its own argument.
int maptype_val = 0;
if (is_unmap) {
maptype_val = 1;
} else if (is_noremap) {
maptype_val = 2;
}
switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) {
case 0:
break;
case 1:
api_set_error(err, kErrorTypeException, (char *)e_invarg, 0);
goto fail_and_free;
case 2:
api_set_error(err, kErrorTypeException, (char *)e_nomap, 0);
goto fail_and_free;
case 5:
api_set_error(err, kErrorTypeException,
"E227: mapping already exists for %s", parsed_args.lhs);
goto fail_and_free;
default:
assert(false && "Unrecognized return code!");
goto fail_and_free;
} // switch
parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success
fail_and_free:
current_sctx = save_current_sctx;
NLUA_CLEAR_REF(parsed_args.rhs_lua);
xfree(parsed_args.rhs);
xfree(parsed_args.orig_rhs);
XFREE_CLEAR(parsed_args.desc);
}
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
@ -930,58 +793,6 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...)
err->type = errType;
}
/// Get an array containing dictionaries describing mappings
/// based on mode and buffer id
///
/// @param mode The abbreviation for the mode
/// @param buf The buffer to get the mapping array. NULL for global
/// @param from_lua Whether it is called from internal lua api.
/// @returns Array of maparg()-like dictionaries describing mappings
ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
{
Array mappings = ARRAY_DICT_INIT;
dict_T *const dict = tv_dict_alloc();
// Convert the string mode to the integer mode
// that is stored within each mapblock
char *p = mode.data;
int int_mode = get_map_mode(&p, 0);
// Determine the desired buffer value
long buffer_value = (buf == NULL) ? 0 : buf->handle;
for (int i = 0; i < MAX_MAPHASH; i++) {
for (const mapblock_T *current_maphash = get_maphash(i, buf);
current_maphash;
current_maphash = current_maphash->m_next) {
if (current_maphash->m_simplified) {
continue;
}
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
mapblock_fill_dict(dict, current_maphash, buffer_value, false);
Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
.vval.v_dict = dict } });
if (from_lua) {
Dictionary d = api_dict.data.dictionary;
for (size_t j = 0; j < d.size; j++) {
if (strequal("callback", d.items[j].key.data)) {
d.items[j].value.type = kObjectTypeLuaRef;
d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer);
break;
}
}
}
ADD(mappings, api_dict);
tv_dict_clear(dict);
}
}
}
tv_dict_free(dict);
return mappings;
}
/// Force obj to bool.
/// If it fails, returns false and sets err
/// @param obj The object to coerce to a boolean

View File

@ -38,6 +38,7 @@
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"

View File

@ -53,6 +53,7 @@
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"

View File

@ -18,6 +18,7 @@
#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"

View File

@ -31,6 +31,7 @@
#include "nvim/indent_c.h"
#include "nvim/keycodes.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@ -6768,34 +6769,6 @@ void free_last_insert(void)
#endif
/// Add character "c" to buffer "s"
///
/// Escapes the special meaning of K_SPECIAL, handles multi-byte
/// characters.
///
/// @param[in] c Character to add.
/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
///
/// @return Pointer to after the added bytes.
char_u *add_char2buf(int c, char_u *s)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u temp[MB_MAXBYTES + 1];
const int len = utf_char2bytes(c, (char *)temp);
for (int i = 0; i < len; i++) {
c = (uint8_t)temp[i];
// Need to escape K_SPECIAL like in the typeahead buffer.
if (c == K_SPECIAL) {
*s++ = K_SPECIAL;
*s++ = KS_SPECIAL;
*s++ = KE_FILLER;
} else {
*s++ = (char_u)c;
}
}
return s;
}
/*
* move cursor to start of line
* if flags & BL_WHITE move to first non-white

View File

@ -6791,57 +6791,6 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)
return argv;
}
/// Fill a dictionary with all applicable maparg() like dictionaries
///
/// @param dict The dictionary to be filled
/// @param mp The maphash that contains the mapping information
/// @param buffer_value The "buffer" value
/// @param compatible True for compatible with old maparg() dict
void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value,
bool compatible)
FUNC_ATTR_NONNULL_ALL
{
char *const lhs = str2special_save((const char *)mp->m_keys,
compatible, !compatible);
char *const mapmode = map_mode_to_chars(mp->m_mode);
varnumber_T noremap_value;
if (compatible) {
// Keep old compatible behavior
// This is unable to determine whether a mapping is a <script> mapping
noremap_value = !!mp->m_noremap;
} else {
// Distinguish between <script> mapping
// If it's not a <script> mapping, check if it's a noremap
noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
}
if (mp->m_luaref != LUA_NOREF) {
tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref);
} else {
if (compatible) {
tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
} else {
tv_dict_add_allocated_str(dict, S_LEN("rhs"),
str2special_save((const char *)mp->m_str, false,
true));
}
}
if (mp->m_desc != NULL) {
tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc));
}
tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
tv_dict_add_nr(dict, S_LEN("script"), mp->m_noremap == REMAP_SCRIPT ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ctx.sc_sid);
tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum);
tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
}
void return_register(int regname, typval_T *rettv)
{
char buf[2] = { (char)regname, 0 };

View File

@ -38,6 +38,7 @@
#include "nvim/input.h"
#include "nvim/lua/executor.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/match.h"
#include "nvim/math.h"
@ -5647,89 +5648,6 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = (varnumber_T)time(NULL);
}
static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
{
char *keys_buf = NULL;
char_u *alt_keys_buf = NULL;
bool did_simplify = false;
char_u *rhs;
LuaRef rhs_lua;
int mode;
int abbr = FALSE;
int get_dict = FALSE;
mapblock_T *mp;
int buffer_local;
int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
// Return empty string for failure.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
char *keys = (char *)tv_get_string(&argvars[0]);
if (*keys == NUL) {
return;
}
char buf[NUMBUFLEN];
const char *which;
if (argvars[1].v_type != VAR_UNKNOWN) {
which = tv_get_string_buf_chk(&argvars[1], buf);
if (argvars[2].v_type != VAR_UNKNOWN) {
abbr = tv_get_number(&argvars[2]);
if (argvars[3].v_type != VAR_UNKNOWN) {
get_dict = tv_get_number(&argvars[3]);
}
}
} else {
which = "";
}
if (which == NULL) {
return;
}
mode = get_map_mode((char **)&which, 0);
char_u *keys_simplified
= (char_u *)replace_termcodes(keys,
STRLEN(keys), &keys_buf, flags, &did_simplify,
CPO_TO_CPO_FLAGS);
rhs = check_map(keys_simplified, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
if (did_simplify) {
// When the lhs is being simplified the not-simplified keys are
// preferred for printing, like in do_map().
(void)replace_termcodes(keys,
STRLEN(keys),
(char **)&alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL,
CPO_TO_CPO_FLAGS);
rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
}
if (!get_dict) {
// Return a string.
if (rhs != NULL) {
if (*rhs == NUL) {
rettv->vval.v_string = xstrdup("<Nop>");
} else {
rettv->vval.v_string = str2special_save((char *)rhs, false, false);
}
} else if (rhs_lua != LUA_NOREF) {
size_t msglen = 100;
char *msg = (char *)xmalloc(msglen);
snprintf(msg, msglen, "<Lua function %d>", mp->m_luaref);
rettv->vval.v_string = msg;
}
} else {
tv_dict_alloc_ret(rettv);
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
// Return a dictionary.
mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true);
}
}
xfree(keys_buf);
xfree(alt_keys_buf);
}
/// luaeval() function implementation
static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FUNC_ATTR_NONNULL_ALL
@ -5748,18 +5666,6 @@ static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr)
filter_map(argvars, rettv, TRUE);
}
/// "maparg()" function
static void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_maparg(argvars, rettv, TRUE);
}
/// "mapcheck()" function
static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
get_maparg(argvars, rettv, FALSE);
}
static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const SomeMatchType type)
{

View File

@ -44,6 +44,7 @@
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/match.h"
#include "nvim/mbyte.h"
@ -5256,45 +5257,6 @@ theend:
return tab_number;
}
/// ":abbreviate" and friends.
static void ex_abbreviate(exarg_T *eap)
{
do_exmap(eap, TRUE); // almost the same as mapping
}
/// ":map" and friends.
static void ex_map(exarg_T *eap)
{
/*
* If we are sourcing .exrc or .vimrc in current directory we
* print the mappings for security reasons.
*/
if (secure) {
secure = 2;
msg_outtrans((char_u *)eap->cmd);
msg_putchar('\n');
}
do_exmap(eap, FALSE);
}
/// ":unmap" and friends.
static void ex_unmap(exarg_T *eap)
{
do_exmap(eap, FALSE);
}
/// ":mapclear" and friends.
static void ex_mapclear(exarg_T *eap)
{
map_clear_mode((char_u *)eap->cmd, (char_u *)eap->arg, eap->forceit, false);
}
/// ":abclear" and friends.
static void ex_abclear(exarg_T *eap)
{
map_clear_mode((char_u *)eap->cmd, (char_u *)eap->arg, true, true);
}
static void ex_autocmd(exarg_T *eap)
{
// Disallow autocommands from .exrc and .vimrc in current
@ -8381,23 +8343,6 @@ void do_sleep(long msec)
}
}
static void do_exmap(exarg_T *eap, int isabbrev)
{
int mode;
char *cmdp = eap->cmd;
mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
(char_u *)eap->arg, mode, isabbrev)) {
case 1:
emsg(_(e_invarg));
break;
case 2:
emsg(isabbrev ? _(e_noabbr) : _(e_nomap));
break;
}
}
/// ":winsize" command (obsolete).
static void ex_winsize(exarg_T *eap)
{

View File

@ -46,6 +46,7 @@
#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"

View File

@ -25,9 +25,9 @@
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/keycodes.h"
#include "nvim/mapping.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os/input.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,7 @@
#ifndef NVIM_GETCHAR_H
#define NVIM_GETCHAR_H
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/os/fileio.h"
#include "nvim/types.h"
#include "nvim/vim.h"
/// Values for "noremap" argument of ins_typebuf()
@ -24,47 +21,6 @@ typedef enum {
FLUSH_INPUT, // flush typebuf and inchar() input
} flush_buffers_T;
/// All possible |:map-arguments| usable in a |:map| command.
///
/// The <special> argument has no effect on mappings and is excluded from this
/// struct declaration. |noremap| is included, since it behaves like a map
/// argument when used in a mapping.
///
/// @see mapblock_T
struct map_arguments {
bool buffer;
bool expr;
bool noremap;
bool nowait;
bool script;
bool silent;
bool unique;
/// The {lhs} of the mapping.
///
/// vim limits this to MAXMAPLEN characters, allowing us to use a static
/// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
/// that {lhs} was too long and truncated.
char_u lhs[MAXMAPLEN + 1];
size_t lhs_len;
/// Unsimplifed {lhs} of the mapping. If no simplification has been done then alt_lhs_len is 0.
char_u alt_lhs[MAXMAPLEN + 1];
size_t alt_lhs_len;
char_u *rhs; /// The {rhs} of the mapping.
size_t rhs_len;
LuaRef rhs_lua; /// lua function as {rhs}
bool rhs_is_noop; /// True when the {rhs} should be <Nop>.
char_u *orig_rhs; /// The original text of the {rhs}.
size_t orig_rhs_len;
char *desc; /// map description
};
typedef struct map_arguments MapArguments;
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
{ 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
#define KEYLEN_PART_KEY (-1) // keylen value for incomplete key-code
#define KEYLEN_PART_MAP (-2) // keylen value for incomplete mapping

View File

@ -1005,6 +1005,78 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
return *bufp;
}
/// Add character "c" to buffer "s"
///
/// Escapes the special meaning of K_SPECIAL, handles multi-byte
/// characters.
///
/// @param[in] c Character to add.
/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes.
///
/// @return Pointer to after the added bytes.
char_u *add_char2buf(int c, char_u *s)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
char_u temp[MB_MAXBYTES + 1];
const int len = utf_char2bytes(c, (char *)temp);
for (int i = 0; i < len; i++) {
c = (uint8_t)temp[i];
// Need to escape K_SPECIAL like in the typeahead buffer.
if (c == K_SPECIAL) {
*s++ = K_SPECIAL;
*s++ = KS_SPECIAL;
*s++ = KE_FILLER;
} else {
*s++ = (char_u)c;
}
}
return s;
}
/// Copy "p" to allocated memory, escaping K_SPECIAL so that the result
/// can be put in the typeahead buffer.
char *vim_strsave_escape_ks(char *p)
{
// Need a buffer to hold up to three times as much. Four in case of an
// illegal utf-8 byte:
// 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
char_u *res = xmalloc(STRLEN(p) * 4 + 1);
char_u *d = res;
for (char_u *s = (char_u *)p; *s != NUL;) {
if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
// Copy special key unmodified.
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
} else {
// Add character, possibly multi-byte to destination, escaping
// K_SPECIAL. Be careful, it can be an illegal byte!
d = add_char2buf(utf_ptr2char((char *)s), d);
s += utf_ptr2len((char *)s);
}
}
*d = NUL;
return (char *)res;
}
/// Remove escaping from K_SPECIAL characters. Reverse of
/// vim_strsave_escape_ks(). Works in-place.
void vim_unescape_ks(char_u *p)
{
char_u *s = p, *d = p;
while (*s != NUL) {
if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) {
*d++ = K_SPECIAL;
s += 3;
} else {
*d++ = *s++;
}
}
*d = NUL;
}
/// Logs a single key as a human-readable keycode.
void log_key(int log_level, int key)
{

View File

@ -23,7 +23,6 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
@ -31,6 +30,7 @@
#include "nvim/if_cscope.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/ui_client.h"
#include "nvim/vim.h"
#ifdef HAVE_LOCALE_H

2553
src/nvim/mapping.c Normal file

File diff suppressed because it is too large Load Diff

54
src/nvim/mapping.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef NVIM_MAPPING_H
#define NVIM_MAPPING_H
#include "nvim/buffer_defs.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/types.h"
#include "nvim/vim.h"
/// All possible |:map-arguments| usable in a |:map| command.
///
/// The <special> argument has no effect on mappings and is excluded from this
/// struct declaration. |noremap| is included, since it behaves like a map
/// argument when used in a mapping.
///
/// @see mapblock_T
struct map_arguments {
bool buffer;
bool expr;
bool noremap;
bool nowait;
bool script;
bool silent;
bool unique;
/// The {lhs} of the mapping.
///
/// vim limits this to MAXMAPLEN characters, allowing us to use a static
/// buffer. Setting lhs_len to a value larger than MAXMAPLEN can signal
/// that {lhs} was too long and truncated.
char_u lhs[MAXMAPLEN + 1];
size_t lhs_len;
/// Unsimplifed {lhs} of the mapping. If no simplification has been done then alt_lhs_len is 0.
char_u alt_lhs[MAXMAPLEN + 1];
size_t alt_lhs_len;
char_u *rhs; /// The {rhs} of the mapping.
size_t rhs_len;
LuaRef rhs_lua; /// lua function as {rhs}
bool rhs_is_noop; /// True when the {rhs} should be <Nop>.
char_u *orig_rhs; /// The original text of the {rhs}.
size_t orig_rhs_len;
char *desc; /// map description
};
typedef struct map_arguments MapArguments;
#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
{ 0 }, 0, { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "mapping.h.generated.h"
#endif
#endif // NVIM_MAPPING_H

View File

@ -37,6 +37,7 @@
#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"

View File

@ -51,6 +51,7 @@
#include "nvim/indent_c.h"
#include "nvim/keycodes.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#include "nvim/memline.h"
@ -7062,175 +7063,6 @@ static int wc_use_keyname(char_u *varp, long *wcp)
return false;
}
/// Any character has an equivalent 'langmap' character. This is used for
/// keyboards that have a special language mode that sends characters above
/// 128 (although other characters can be translated too). The "to" field is a
/// Vim command character. This avoids having to switch the keyboard back to
/// ASCII mode when leaving Insert mode.
///
/// langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
/// commands.
/// langmap_mapga.ga_data is a sorted table of langmap_entry_T.
/// This does the same as langmap_mapchar[] for characters >= 256.
///
/// With multi-byte support use growarray for 'langmap' chars >= 256
typedef struct {
int from;
int to;
} langmap_entry_T;
static garray_T langmap_mapga = GA_EMPTY_INIT_VALUE;
/// Search for an entry in "langmap_mapga" for "from". If found set the "to"
/// field. If not found insert a new entry at the appropriate location.
static void langmap_set_entry(int from, int to)
{
langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
unsigned int a = 0;
assert(langmap_mapga.ga_len >= 0);
unsigned int b = (unsigned int)langmap_mapga.ga_len;
// Do a binary search for an existing entry.
while (a != b) {
unsigned int i = (a + b) / 2;
int d = entries[i].from - from;
if (d == 0) {
entries[i].to = to;
return;
}
if (d < 0) {
a = i + 1;
} else {
b = i;
}
}
ga_grow(&langmap_mapga, 1);
// insert new entry at position "a"
entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
memmove(entries + 1, entries,
((unsigned int)langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
langmap_mapga.ga_len++;
entries[0].from = from;
entries[0].to = to;
}
/// Apply 'langmap' to multi-byte character "c" and return the result.
int langmap_adjust_mb(int c)
{
langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
int a = 0;
int b = langmap_mapga.ga_len;
while (a != b) {
int i = (a + b) / 2;
int d = entries[i].from - c;
if (d == 0) {
return entries[i].to; // found matching entry
}
if (d < 0) {
a = i + 1;
} else {
b = i;
}
}
return c; // no entry found, return "c" unmodified
}
static void langmap_init(void)
{
for (int i = 0; i < 256; i++) {
langmap_mapchar[i] = (char_u)i; // we init with a one-to-one map
}
ga_init(&langmap_mapga, sizeof(langmap_entry_T), 8);
}
/// Called when langmap option is set; the language map can be
/// changed at any time!
static void langmap_set(void)
{
char_u *p;
char_u *p2;
int from, to;
ga_clear(&langmap_mapga); // clear the previous map first
langmap_init(); // back to one-to-one map
for (p = p_langmap; p[0] != NUL;) {
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
MB_PTR_ADV(p2)) {
if (p2[0] == '\\' && p2[1] != NUL) {
p2++;
}
}
if (p2[0] == ';') {
p2++; // abcd;ABCD form, p2 points to A
} else {
p2 = NULL; // aAbBcCdD form, p2 is NULL
}
while (p[0]) {
if (p[0] == ',') {
p++;
break;
}
if (p[0] == '\\' && p[1] != NUL) {
p++;
}
from = utf_ptr2char((char *)p);
to = NUL;
if (p2 == NULL) {
MB_PTR_ADV(p);
if (p[0] != ',') {
if (p[0] == '\\') {
p++;
}
to = utf_ptr2char((char *)p);
}
} else {
if (p2[0] != ',') {
if (p2[0] == '\\') {
p2++;
}
to = utf_ptr2char((char *)p2);
}
}
if (to == NUL) {
semsg(_("E357: 'langmap': Matching character missing for %s"),
transchar(from));
return;
}
if (from >= 256) {
langmap_set_entry(from, to);
} else {
assert(to <= UCHAR_MAX);
langmap_mapchar[from & 255] = (char_u)to;
}
// Advance to next pair
MB_PTR_ADV(p);
if (p2 != NULL) {
MB_PTR_ADV(p2);
if (*p == ';') {
p = p2;
if (p[0] != NUL) {
if (p[0] != ',') {
semsg(_("E358: 'langmap': Extra characters after semicolon: %s"),
p);
return;
}
p++;
}
break;
}
}
}
}
}
/// Return true if format option 'x' is in effect.
/// Take care of no formatting when 'paste' is set.
bool has_format_option(int x)

View File

@ -26,6 +26,7 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/match.h"
#include "nvim/memline.h"