Mercurial > vim
changeset 27140:a9eeb18e749c v8.2.4099
patch 8.2.4099: Vim9: cannot use Vim9 syntax in mapping
Commit: https://github.com/vim/vim/commit/e32c3c462ce9b3163a4a4bffd985897910885d29
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Jan 15 18:26:04 2022 +0000
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.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 15 Jan 2022 19:30:04 +0100 |
parents | fd4193f6d59e |
children | 5d0ddfc61e67 |
files | runtime/doc/map.txt src/edit.c src/ex_getln.c src/getchar.c src/insexpand.c src/keymap.h src/misc2.c src/normal.c src/ops.c src/proto/getchar.pro src/terminal.c src/testdir/test_vim9_import.vim src/version.c |
diffstat | 13 files changed, 113 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -284,6 +284,10 @@ This can be solved by inserting <Ignore> 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 mo 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*
--- a/src/edit.c +++ b/src/edit.c @@ -1055,8 +1055,9 @@ doESCkey: case K_IGNORE: // Something mapped to nothing break; - case K_COMMAND: // <Cmd>command<CR> - do_cmdline(NULL, getcmdkeycmd, NULL, 0); + case K_COMMAND: // <Cmd>command<CR> + 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.
--- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -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;
--- a/src/getchar.c +++ b/src/getchar.c @@ -83,6 +83,10 @@ static char_u noremapbuf_init[TYPELEN_IN 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
--- a/src/insexpand.c +++ b/src/insexpand.c @@ -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
--- a/src/keymap.h +++ b/src/keymap.h @@ -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
--- a/src/misc2.c +++ b/src/misc2.c @@ -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}
--- a/src/normal.c +++ b/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)
--- a/src/ops.c +++ b/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 o && ((!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 o 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 o 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);
--- a/src/proto/getchar.pro +++ b/src/proto/getchar.pro @@ -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 : */
--- a/src/terminal.c +++ b/src/terminal.c @@ -2229,7 +2229,8 @@ send_keys_to_term(term_T *term, int c, i 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;
--- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -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')