Mirror of the Vim source from https://github.com/vim/vim
Révision | a9eeb18e749c45731e0973ba2f38f7bc0b2b6524 (tree) |
---|---|
l'heure | 2022-01-16 03:30:04 |
Auteur | Bram Moolenaar <Bram@vim....> |
Commiter | Bram Moolenaar |
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
@@ -284,6 +284,10 @@ | ||
284 | 284 | expression-mapped: > |
285 | 285 | nmap ! f!<Ignore>x |
286 | 286 | |
287 | +When defining a mapping in a |Vim9| script, the expression will be evaluated | |
288 | +in the context of that script. This means that script-local items can be | |
289 | +accessed in the expression. | |
290 | + | |
287 | 291 | Be very careful about side effects! The expression is evaluated while |
288 | 292 | obtaining characters, you may very well make the command dysfunctional. |
289 | 293 | For this reason the following is blocked: |
@@ -342,9 +346,24 @@ | ||
342 | 346 | Unlike <expr> mappings, there are no special restrictions on the <Cmd> |
343 | 347 | command: it is executed as if an (unrestricted) |autocommand| was invoked. |
344 | 348 | |
349 | + *<ScriptCmd>* | |
350 | +<ScriptCmd> is like <Cmd> but sets the context to the script the mapping was | |
351 | +defined in, for the duration of the command execution. This is especially | |
352 | +useful for |Vim9| script. It also works to access an import, which is useful | |
353 | +in a plugin using an autoload script: > | |
354 | + vim9script | |
355 | + import autoload 'implementation.vim' as impl | |
356 | + nnoremap <silent> <F4> <ScriptCmd>impl.DoTheWork()<CR> | |
357 | + | |
358 | +No matter where <F4> is typed, the "impl" import will be found in the script | |
359 | +context of where the mapping was defined. And since it's an autoload import, | |
360 | +the "implementation.vim" script will only be loaded once <F4> is typed, not | |
361 | +when the mapping is defined. | |
362 | + | |
345 | 363 | Note: |
346 | -- Because <Cmd> avoids mode-changes it does not trigger |CmdlineEnter| and | |
347 | - |CmdlineLeave| events, because no user interaction is expected. | |
364 | +- Because <Cmd> and <ScriptCmd> avoid mode-changes it does not trigger | |
365 | + |CmdlineEnter| and |CmdlineLeave| events, because no user interaction is | |
366 | + expected. | |
348 | 367 | - For the same reason, |keycodes| like <C-R><C-W> are interpreted as plain, |
349 | 368 | unmapped keys. |
350 | 369 | - The command is not echo'ed, no need for <silent>. |
@@ -356,12 +375,13 @@ | ||
356 | 375 | Visual mode. Use |:smap| to handle Select mode differently. |
357 | 376 | |
358 | 377 | *E1255* *E1136* |
359 | -<Cmd> commands must terminate, that is, they must be followed by <CR> in the | |
360 | -{rhs} of the mapping definition. |Command-line| mode is never entered. | |
378 | +<Cmd> and <ScriptCmd> commands must terminate, that is, they must be followed | |
379 | +by <CR> in the {rhs} of the mapping definition. |Command-line| mode is never | |
380 | +entered. | |
361 | 381 | |
362 | 382 | *E1137* |
363 | -<Cmd> commands can have only normal characters and cannot contain special | |
364 | -characters like function keys. | |
383 | +<Cmd> and <ScriptCmd> commands can have only normal characters and cannot | |
384 | +contain special characters like function keys. | |
365 | 385 | |
366 | 386 | |
367 | 387 | 1.3 MAPPING AND MODES *:map-modes* |
@@ -1055,8 +1055,9 @@ | ||
1055 | 1055 | case K_IGNORE: // Something mapped to nothing |
1056 | 1056 | break; |
1057 | 1057 | |
1058 | - case K_COMMAND: // <Cmd>command<CR> | |
1059 | - do_cmdline(NULL, getcmdkeycmd, NULL, 0); | |
1058 | + case K_COMMAND: // <Cmd>command<CR> | |
1059 | + case K_SCRIPT_COMMAND: // <ScriptCmd>command<CR> | |
1060 | + do_cmdkey_command(c, 0); | |
1060 | 1061 | #ifdef FEAT_TERMINAL |
1061 | 1062 | if (term_use_loop()) |
1062 | 1063 | // Started a terminal that gets the input, exit Insert mode. |
@@ -1772,11 +1772,11 @@ | ||
1772 | 1772 | c = safe_vgetc(); |
1773 | 1773 | } while (c == K_IGNORE || c == K_NOP); |
1774 | 1774 | |
1775 | - if (c == K_COMMAND) | |
1775 | + if (c == K_COMMAND || c == K_SCRIPT_COMMAND) | |
1776 | 1776 | { |
1777 | 1777 | int clen = ccline.cmdlen; |
1778 | 1778 | |
1779 | - if (do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT) == OK) | |
1779 | + if (do_cmdkey_command(c, DOCMD_NOWAIT) == OK) | |
1780 | 1780 | { |
1781 | 1781 | if (clen == ccline.cmdlen) |
1782 | 1782 | trigger_cmdlinechanged = FALSE; |
@@ -83,6 +83,10 @@ | ||
83 | 83 | |
84 | 84 | static int last_recorded_len = 0; // number of last recorded chars |
85 | 85 | |
86 | +#ifdef FEAT_EVAL | |
87 | +mapblock_T *last_used_map = NULL; | |
88 | +#endif | |
89 | + | |
86 | 90 | static int read_readbuf(buffheader_T *buf, int advance); |
87 | 91 | static void init_typebuf(void); |
88 | 92 | static void may_sync_undo(void); |
@@ -2893,6 +2897,7 @@ | ||
2893 | 2897 | #ifdef FEAT_EVAL |
2894 | 2898 | if (save_m_expr) |
2895 | 2899 | vim_free(map_str); |
2900 | + last_used_map = mp; | |
2896 | 2901 | #endif |
2897 | 2902 | } |
2898 | 2903 | #ifdef FEAT_EVAL |
@@ -3708,7 +3713,7 @@ | ||
3708 | 3713 | * Function passed to do_cmdline() to get the command after a <Cmd> key from |
3709 | 3714 | * typeahead. |
3710 | 3715 | */ |
3711 | - char_u * | |
3716 | + static char_u * | |
3712 | 3717 | getcmdkeycmd( |
3713 | 3718 | int promptc UNUSED, |
3714 | 3719 | void *cookie UNUSED, |
@@ -3774,7 +3779,7 @@ | ||
3774 | 3779 | c1 = NUL; // end the line |
3775 | 3780 | else if (c1 == ESC) |
3776 | 3781 | aborted = TRUE; |
3777 | - else if (c1 == K_COMMAND) | |
3782 | + else if (c1 == K_COMMAND || c1 == K_SCRIPT_COMMAND) | |
3778 | 3783 | { |
3779 | 3784 | // give a nicer error message for this special case |
3780 | 3785 | emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd)); |
@@ -3804,3 +3809,35 @@ | ||
3804 | 3809 | |
3805 | 3810 | return (char_u *)line_ga.ga_data; |
3806 | 3811 | } |
3812 | + | |
3813 | + int | |
3814 | +do_cmdkey_command(int key, int flags) | |
3815 | +{ | |
3816 | + int res; | |
3817 | +#ifdef FEAT_EVAL | |
3818 | + sctx_T save_current_sctx = {0, 0, 0, 0}; | |
3819 | + | |
3820 | + if (key == K_SCRIPT_COMMAND && last_used_map != NULL) | |
3821 | + { | |
3822 | + save_current_sctx = current_sctx; | |
3823 | + current_sctx = last_used_map->m_script_ctx; | |
3824 | + } | |
3825 | +#endif | |
3826 | + | |
3827 | + res = do_cmdline(NULL, getcmdkeycmd, NULL, flags); | |
3828 | + | |
3829 | +#ifdef FEAT_EVAL | |
3830 | + if (save_current_sctx.sc_sid > 0) | |
3831 | + current_sctx = save_current_sctx; | |
3832 | +#endif | |
3833 | + | |
3834 | + return res; | |
3835 | +} | |
3836 | + | |
3837 | +#if defined(FEAT_EVAL) || defined(PROTO) | |
3838 | + void | |
3839 | +reset_last_used_map(void) | |
3840 | +{ | |
3841 | + last_used_map = NULL; | |
3842 | +} | |
3843 | +#endif |
@@ -2281,7 +2281,8 @@ | ||
2281 | 2281 | |
2282 | 2282 | // Ignore end of Select mode mapping and mouse scroll buttons. |
2283 | 2283 | if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP |
2284 | - || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_COMMAND) | |
2284 | + || c == K_MOUSELEFT || c == K_MOUSERIGHT | |
2285 | + || c == K_COMMAND || c == K_SCRIPT_COMMAND) | |
2285 | 2286 | return retval; |
2286 | 2287 | |
2287 | 2288 | #ifdef FEAT_PROP_POPUP |
@@ -276,6 +276,7 @@ | ||
276 | 276 | , KE_MOUSEMOVE_XY = 101 // KE_MOUSEMOVE with coordinates |
277 | 277 | , KE_CANCEL = 102 // return from vgetc() |
278 | 278 | , KE_COMMAND = 103 // <Cmd> special key |
279 | + , KE_SCRIPT_COMMAND = 104 // <ScriptCmd> special key | |
279 | 280 | }; |
280 | 281 | |
281 | 282 | /* |
@@ -480,6 +481,7 @@ | ||
480 | 481 | #define K_CURSORHOLD TERMCAP2KEY(KS_EXTRA, KE_CURSORHOLD) |
481 | 482 | |
482 | 483 | #define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND) |
484 | +#define K_SCRIPT_COMMAND TERMCAP2KEY(KS_EXTRA, KE_SCRIPT_COMMAND) | |
483 | 485 | |
484 | 486 | // Bits for modifier mask |
485 | 487 | // 0x01 cannot be used, because the modifier must be 0x02 or higher |
@@ -1057,6 +1057,7 @@ | ||
1057 | 1057 | {K_CURSORHOLD, (char_u *)"CursorHold"}, |
1058 | 1058 | {K_IGNORE, (char_u *)"Ignore"}, |
1059 | 1059 | {K_COMMAND, (char_u *)"Cmd"}, |
1060 | + {K_SCRIPT_COMMAND, (char_u *)"ScriptCmd"}, | |
1060 | 1061 | {K_FOCUSGAINED, (char_u *)"FocusGained"}, |
1061 | 1062 | {K_FOCUSLOST, (char_u *)"FocusLost"}, |
1062 | 1063 | {0, NULL} |
@@ -373,6 +373,7 @@ | ||
373 | 373 | {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0}, |
374 | 374 | {K_PS, nv_edit, 0, 0}, |
375 | 375 | {K_COMMAND, nv_colon, 0, 0}, |
376 | + {K_SCRIPT_COMMAND, nv_colon, 0, 0}, | |
376 | 377 | }; |
377 | 378 | |
378 | 379 | // Number of commands in nv_cmds[]. |
@@ -3429,7 +3430,9 @@ | ||
3429 | 3430 | { |
3430 | 3431 | int old_p_im; |
3431 | 3432 | int cmd_result; |
3432 | - int is_cmdkey = cap->cmdchar == K_COMMAND; | |
3433 | + int is_cmdkey = cap->cmdchar == K_COMMAND | |
3434 | + || cap->cmdchar == K_SCRIPT_COMMAND; | |
3435 | + int flags; | |
3433 | 3436 | |
3434 | 3437 | if (VIsual_active && !is_cmdkey) |
3435 | 3438 | nv_operator(cap); |
@@ -3459,8 +3462,11 @@ | ||
3459 | 3462 | old_p_im = p_im; |
3460 | 3463 | |
3461 | 3464 | // get a command line and execute it |
3462 | - cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, | |
3463 | - cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0); | |
3465 | + flags = cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0; | |
3466 | + if (is_cmdkey) | |
3467 | + cmd_result = do_cmdkey_command(cap->cmdchar, flags); | |
3468 | + else | |
3469 | + cmd_result = do_cmdline(NULL, getexline, NULL, flags); | |
3464 | 3470 | |
3465 | 3471 | // If 'insertmode' changed, enter or exit Insert mode |
3466 | 3472 | if (p_im != old_p_im) |
@@ -3501,6 +3501,14 @@ | ||
3501 | 3501 | int rv_arg; // extra argument |
3502 | 3502 | } redo_VIsual_T; |
3503 | 3503 | |
3504 | + static int | |
3505 | +is_ex_cmdchar(cmdarg_T *cap) | |
3506 | +{ | |
3507 | + return cap->cmdchar == ':' | |
3508 | + || cap->cmdchar == K_COMMAND | |
3509 | + || cap->cmdchar == K_SCRIPT_COMMAND; | |
3510 | +} | |
3511 | + | |
3504 | 3512 | /* |
3505 | 3513 | * Handle an operator after Visual mode or when the movement is finished. |
3506 | 3514 | * "gui_yank" is true when yanking text for the clipboard. |
@@ -3583,8 +3591,7 @@ | ||
3583 | 3591 | && ((!VIsual_active || oap->motion_force) |
3584 | 3592 | // Also redo Operator-pending Visual mode mappings |
3585 | 3593 | || (VIsual_active |
3586 | - && (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) | |
3587 | - && oap->op_type != OP_COLON)) | |
3594 | + && is_ex_cmdchar(cap) && oap->op_type != OP_COLON)) | |
3588 | 3595 | && cap->cmdchar != 'D' |
3589 | 3596 | #ifdef FEAT_FOLDING |
3590 | 3597 | && oap->op_type != OP_FOLD |
@@ -3608,7 +3615,7 @@ | ||
3608 | 3615 | AppendToRedobuffLit(cap->searchbuf, -1); |
3609 | 3616 | AppendToRedobuff(NL_STR); |
3610 | 3617 | } |
3611 | - else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) | |
3618 | + else if (is_ex_cmdchar(cap)) | |
3612 | 3619 | { |
3613 | 3620 | // do_cmdline() has stored the first typed line in |
3614 | 3621 | // "repeat_cmdline". When several lines are typed repeating |
@@ -3806,7 +3813,7 @@ | ||
3806 | 3813 | get_op_char(oap->op_type), |
3807 | 3814 | get_extra_op_char(oap->op_type), |
3808 | 3815 | oap->motion_force, cap->cmdchar, cap->nchar); |
3809 | - else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) | |
3816 | + else if (!is_ex_cmdchar(cap)) | |
3810 | 3817 | { |
3811 | 3818 | int opchar = get_op_char(oap->op_type); |
3812 | 3819 | int extra_opchar = get_extra_op_char(oap->op_type); |
@@ -52,5 +52,6 @@ | ||
52 | 52 | void vungetc(int c); |
53 | 53 | int fix_input_buffer(char_u *buf, int len); |
54 | 54 | int input_available(void); |
55 | -char_u *getcmdkeycmd(int promptc, void *cookie, int indent, getline_opt_T do_concat); | |
55 | +int do_cmdkey_command(int key, int flags); | |
56 | +void reset_last_used_map(void); | |
56 | 57 | /* vim: set ft=c : */ |
@@ -2229,7 +2229,8 @@ | ||
2229 | 2229 | break; |
2230 | 2230 | |
2231 | 2231 | case K_COMMAND: |
2232 | - return do_cmdline(NULL, getcmdkeycmd, NULL, 0); | |
2232 | + case K_SCRIPT_COMMAND: | |
2233 | + return do_cmdkey_command(c, 0); | |
2233 | 2234 | } |
2234 | 2235 | if (typed) |
2235 | 2236 | mouse_was_outside = FALSE; |
@@ -1337,6 +1337,9 @@ | ||
1337 | 1337 | export def Toggle(): string |
1338 | 1338 | return ":g:toggle_called = 'yes'\<CR>" |
1339 | 1339 | enddef |
1340 | + export def Doit() | |
1341 | + g:doit_called = 'yes' | |
1342 | + enddef | |
1340 | 1343 | END |
1341 | 1344 | writefile(lines, 'Xdir/autoload/toggle.vim') |
1342 | 1345 |
@@ -1346,6 +1349,8 @@ | ||
1346 | 1349 | import autoload 'toggle.vim' |
1347 | 1350 | |
1348 | 1351 | nnoremap <silent> <expr> tt toggle.Toggle() |
1352 | + nnoremap <silent> xx <ScriptCmd>toggle.Doit()<CR> | |
1353 | + nnoremap <silent> yy <Cmd>toggle.Doit()<CR> | |
1349 | 1354 | END |
1350 | 1355 | CheckScriptSuccess(lines) |
1351 | 1356 | assert_false(exists("g:toggle_loaded")) |
@@ -1355,7 +1360,14 @@ | ||
1355 | 1360 | assert_equal('yes', g:toggle_loaded) |
1356 | 1361 | assert_equal('yes', g:toggle_called) |
1357 | 1362 | |
1363 | + feedkeys("xx", 'xt') | |
1364 | + assert_equal('yes', g:doit_called) | |
1365 | + | |
1366 | + assert_fails('call feedkeys("yy", "xt")', 'E121: Undefined variable: toggle') | |
1367 | + | |
1358 | 1368 | nunmap tt |
1369 | + nunmap xx | |
1370 | + nunmap yy | |
1359 | 1371 | unlet g:toggle_loaded |
1360 | 1372 | unlet g:toggle_called |
1361 | 1373 | delete('Xdir', 'rf') |
@@ -751,6 +751,8 @@ | ||
751 | 751 | static int included_patches[] = |
752 | 752 | { /* Add new patch number below this line */ |
753 | 753 | /**/ |
754 | + 4099, | |
755 | +/**/ | |
754 | 756 | 4098, |
755 | 757 | /**/ |
756 | 758 | 4097, |