mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 8.2.4099: Vim9: cannot use Vim9 syntax in mapping
Problem: Vim9: cannot use Vim9 syntax in mapping. Solution: Add <ScriptCmd> to use the script context for a command.
This commit is contained in:
@@ -284,6 +284,10 @@ This can be solved by inserting <Ignore> before the character that is
|
||||
expression-mapped: >
|
||||
nmap ! f!<Ignore>x
|
||||
|
||||
When defining a mapping in a |Vim9| script, the expression will be evaluated
|
||||
in the context of that script. This means that script-local items can be
|
||||
accessed in the expression.
|
||||
|
||||
Be very careful about side effects! The expression is evaluated while
|
||||
obtaining characters, you may very well make the command dysfunctional.
|
||||
For this reason the following is blocked:
|
||||
@@ -342,9 +346,24 @@ Example of using <Cmd> halfway Insert mode: >
|
||||
Unlike <expr> mappings, there are no special restrictions on the <Cmd>
|
||||
command: it is executed as if an (unrestricted) |autocommand| was invoked.
|
||||
|
||||
*<ScriptCmd>*
|
||||
<ScriptCmd> is like <Cmd> but sets the context to the script the mapping was
|
||||
defined in, for the duration of the command execution. This is especially
|
||||
useful for |Vim9| script. It also works to access an import, which is useful
|
||||
in a plugin using an autoload script: >
|
||||
vim9script
|
||||
import autoload 'implementation.vim' as impl
|
||||
nnoremap <silent> <F4> <ScriptCmd>impl.DoTheWork()<CR>
|
||||
|
||||
No matter where <F4> is typed, the "impl" import will be found in the script
|
||||
context of where the mapping was defined. And since it's an autoload import,
|
||||
the "implementation.vim" script will only be loaded once <F4> is typed, not
|
||||
when the mapping is defined.
|
||||
|
||||
Note:
|
||||
- Because <Cmd> avoids mode-changes it does not trigger |CmdlineEnter| and
|
||||
|CmdlineLeave| events, because no user interaction is expected.
|
||||
- Because <Cmd> and <ScriptCmd> avoid mode-changes it does not trigger
|
||||
|CmdlineEnter| and |CmdlineLeave| events, because no user interaction is
|
||||
expected.
|
||||
- For the same reason, |keycodes| like <C-R><C-W> are interpreted as plain,
|
||||
unmapped keys.
|
||||
- The command is not echo'ed, no need for <silent>.
|
||||
@@ -356,12 +375,13 @@ Note:
|
||||
Visual mode. Use |:smap| to handle Select mode differently.
|
||||
|
||||
*E1255* *E1136*
|
||||
<Cmd> commands must terminate, that is, they must be followed by <CR> in the
|
||||
{rhs} of the mapping definition. |Command-line| mode is never entered.
|
||||
<Cmd> and <ScriptCmd> commands must terminate, that is, they must be followed
|
||||
by <CR> in the {rhs} of the mapping definition. |Command-line| mode is never
|
||||
entered.
|
||||
|
||||
*E1137*
|
||||
<Cmd> commands can have only normal characters and cannot contain special
|
||||
characters like function keys.
|
||||
<Cmd> and <ScriptCmd> commands can have only normal characters and cannot
|
||||
contain special characters like function keys.
|
||||
|
||||
|
||||
1.3 MAPPING AND MODES *:map-modes*
|
||||
|
@@ -1056,7 +1056,8 @@ doESCkey:
|
||||
break;
|
||||
|
||||
case K_COMMAND: // <Cmd>command<CR>
|
||||
do_cmdline(NULL, getcmdkeycmd, NULL, 0);
|
||||
case K_SCRIPT_COMMAND: // <ScriptCmd>command<CR>
|
||||
do_cmdkey_command(c, 0);
|
||||
#ifdef FEAT_TERMINAL
|
||||
if (term_use_loop())
|
||||
// Started a terminal that gets the input, exit Insert mode.
|
||||
|
@@ -1772,11 +1772,11 @@ getcmdline_int(
|
||||
c = safe_vgetc();
|
||||
} while (c == K_IGNORE || c == K_NOP);
|
||||
|
||||
if (c == K_COMMAND)
|
||||
if (c == K_COMMAND || c == K_SCRIPT_COMMAND)
|
||||
{
|
||||
int clen = ccline.cmdlen;
|
||||
|
||||
if (do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT) == OK)
|
||||
if (do_cmdkey_command(c, DOCMD_NOWAIT) == OK)
|
||||
{
|
||||
if (clen == ccline.cmdlen)
|
||||
trigger_cmdlinechanged = FALSE;
|
||||
|
@@ -83,6 +83,10 @@ static char_u noremapbuf_init[TYPELEN_INIT]; // initial typebuf.tb_noremap
|
||||
|
||||
static int last_recorded_len = 0; // number of last recorded chars
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
mapblock_T *last_used_map = NULL;
|
||||
#endif
|
||||
|
||||
static int read_readbuf(buffheader_T *buf, int advance);
|
||||
static void init_typebuf(void);
|
||||
static void may_sync_undo(void);
|
||||
@@ -2893,6 +2897,7 @@ handle_mapping(
|
||||
#ifdef FEAT_EVAL
|
||||
if (save_m_expr)
|
||||
vim_free(map_str);
|
||||
last_used_map = mp;
|
||||
#endif
|
||||
}
|
||||
#ifdef FEAT_EVAL
|
||||
@@ -3708,7 +3713,7 @@ input_available(void)
|
||||
* Function passed to do_cmdline() to get the command after a <Cmd> key from
|
||||
* typeahead.
|
||||
*/
|
||||
char_u *
|
||||
static char_u *
|
||||
getcmdkeycmd(
|
||||
int promptc UNUSED,
|
||||
void *cookie UNUSED,
|
||||
@@ -3774,7 +3779,7 @@ getcmdkeycmd(
|
||||
c1 = NUL; // end the line
|
||||
else if (c1 == ESC)
|
||||
aborted = TRUE;
|
||||
else if (c1 == K_COMMAND)
|
||||
else if (c1 == K_COMMAND || c1 == K_SCRIPT_COMMAND)
|
||||
{
|
||||
// give a nicer error message for this special case
|
||||
emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd));
|
||||
@@ -3804,3 +3809,35 @@ getcmdkeycmd(
|
||||
|
||||
return (char_u *)line_ga.ga_data;
|
||||
}
|
||||
|
||||
int
|
||||
do_cmdkey_command(int key, int flags)
|
||||
{
|
||||
int res;
|
||||
#ifdef FEAT_EVAL
|
||||
sctx_T save_current_sctx = {0, 0, 0, 0};
|
||||
|
||||
if (key == K_SCRIPT_COMMAND && last_used_map != NULL)
|
||||
{
|
||||
save_current_sctx = current_sctx;
|
||||
current_sctx = last_used_map->m_script_ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
res = do_cmdline(NULL, getcmdkeycmd, NULL, flags);
|
||||
|
||||
#ifdef FEAT_EVAL
|
||||
if (save_current_sctx.sc_sid > 0)
|
||||
current_sctx = save_current_sctx;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
void
|
||||
reset_last_used_map(void)
|
||||
{
|
||||
last_used_map = NULL;
|
||||
}
|
||||
#endif
|
||||
|
@@ -2281,7 +2281,8 @@ ins_compl_prep(int c)
|
||||
|
||||
// Ignore end of Select mode mapping and mouse scroll buttons.
|
||||
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|
||||
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_COMMAND)
|
||||
|| c == K_MOUSELEFT || c == K_MOUSERIGHT
|
||||
|| c == K_COMMAND || c == K_SCRIPT_COMMAND)
|
||||
return retval;
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
|
@@ -276,6 +276,7 @@ enum key_extra
|
||||
, KE_MOUSEMOVE_XY = 101 // KE_MOUSEMOVE with coordinates
|
||||
, KE_CANCEL = 102 // return from vgetc()
|
||||
, KE_COMMAND = 103 // <Cmd> special key
|
||||
, KE_SCRIPT_COMMAND = 104 // <ScriptCmd> special key
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -480,6 +481,7 @@ enum key_extra
|
||||
#define K_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD)
|
||||
|
||||
#define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND)
|
||||
#define K_SCRIPT_COMMAND TERMCAP2KEY(KS_EXTRA, KE_SCRIPT_COMMAND)
|
||||
|
||||
// Bits for modifier mask
|
||||
// 0x01 cannot be used, because the modifier must be 0x02 or higher
|
||||
|
@@ -1057,6 +1057,7 @@ static struct key_name_entry
|
||||
{K_CURSORHOLD, (char_u *)"CursorHold"},
|
||||
{K_IGNORE, (char_u *)"Ignore"},
|
||||
{K_COMMAND, (char_u *)"Cmd"},
|
||||
{K_SCRIPT_COMMAND, (char_u *)"ScriptCmd"},
|
||||
{K_FOCUSGAINED, (char_u *)"FocusGained"},
|
||||
{K_FOCUSLOST, (char_u *)"FocusLost"},
|
||||
{0, NULL}
|
||||
|
12
src/normal.c
12
src/normal.c
@@ -373,6 +373,7 @@ static const struct nv_cmd
|
||||
{K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0},
|
||||
{K_PS, nv_edit, 0, 0},
|
||||
{K_COMMAND, nv_colon, 0, 0},
|
||||
{K_SCRIPT_COMMAND, nv_colon, 0, 0},
|
||||
};
|
||||
|
||||
// Number of commands in nv_cmds[].
|
||||
@@ -3429,7 +3430,9 @@ nv_colon(cmdarg_T *cap)
|
||||
{
|
||||
int old_p_im;
|
||||
int cmd_result;
|
||||
int is_cmdkey = cap->cmdchar == K_COMMAND;
|
||||
int is_cmdkey = cap->cmdchar == K_COMMAND
|
||||
|| cap->cmdchar == K_SCRIPT_COMMAND;
|
||||
int flags;
|
||||
|
||||
if (VIsual_active && !is_cmdkey)
|
||||
nv_operator(cap);
|
||||
@@ -3459,8 +3462,11 @@ nv_colon(cmdarg_T *cap)
|
||||
old_p_im = p_im;
|
||||
|
||||
// get a command line and execute it
|
||||
cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
|
||||
cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
|
||||
flags = cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0;
|
||||
if (is_cmdkey)
|
||||
cmd_result = do_cmdkey_command(cap->cmdchar, flags);
|
||||
else
|
||||
cmd_result = do_cmdline(NULL, getexline, NULL, flags);
|
||||
|
||||
// If 'insertmode' changed, enter or exit Insert mode
|
||||
if (p_im != old_p_im)
|
||||
|
15
src/ops.c
15
src/ops.c
@@ -3501,6 +3501,14 @@ typedef struct {
|
||||
int rv_arg; // extra argument
|
||||
} redo_VIsual_T;
|
||||
|
||||
static int
|
||||
is_ex_cmdchar(cmdarg_T *cap)
|
||||
{
|
||||
return cap->cmdchar == ':'
|
||||
|| cap->cmdchar == K_COMMAND
|
||||
|| cap->cmdchar == K_SCRIPT_COMMAND;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an operator after Visual mode or when the movement is finished.
|
||||
* "gui_yank" is true when yanking text for the clipboard.
|
||||
@@ -3583,8 +3591,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
|
||||
&& ((!VIsual_active || oap->motion_force)
|
||||
// Also redo Operator-pending Visual mode mappings
|
||||
|| (VIsual_active
|
||||
&& (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND)
|
||||
&& oap->op_type != OP_COLON))
|
||||
&& is_ex_cmdchar(cap) && oap->op_type != OP_COLON))
|
||||
&& cap->cmdchar != 'D'
|
||||
#ifdef FEAT_FOLDING
|
||||
&& oap->op_type != OP_FOLD
|
||||
@@ -3608,7 +3615,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
|
||||
AppendToRedobuffLit(cap->searchbuf, -1);
|
||||
AppendToRedobuff(NL_STR);
|
||||
}
|
||||
else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND)
|
||||
else if (is_ex_cmdchar(cap))
|
||||
{
|
||||
// do_cmdline() has stored the first typed line in
|
||||
// "repeat_cmdline". When several lines are typed repeating
|
||||
@@ -3806,7 +3813,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank)
|
||||
get_op_char(oap->op_type),
|
||||
get_extra_op_char(oap->op_type),
|
||||
oap->motion_force, cap->cmdchar, cap->nchar);
|
||||
else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND)
|
||||
else if (!is_ex_cmdchar(cap))
|
||||
{
|
||||
int opchar = get_op_char(oap->op_type);
|
||||
int extra_opchar = get_extra_op_char(oap->op_type);
|
||||
|
@@ -52,5 +52,6 @@ void parse_queued_messages(void);
|
||||
void vungetc(int c);
|
||||
int fix_input_buffer(char_u *buf, int len);
|
||||
int input_available(void);
|
||||
char_u *getcmdkeycmd(int promptc, void *cookie, int indent, getline_opt_T do_concat);
|
||||
int do_cmdkey_command(int key, int flags);
|
||||
void reset_last_used_map(void);
|
||||
/* vim: set ft=c : */
|
||||
|
@@ -2229,7 +2229,8 @@ send_keys_to_term(term_T *term, int c, int modmask, int typed)
|
||||
break;
|
||||
|
||||
case K_COMMAND:
|
||||
return do_cmdline(NULL, getcmdkeycmd, NULL, 0);
|
||||
case K_SCRIPT_COMMAND:
|
||||
return do_cmdkey_command(c, 0);
|
||||
}
|
||||
if (typed)
|
||||
mouse_was_outside = FALSE;
|
||||
|
@@ -1337,6 +1337,9 @@ def Test_autoload_mapping()
|
||||
export def Toggle(): string
|
||||
return ":g:toggle_called = 'yes'\<CR>"
|
||||
enddef
|
||||
export def Doit()
|
||||
g:doit_called = 'yes'
|
||||
enddef
|
||||
END
|
||||
writefile(lines, 'Xdir/autoload/toggle.vim')
|
||||
|
||||
@@ -1346,6 +1349,8 @@ def Test_autoload_mapping()
|
||||
import autoload 'toggle.vim'
|
||||
|
||||
nnoremap <silent> <expr> tt toggle.Toggle()
|
||||
nnoremap <silent> xx <ScriptCmd>toggle.Doit()<CR>
|
||||
nnoremap <silent> yy <Cmd>toggle.Doit()<CR>
|
||||
END
|
||||
CheckScriptSuccess(lines)
|
||||
assert_false(exists("g:toggle_loaded"))
|
||||
@@ -1355,7 +1360,14 @@ def Test_autoload_mapping()
|
||||
assert_equal('yes', g:toggle_loaded)
|
||||
assert_equal('yes', g:toggle_called)
|
||||
|
||||
feedkeys("xx", 'xt')
|
||||
assert_equal('yes', g:doit_called)
|
||||
|
||||
assert_fails('call feedkeys("yy", "xt")', 'E121: Undefined variable: toggle')
|
||||
|
||||
nunmap tt
|
||||
nunmap xx
|
||||
nunmap yy
|
||||
unlet g:toggle_loaded
|
||||
unlet g:toggle_called
|
||||
delete('Xdir', 'rf')
|
||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
4099,
|
||||
/**/
|
||||
4098,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user