To: vim_dev@googlegroups.com Subject: Patch 8.2.4099 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4099 Problem: Vim9: cannot use Vim9 syntax in mapping. Solution: Add to use the script context for a command. Files: runtime/doc/map.txt, src/normal.c, src/getchar.c, src/proto/getchar.pro, src/ex_getln.c, src/edit.c, src/terminal.c, src/keymap.h, src/insexpand.c, src/misc2.c, src/ops.c, src/testdir/test_vim9_import.vim *** ../vim-8.2.4098/runtime/doc/map.txt 2021-11-18 22:08:52.007682711 +0000 --- runtime/doc/map.txt 2022-01-15 18:13:21.243740342 +0000 *************** *** 335,358 **** Unlike mappings, there are no special restrictions on the command: it is executed as if an (unrestricted) |autocommand| was invoked. Note: ! - Because avoids mode-changes it does not trigger |CmdlineEnter| and ! |CmdlineLeave| events, because no user interaction is expected. - For the same reason, |keycodes| like are interpreted as plain, unmapped keys. - The command is not echo'ed, no need for . - In Visual mode you can use `line('v')` and `col('v')` to get one end of the Visual area, the cursor is at the other end. - In Select mode, |:map| and |:vmap| command mappings are executed in Visual mode. Use |:smap| to handle Select mode differently. ! *E1135* *E1136* ! commands must terminate, that is, they must be followed by in the ! {rhs} of the mapping definition. |Command-line| mode is never entered. *E1137* ! commands can have only normal characters and cannot contain special ! characters like function keys. 1.3 MAPPING AND MODES *:map-modes* --- 346,387 ---- Unlike mappings, there are no special restrictions on the command: it is executed as if an (unrestricted) |autocommand| was invoked. + ** + is like 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 impl.DoTheWork() + + No matter where 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 is typed, not + when the mapping is defined. + Note: ! - Because and avoid mode-changes it does not trigger ! |CmdlineEnter| and |CmdlineLeave| events, because no user interaction is ! expected. - For the same reason, |keycodes| like are interpreted as plain, unmapped keys. - The command is not echo'ed, no need for . + - The {rhs} is not subject to abbreviations nor to other mappings, even if the + mapping is recursive. - In Visual mode you can use `line('v')` and `col('v')` to get one end of the Visual area, the cursor is at the other end. - In Select mode, |:map| and |:vmap| command mappings are executed in Visual mode. Use |:smap| to handle Select mode differently. ! *E1255* *E1136* ! and commands must terminate, that is, they must be followed ! by in the {rhs} of the mapping definition. |Command-line| mode is never ! entered. *E1137* ! and commands can have only normal characters and cannot ! contain special characters like function keys. 1.3 MAPPING AND MODES *:map-modes* *** ../vim-8.2.4098/src/normal.c 2022-01-04 21:30:43.541800349 +0000 --- src/normal.c 2022-01-15 17:49:10.142598871 +0000 *************** *** 373,378 **** --- 373,379 ---- {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,3435 **** { int old_p_im; int cmd_result; ! int is_cmdkey = cap->cmdchar == K_COMMAND; if (VIsual_active && !is_cmdkey) nv_operator(cap); --- 3430,3438 ---- { int old_p_im; int cmd_result; ! int is_cmdkey = cap->cmdchar == K_COMMAND ! || cap->cmdchar == K_SCRIPT_COMMAND; ! int flags; if (VIsual_active && !is_cmdkey) nv_operator(cap); *************** *** 3459,3466 **** 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); // If 'insertmode' changed, enter or exit Insert mode if (p_im != old_p_im) --- 3462,3472 ---- old_p_im = p_im; // get a command line and execute it ! 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) *** ../vim-8.2.4098/src/getchar.c 2022-01-11 11:58:14.920745981 +0000 --- src/getchar.c 2022-01-15 18:01:26.929136912 +0000 *************** *** 83,88 **** --- 83,92 ---- 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,2898 **** --- 2897,2903 ---- #ifdef FEAT_EVAL if (save_m_expr) vim_free(map_str); + last_used_map = mp; #endif } #ifdef FEAT_EVAL *************** *** 3708,3714 **** * Function passed to do_cmdline() to get the command after a key from * typeahead. */ ! char_u * getcmdkeycmd( int promptc UNUSED, void *cookie UNUSED, --- 3713,3719 ---- * Function passed to do_cmdline() to get the command after a key from * typeahead. */ ! static char_u * getcmdkeycmd( int promptc UNUSED, void *cookie UNUSED, *************** *** 3774,3780 **** c1 = NUL; // end the line else if (c1 == ESC) aborted = TRUE; ! else if (c1 == K_COMMAND) { // give a nicer error message for this special case emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd)); --- 3779,3785 ---- c1 = NUL; // end the line else if (c1 == ESC) aborted = TRUE; ! 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,3806 **** --- 3809,3843 ---- 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 *** ../vim-8.2.4098/src/proto/getchar.pro 2022-01-03 13:47:45.956911773 +0000 --- src/proto/getchar.pro 2022-01-15 17:38:39.929473812 +0000 *************** *** 52,56 **** 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); /* vim: set ft=c : */ --- 52,57 ---- void vungetc(int c); int fix_input_buffer(char_u *buf, int len); int input_available(void); ! int do_cmdkey_command(int key, int flags); ! void reset_last_used_map(void); /* vim: set ft=c : */ *** ../vim-8.2.4098/src/ex_getln.c 2022-01-08 18:43:36.877446896 +0000 --- src/ex_getln.c 2022-01-15 17:03:45.661878223 +0000 *************** *** 1772,1782 **** c = safe_vgetc(); } while (c == K_IGNORE || c == K_NOP); ! if (c == K_COMMAND) { int clen = ccline.cmdlen; ! if (do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT) == OK) { if (clen == ccline.cmdlen) trigger_cmdlinechanged = FALSE; --- 1772,1782 ---- c = safe_vgetc(); } while (c == K_IGNORE || c == K_NOP); ! if (c == K_COMMAND || c == K_SCRIPT_COMMAND) { int clen = ccline.cmdlen; ! if (do_cmdkey_command(c, DOCMD_NOWAIT) == OK) { if (clen == ccline.cmdlen) trigger_cmdlinechanged = FALSE; *** ../vim-8.2.4098/src/edit.c 2022-01-08 12:41:12.200795557 +0000 --- src/edit.c 2022-01-15 17:04:11.230100606 +0000 *************** *** 1055,1062 **** case K_IGNORE: // Something mapped to nothing break; ! case K_COMMAND: // command ! do_cmdline(NULL, getcmdkeycmd, NULL, 0); #ifdef FEAT_TERMINAL if (term_use_loop()) // Started a terminal that gets the input, exit Insert mode. --- 1055,1063 ---- case K_IGNORE: // Something mapped to nothing break; ! case K_COMMAND: // command ! case K_SCRIPT_COMMAND: // command ! do_cmdkey_command(c, 0); #ifdef FEAT_TERMINAL if (term_use_loop()) // Started a terminal that gets the input, exit Insert mode. *** ../vim-8.2.4098/src/terminal.c 2022-01-08 16:19:18.509639849 +0000 --- src/terminal.c 2022-01-15 17:05:16.346618503 +0000 *************** *** 2229,2235 **** break; case K_COMMAND: ! return do_cmdline(NULL, getcmdkeycmd, NULL, 0); } if (typed) mouse_was_outside = FALSE; --- 2229,2236 ---- break; case K_COMMAND: ! case K_SCRIPT_COMMAND: ! return do_cmdkey_command(c, 0); } if (typed) mouse_was_outside = FALSE; *** ../vim-8.2.4098/src/keymap.h 2021-06-08 19:13:27.359916592 +0100 --- src/keymap.h 2022-01-15 17:06:35.879166599 +0000 *************** *** 276,281 **** --- 276,282 ---- , KE_MOUSEMOVE_XY = 101 // KE_MOUSEMOVE with coordinates , KE_CANCEL = 102 // return from vgetc() , KE_COMMAND = 103 // special key + , KE_SCRIPT_COMMAND = 104 // special key }; /* *************** *** 480,485 **** --- 481,487 ---- #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 *** ../vim-8.2.4098/src/insexpand.c 2022-01-08 12:41:12.204795554 +0000 --- src/insexpand.c 2022-01-15 17:07:25.155465065 +0000 *************** *** 2281,2287 **** // 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) return retval; #ifdef FEAT_PROP_POPUP --- 2281,2288 ---- // 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_SCRIPT_COMMAND) return retval; #ifdef FEAT_PROP_POPUP *** ../vim-8.2.4098/src/misc2.c 2022-01-08 12:41:12.208795550 +0000 --- src/misc2.c 2022-01-15 17:07:46.883587568 +0000 *************** *** 1057,1062 **** --- 1057,1063 ---- {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} *** ../vim-8.2.4098/src/ops.c 2022-01-08 12:41:12.208795550 +0000 --- src/ops.c 2022-01-15 17:12:50.308841347 +0000 *************** *** 3501,3506 **** --- 3501,3514 ---- 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,3590 **** && ((!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)) && cap->cmdchar != 'D' #ifdef FEAT_FOLDING && oap->op_type != OP_FOLD --- 3591,3597 ---- && ((!VIsual_active || oap->motion_force) // Also redo Operator-pending Visual mode mappings || (VIsual_active ! && is_ex_cmdchar(cap) && oap->op_type != OP_COLON)) && cap->cmdchar != 'D' #ifdef FEAT_FOLDING && oap->op_type != OP_FOLD *************** *** 3608,3614 **** AppendToRedobuffLit(cap->searchbuf, -1); AppendToRedobuff(NL_STR); } ! else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { // do_cmdline() has stored the first typed line in // "repeat_cmdline". When several lines are typed repeating --- 3615,3621 ---- AppendToRedobuffLit(cap->searchbuf, -1); AppendToRedobuff(NL_STR); } ! else if (is_ex_cmdchar(cap)) { // do_cmdline() has stored the first typed line in // "repeat_cmdline". When several lines are typed repeating *************** *** 3806,3812 **** 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) { int opchar = get_op_char(oap->op_type); int extra_opchar = get_extra_op_char(oap->op_type); --- 3813,3819 ---- get_op_char(oap->op_type), get_extra_op_char(oap->op_type), oap->motion_force, cap->cmdchar, cap->nchar); ! else if (!is_ex_cmdchar(cap)) { int opchar = get_op_char(oap->op_type); int extra_opchar = get_extra_op_char(oap->op_type); *** ../vim-8.2.4098/src/testdir/test_vim9_import.vim 2022-01-13 22:05:05.567104513 +0000 --- src/testdir/test_vim9_import.vim 2022-01-15 18:05:59.736764583 +0000 *************** *** 1337,1342 **** --- 1337,1345 ---- export def Toggle(): string return ":g:toggle_called = 'yes'\" enddef + export def Doit() + g:doit_called = 'yes' + enddef END writefile(lines, 'Xdir/autoload/toggle.vim') *************** *** 1346,1351 **** --- 1349,1356 ---- import autoload 'toggle.vim' nnoremap tt toggle.Toggle() + nnoremap xx toggle.Doit() + nnoremap yy toggle.Doit() END CheckScriptSuccess(lines) assert_false(exists("g:toggle_loaded")) *************** *** 1355,1361 **** --- 1360,1373 ---- 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') *** ../vim-8.2.4098/src/version.c 2022-01-15 15:23:40.911170017 +0000 --- src/version.c 2022-01-15 18:06:27.872728597 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4099, /**/ -- Veni, Vidi, Video -- I came, I saw, I taped what I saw. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///