Merge #4419 'implement <Cmd> key'
This commit is contained in:
commit
6a7c904648
|
@ -232,8 +232,10 @@ For this reason the following is blocked:
|
|||
- Editing another buffer.
|
||||
- The |:normal| command.
|
||||
- Moving the cursor is allowed, but it is restored afterwards.
|
||||
- If the cmdline is changed, the old text and cursor position are restored.
|
||||
If you want the mapping to do any of these let the returned characters do
|
||||
that.
|
||||
that. Alternatively use a |<Cmd>| mapping which doesn't have these
|
||||
restrictions.
|
||||
|
||||
You can use getchar(), it consumes typeahead if there is any. E.g., if you
|
||||
have these mappings: >
|
||||
|
@ -272,6 +274,29 @@ again for using <expr>. This does work: >
|
|||
Using 0x80 as a single byte before other text does not work, it will be seen
|
||||
as a special key.
|
||||
|
||||
*<Cmd>* *:map-command*
|
||||
A command mapping is a mapping that directly executes a command. Command
|
||||
mappings are written by placing a command in between <Cmd> and <CR> in the
|
||||
rhs of a mapping (in any mode): >
|
||||
noremap <f3> <Cmd>echo mode(1)<cr>
|
||||
<
|
||||
*E5520*
|
||||
The command must be complete and ended with a <CR>. If the command is
|
||||
incomplete, an error is raised. |Command-line| mode is never entered.
|
||||
|
||||
This is more flexible than using `:<c-u>` in visual and operator pending
|
||||
mode, or `<c-o>:` in insert mode, as the commands are exectued directly in the
|
||||
mode, and not normal mode. Also visual mode is not aborted. Commands can be
|
||||
invoked directly in cmdline mode, which is not simple otherwise (a timer has
|
||||
to be used). Unlike <expr> mappings, there are not any specific restrictions
|
||||
what the command can do, except for what is normally possible to do in every
|
||||
specific mode. The command should be executed the same way as if an
|
||||
(unrestricted) |autocmd| was invoked or an async event event was processed.
|
||||
|
||||
Note: In select mode, |:map| or |:vmap| command mappings will be executed in
|
||||
visual mode. If a mapping is intended to work in select mode, it is
|
||||
recomendend to map it using |:smap|, possibly in addition to the same mapping
|
||||
with |:map| or |:xmap|.
|
||||
|
||||
1.3 MAPPING AND MODES *:map-modes*
|
||||
*mapmode-nvo* *mapmode-n* *mapmode-v* *mapmode-o* *mapmode-t*
|
||||
|
|
|
@ -974,6 +974,10 @@ static int insert_handle_key(InsertState *s)
|
|||
multiqueue_process_events(main_loop.events);
|
||||
break;
|
||||
|
||||
case K_COMMAND: // some command
|
||||
do_cmdline(NULL, getcmdkeycmd, NULL, 0);
|
||||
break;
|
||||
|
||||
case K_HOME: // <Home>
|
||||
case K_KHOME:
|
||||
case K_S_HOME:
|
||||
|
|
|
@ -8156,6 +8156,10 @@ static void ex_startinsert(exarg_T *eap)
|
|||
restart_edit = 'i';
|
||||
curwin->w_curswant = 0; /* avoid MAXCOL */
|
||||
}
|
||||
|
||||
if (VIsual_active) {
|
||||
showmode();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -512,8 +512,12 @@ static int command_line_execute(VimState *state, int key)
|
|||
CommandLineState *s = (CommandLineState *)state;
|
||||
s->c = key;
|
||||
|
||||
if (s->c == K_EVENT) {
|
||||
multiqueue_process_events(main_loop.events);
|
||||
if (s->c == K_EVENT || s->c == K_COMMAND) {
|
||||
if (s->c == K_EVENT) {
|
||||
multiqueue_process_events(main_loop.events);
|
||||
} else {
|
||||
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
|
||||
}
|
||||
redrawcmdline();
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -4247,3 +4247,70 @@ mapblock_T *get_maphash(int index, buf_T *buf)
|
|||
|
||||
return (buf == NULL) ? maphash[index] : buf->b_maphash[index];
|
||||
}
|
||||
|
||||
/// Get command argument for <Cmd> key
|
||||
char_u * getcmdkeycmd(int promptc, void *cookie, int indent)
|
||||
{
|
||||
garray_T line_ga;
|
||||
int c1 = -1, c2;
|
||||
int cmod = 0;
|
||||
bool aborted = false;
|
||||
|
||||
ga_init(&line_ga, 1, 32);
|
||||
|
||||
no_mapping++;
|
||||
|
||||
got_int = false;
|
||||
while (c1 != NUL && !aborted) {
|
||||
ga_grow(&line_ga, 32);
|
||||
|
||||
if (vgetorpeek(false) == NUL) {
|
||||
// incomplete <Cmd> is an error, because there is not much the user
|
||||
// could do in this state.
|
||||
EMSG(e_cmdmap_err);
|
||||
aborted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get one character at a time.
|
||||
c1 = vgetorpeek(true);
|
||||
// Get two extra bytes for special keys
|
||||
if (c1 == K_SPECIAL) {
|
||||
c1 = vgetorpeek(true); // no mapping for these chars
|
||||
c2 = vgetorpeek(true);
|
||||
if (c1 == KS_MODIFIER) {
|
||||
cmod = c2;
|
||||
continue;
|
||||
}
|
||||
c1 = TO_SPECIAL(c1, c2);
|
||||
}
|
||||
|
||||
|
||||
if (got_int) {
|
||||
aborted = true;
|
||||
} else if (c1 == '\r' || c1 == '\n') {
|
||||
c1 = NUL; // end the line
|
||||
} else if (c1 == ESC) {
|
||||
aborted = true;
|
||||
} else if (c1 == K_COMMAND) {
|
||||
// special case to give nicer error message
|
||||
EMSG(e_cmdmap_repeated);
|
||||
aborted = true;
|
||||
} else if (IS_SPECIAL(c1)) {
|
||||
EMSG2(e_cmdmap_key, get_special_key_name(c1, cmod));
|
||||
aborted = true;
|
||||
} else {
|
||||
ga_append(&line_ga, (char)c1);
|
||||
}
|
||||
|
||||
cmod = 0;
|
||||
}
|
||||
|
||||
no_mapping--;
|
||||
|
||||
if (aborted) {
|
||||
ga_clear(&line_ga);
|
||||
}
|
||||
|
||||
return (char_u *)line_ga.ga_data;
|
||||
}
|
||||
|
|
|
@ -1154,6 +1154,12 @@ EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
|
|||
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
|
||||
EXTERN char_u e_autocmd_err[] INIT(=N_(
|
||||
"E5500: autocmd has thrown an exception: %s"));
|
||||
EXTERN char_u e_cmdmap_err[] INIT(=N_(
|
||||
"E5520: <Cmd> mapping must end with <CR>"));
|
||||
EXTERN char_u e_cmdmap_repeated[] INIT(=N_(
|
||||
"E5521: <Cmd> mapping must end with <CR> before second <Cmd>"));
|
||||
EXTERN char_u e_cmdmap_key[] INIT(=N_(
|
||||
"E5522: <Cmd> mapping must not include %s key"));
|
||||
|
||||
|
||||
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
|
||||
|
|
|
@ -285,6 +285,7 @@ static const struct key_name_entry {
|
|||
{ K_SNR, "SNR" },
|
||||
{ K_PLUG, "Plug" },
|
||||
{ K_PASTE, "Paste" },
|
||||
{ K_COMMAND, "Cmd" },
|
||||
{ 0, NULL }
|
||||
// NOTE: When adding a long name update MAX_KEY_NAME_LEN.
|
||||
};
|
||||
|
|
|
@ -243,6 +243,7 @@ enum key_extra {
|
|||
, KE_EVENT // event
|
||||
, KE_PASTE // special key to toggle the 'paste' option.
|
||||
// sent only by UIs
|
||||
, KE_COMMAND // special key to execute command in any mode
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -431,6 +432,7 @@ enum key_extra {
|
|||
|
||||
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
|
||||
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)
|
||||
#define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND)
|
||||
|
||||
/* Bits for modifier mask */
|
||||
/* 0x01 cannot be used, because the modifier must be 0x02 or higher */
|
||||
|
|
|
@ -345,6 +345,7 @@ static const struct nv_cmd {
|
|||
{ K_F8, farsi_f8, 0, 0 },
|
||||
{ K_F9, farsi_f9, 0, 0 },
|
||||
{ K_EVENT, nv_event, NV_KEEPREG, 0 },
|
||||
{ K_COMMAND, nv_colon, 0, 0 },
|
||||
};
|
||||
|
||||
/* Number of commands in nv_cmds[]. */
|
||||
|
@ -1473,13 +1474,13 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|
|||
AppendToRedobuffLit(cap->searchbuf, -1);
|
||||
}
|
||||
AppendToRedobuff(NL_STR);
|
||||
} else if (cap->cmdchar == ':') {
|
||||
/* do_cmdline() has stored the first typed line in
|
||||
* "repeat_cmdline". When several lines are typed repeating
|
||||
* won't be possible. */
|
||||
if (repeat_cmdline == NULL)
|
||||
} 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
|
||||
// won't be possible.
|
||||
if (repeat_cmdline == NULL) {
|
||||
ResetRedobuff();
|
||||
else {
|
||||
} else {
|
||||
AppendToRedobuffLit(repeat_cmdline, -1);
|
||||
AppendToRedobuff(NL_STR);
|
||||
xfree(repeat_cmdline);
|
||||
|
@ -4524,23 +4525,22 @@ static void nv_exmode(cmdarg_T *cap)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a ":" command.
|
||||
*/
|
||||
/// Handle a ":" command and <Cmd>.
|
||||
static void nv_colon(cmdarg_T *cap)
|
||||
{
|
||||
int old_p_im;
|
||||
bool cmd_result;
|
||||
bool is_cmdkey = cap->cmdchar == K_COMMAND;
|
||||
|
||||
if (VIsual_active)
|
||||
if (VIsual_active && !is_cmdkey) {
|
||||
nv_operator(cap);
|
||||
else {
|
||||
} else {
|
||||
if (cap->oap->op_type != OP_NOP) {
|
||||
// Using ":" as a movement is characterwise exclusive.
|
||||
cap->oap->motion_type = kMTCharWise;
|
||||
cap->oap->inclusive = false;
|
||||
} else if (cap->count0) {
|
||||
/* translate "count:" into ":.,.+(count - 1)" */
|
||||
} else if (cap->count0 && !is_cmdkey) {
|
||||
// translate "count:" into ":.,.+(count - 1)"
|
||||
stuffcharReadbuff('.');
|
||||
if (cap->count0 > 1) {
|
||||
stuffReadbuff(",.+");
|
||||
|
@ -4554,9 +4554,9 @@ static void nv_colon(cmdarg_T *cap)
|
|||
|
||||
old_p_im = p_im;
|
||||
|
||||
/* get a command line and execute it */
|
||||
cmd_result = do_cmdline(NULL, getexline, NULL,
|
||||
cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
|
||||
// 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) {
|
||||
|
|
|
@ -6748,16 +6748,20 @@ int showmode(void)
|
|||
if (p_ri)
|
||||
MSG_PUTS_ATTR(_(" REVERSE"), attr);
|
||||
MSG_PUTS_ATTR(_(" INSERT"), attr);
|
||||
} else if (restart_edit == 'I')
|
||||
} else if (restart_edit == 'I' || restart_edit == 'i'
|
||||
|| restart_edit == 'a') {
|
||||
MSG_PUTS_ATTR(_(" (insert)"), attr);
|
||||
else if (restart_edit == 'R')
|
||||
} else if (restart_edit == 'R') {
|
||||
MSG_PUTS_ATTR(_(" (replace)"), attr);
|
||||
else if (restart_edit == 'V')
|
||||
} else if (restart_edit == 'V') {
|
||||
MSG_PUTS_ATTR(_(" (vreplace)"), attr);
|
||||
if (p_hkmap)
|
||||
}
|
||||
if (p_hkmap) {
|
||||
MSG_PUTS_ATTR(_(" Hebrew"), attr);
|
||||
if (p_fkmap)
|
||||
}
|
||||
if (p_fkmap) {
|
||||
MSG_PUTS_ATTR(farsi_text_5, attr);
|
||||
}
|
||||
if (State & LANGMAP) {
|
||||
if (curwin->w_p_arab) {
|
||||
MSG_PUTS_ATTR(_(" Arabic"), attr);
|
||||
|
|
|
@ -461,6 +461,10 @@ static int terminal_execute(VimState *state, int key)
|
|||
}
|
||||
break;
|
||||
|
||||
case K_COMMAND:
|
||||
do_cmdline(NULL, getcmdkeycmd, NULL, 0);
|
||||
break;
|
||||
|
||||
case Ctrl_N:
|
||||
if (s->got_bsl) {
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,771 @@
|
|||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear = helpers.clear
|
||||
local feed_command = helpers.feed_command
|
||||
local feed = helpers.feed
|
||||
local eq = helpers.eq
|
||||
local expect = helpers.expect
|
||||
local eval = helpers.eval
|
||||
local funcs = helpers.funcs
|
||||
local insert = helpers.insert
|
||||
local exc_exec = helpers.exc_exec
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
describe('mappings with <Cmd>', function()
|
||||
local screen
|
||||
local function cmdmap(lhs, rhs)
|
||||
feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
|
||||
feed_command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = Screen.new(65, 8)
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||
[3] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||
[4] = {bold = true},
|
||||
[5] = {background = Screen.colors.LightGrey},
|
||||
[6] = {foreground = Screen.colors.Blue1},
|
||||
})
|
||||
screen:attach()
|
||||
|
||||
cmdmap('<F3>', 'let m = mode(1)')
|
||||
cmdmap('<F4>', 'normal! ww')
|
||||
cmdmap('<F5>', 'normal! "ay')
|
||||
cmdmap('<F6>', 'throw "very error"')
|
||||
feed_command([[
|
||||
function! TextObj()
|
||||
if mode() !=# "v"
|
||||
normal! v
|
||||
end
|
||||
call cursor(1,3)
|
||||
normal! o
|
||||
call cursor(2,4)
|
||||
endfunction]])
|
||||
cmdmap('<F7>', 'call TextObj()')
|
||||
insert([[
|
||||
some short lines
|
||||
of test text]])
|
||||
feed('gg')
|
||||
cmdmap('<F8>', 'startinsert')
|
||||
cmdmap('<F9>', 'stopinsert')
|
||||
feed_command("abbr foo <Cmd>let g:y = 17<cr>bar")
|
||||
end)
|
||||
|
||||
it('can be displayed', function()
|
||||
feed_command('map <F3>')
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{6:<F3>} {6:*} {6:<Cmd>}let m = mode(1){6:<CR>} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('handles invalid mappings', function()
|
||||
feed_command('let x = 0')
|
||||
feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
|
||||
feed('<F3>')
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} |
|
||||
]])
|
||||
|
||||
feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>')
|
||||
feed('<F3>')
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:E5522: <Cmd> mapping must not include <F3> key} |
|
||||
]])
|
||||
|
||||
feed_command('noremap <F3> <Cmd>let x = 3')
|
||||
feed('<F3>')
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:E5520: <Cmd> mapping must end with <CR>} |
|
||||
]])
|
||||
eq(0, eval('x'))
|
||||
end)
|
||||
|
||||
it('works in various modes and sees correct `mode()` value', function()
|
||||
-- normal mode
|
||||
feed('<F3>')
|
||||
eq('n', eval('m'))
|
||||
|
||||
-- visual mode
|
||||
feed('v<F3>')
|
||||
eq('v', eval('m'))
|
||||
-- didn't leave visual mode
|
||||
eq('v', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- visual mapping in select mode
|
||||
feed('gh<F3>')
|
||||
eq('v', eval('m'))
|
||||
-- didn't leave select mode
|
||||
eq('s', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- select mode mapping
|
||||
feed_command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
|
||||
feed('gh<F3>')
|
||||
eq('s', eval('m'))
|
||||
-- didn't leave select mode
|
||||
eq('s', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- operator-pending mode
|
||||
feed("d<F3>")
|
||||
eq('no', eval('m'))
|
||||
-- did leave operator-pending mode
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
--insert mode
|
||||
feed('i<F3>')
|
||||
eq('i', eval('m'))
|
||||
eq('i', eval('mode(1)'))
|
||||
|
||||
-- replace mode
|
||||
feed("<Ins><F3>")
|
||||
eq('R', eval('m'))
|
||||
eq('R', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- virtual replace mode
|
||||
feed("gR<F3>")
|
||||
eq('Rv', eval('m'))
|
||||
eq('Rv', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- langmap works, but is not distinguished in mode(1)
|
||||
feed(":set iminsert=1<cr>i<F3>")
|
||||
eq('i', eval('m'))
|
||||
eq('i', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
feed(':<F3>')
|
||||
eq('c', eval('m'))
|
||||
eq('c', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
-- terminal mode
|
||||
feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
|
||||
feed_command('split | terminal')
|
||||
feed('i')
|
||||
eq('t', eval('mode(1)'))
|
||||
feed('<F3>')
|
||||
eq('t', eval('m'))
|
||||
eq('t', eval('mode(1)'))
|
||||
end)
|
||||
|
||||
it('works in normal mode', function()
|
||||
cmdmap('<F2>', 'let s = [mode(1), v:count, v:register]')
|
||||
|
||||
-- check v:count and v:register works
|
||||
feed('<F2>')
|
||||
eq({'n', 0, '"'}, eval('s'))
|
||||
feed('7<F2>')
|
||||
eq({'n', 7, '"'}, eval('s'))
|
||||
feed('"e<F2>')
|
||||
eq({'n', 0, 'e'}, eval('s'))
|
||||
feed('5"k<F2>')
|
||||
eq({'n', 5, 'k'}, eval('s'))
|
||||
feed('"+2<F2>')
|
||||
eq({'n', 2, '+'}, eval('s'))
|
||||
|
||||
-- text object enters visual mode
|
||||
feed('<F7>')
|
||||
screen:expect([[
|
||||
so{5:me short lines} |
|
||||
{5:of }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- VISUAL --} |
|
||||
]])
|
||||
feed('<esc>')
|
||||
|
||||
-- startinsert
|
||||
feed('<F8>')
|
||||
eq('i', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
|
||||
eq('n', eval('mode(1)'))
|
||||
cmdmap(',a', 'call feedkeys("aalpha") \\| let g:a = getline(2)')
|
||||
cmdmap(',b', 'call feedkeys("abeta", "x") \\| let g:b = getline(2)')
|
||||
|
||||
feed(',a<F3>')
|
||||
screen:expect([[
|
||||
some short lines |
|
||||
of alpha^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
-- feedkeys were not executed immediately
|
||||
eq({'n', 'of test text'}, eval('[m,a]'))
|
||||
eq('i', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
|
||||
feed(',b<F3>')
|
||||
screen:expect([[
|
||||
some short lines |
|
||||
of alphabet^atest text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
-- feedkeys(..., 'x') was executed immediately, but insert mode gets aborted
|
||||
eq({'n', 'of alphabetatest text'}, eval('[m,b]'))
|
||||
eq('n', eval('mode(1)'))
|
||||
end)
|
||||
|
||||
it('works in :normal command', function()
|
||||
feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
|
||||
feed_command('noremap ,f <Cmd>nosuchcommand<cr>')
|
||||
feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
|
||||
feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
|
||||
feed_command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
|
||||
|
||||
feed(":normal ,x<cr>")
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
aa |
|
||||
xx |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
|
||||
eq('Vim:E492: Not an editor command: nosuchcommand', exc_exec("normal ,f"))
|
||||
eq('very error', exc_exec("normal ,e"))
|
||||
eq('Vim(echoerr):The message.', exc_exec("normal ,m"))
|
||||
feed('w')
|
||||
screen:expect([[
|
||||
some ^short lines |
|
||||
aa |
|
||||
xx |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
|
||||
feed_command(':%d')
|
||||
eq('Vim(echoerr):Err', exc_exec("normal ,w"))
|
||||
screen:expect([[
|
||||
^ |
|
||||
0 |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
--No lines in buffer-- |
|
||||
]])
|
||||
|
||||
feed_command(':%d')
|
||||
feed_command(':normal ,w')
|
||||
screen:expect([[
|
||||
^ |
|
||||
4 |
|
||||
3 |
|
||||
2 |
|
||||
1 |
|
||||
0 |
|
||||
{1:~ }|
|
||||
{2:Err} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('works in visual mode', function()
|
||||
-- can extend visual mode
|
||||
feed('v<F4>')
|
||||
screen:expect([[
|
||||
{5:some short }^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- VISUAL --} |
|
||||
]])
|
||||
eq('v', funcs.mode(1))
|
||||
|
||||
-- can invoke operator, ending visual mode
|
||||
feed('<F5>')
|
||||
eq('n', funcs.mode(1))
|
||||
eq({'some short l'}, funcs.getreg('a',1,1))
|
||||
|
||||
-- error doesn't interrupt visual mode
|
||||
feed('ggvw<F6>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<cr>')
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
-- still in visual mode, <cr> was consumed by the error prompt
|
||||
screen:expect([[
|
||||
{5:some }^short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- VISUAL --} |
|
||||
]])
|
||||
eq('v', funcs.mode(1))
|
||||
feed('<F7>')
|
||||
screen:expect([[
|
||||
so{5:me short lines} |
|
||||
{5:of }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- VISUAL --} |
|
||||
]])
|
||||
eq('v', funcs.mode(1))
|
||||
|
||||
-- startinsert gives "-- (insert) VISUAL --" mode
|
||||
feed('<F8>')
|
||||
screen:expect([[
|
||||
so{5:me short lines} |
|
||||
{5:of }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- (insert) VISUAL --} |
|
||||
]])
|
||||
eq('v', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('i', eval('mode(1)'))
|
||||
end)
|
||||
|
||||
it('works in select mode', function()
|
||||
feed_command('snoremap <F1> <cmd>throw "very error"<cr>')
|
||||
feed_command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
|
||||
-- can extend select mode
|
||||
feed('gh<F4>')
|
||||
screen:expect([[
|
||||
{5:some short }^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- SELECT --} |
|
||||
]])
|
||||
eq('s', funcs.mode(1))
|
||||
|
||||
-- visual mapping in select mode restart selct mode after operator
|
||||
feed('<F5>')
|
||||
eq('s', funcs.mode(1))
|
||||
eq({'some short l'}, funcs.getreg('a',1,1))
|
||||
|
||||
-- select mode mapping works, and does not restart select mode
|
||||
feed('<F2>')
|
||||
eq('n', funcs.mode(1))
|
||||
eq({'some short l'}, funcs.getreg('b',1,1))
|
||||
|
||||
-- error doesn't interrupt temporary visual mode
|
||||
feed('<esc>ggvw<c-g><F6>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<cr>')
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
-- still in visual mode, <cr> was consumed by the error prompt
|
||||
screen:expect([[
|
||||
{5:some }^short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- VISUAL --} |
|
||||
]])
|
||||
-- quirk: restoration of select mode is not performed
|
||||
eq('v', funcs.mode(1))
|
||||
|
||||
-- error doesn't interrupt select mode
|
||||
feed('<esc>ggvw<c-g><F1>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<cr>')
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
-- still in select mode, <cr> was consumed by the error prompt
|
||||
screen:expect([[
|
||||
{5:some }^short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- SELECT --} |
|
||||
]])
|
||||
-- quirk: restoration of select mode is not performed
|
||||
eq('s', funcs.mode(1))
|
||||
|
||||
feed('<F7>')
|
||||
screen:expect([[
|
||||
so{5:me short lines} |
|
||||
{5:of }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- SELECT --} |
|
||||
]])
|
||||
eq('s', funcs.mode(1))
|
||||
|
||||
-- startinsert gives "-- SELECT (insert) --" mode
|
||||
feed('<F8>')
|
||||
screen:expect([[
|
||||
so{5:me short lines} |
|
||||
{5:of }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- (insert) SELECT --} |
|
||||
]])
|
||||
eq('s', eval('mode(1)'))
|
||||
feed('<esc>')
|
||||
eq('i', eval('mode(1)'))
|
||||
end)
|
||||
|
||||
|
||||
it('works in operator-pending mode', function()
|
||||
feed('d<F4>')
|
||||
expect([[
|
||||
lines
|
||||
of test text]])
|
||||
eq({'some short '}, funcs.getreg('"',1,1))
|
||||
feed('.')
|
||||
expect([[
|
||||
test text]])
|
||||
eq({'lines', 'of '}, funcs.getreg('"',1,1))
|
||||
feed('uu')
|
||||
expect([[
|
||||
some short lines
|
||||
of test text]])
|
||||
|
||||
-- error aborts operator-pending, operator not performed
|
||||
feed('d<F6>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
feed('<cr>')
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
expect([[
|
||||
some short lines
|
||||
of test text]])
|
||||
|
||||
feed('"bd<F7>')
|
||||
expect([[
|
||||
soest text]])
|
||||
eq(funcs.getreg('b',1,1), {'me short lines', 'of t'})
|
||||
|
||||
-- startinsert aborts operator
|
||||
feed('d<F8>')
|
||||
eq('i', eval('mode(1)'))
|
||||
expect([[
|
||||
soest text]])
|
||||
end)
|
||||
|
||||
it('works in insert mode', function()
|
||||
|
||||
-- works the same as <c-o>w<c-o>w
|
||||
feed('iindeed <F4>little ')
|
||||
screen:expect([[
|
||||
indeed some short little ^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<F6>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
|
||||
|
||||
feed('<cr>')
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
-- still in insert
|
||||
screen:expect([[
|
||||
indeed some short little ^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
eq('i', eval('mode(1)'))
|
||||
|
||||
-- When entering visual mode from InsertEnter autocmd, an async event, or
|
||||
-- a <cmd> mapping, vim ends up in undocumented "INSERT VISUAL" mode. If a
|
||||
-- vim patch decides to disable this mode, this test is expected to fail.
|
||||
feed('<F7>stuff ')
|
||||
screen:expect([[
|
||||
in{5:deed some short little lines} |
|
||||
{5:of stuff }^test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT VISUAL --} |
|
||||
]])
|
||||
expect([[
|
||||
indeed some short little lines
|
||||
of stuff test text]])
|
||||
|
||||
feed('<F5>')
|
||||
eq(funcs.getreg('a',1,1), {'deed some short little lines', 'of stuff t'})
|
||||
|
||||
-- still in insert
|
||||
screen:expect([[
|
||||
in^deed some short little lines |
|
||||
of stuff test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
eq('i', eval('mode(1)'))
|
||||
|
||||
-- also works as part of abbreviation
|
||||
feed('<space>foo ')
|
||||
screen:expect([[
|
||||
in bar ^deed some short little lines |
|
||||
of stuff test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
eq(17, eval('g:y'))
|
||||
|
||||
-- :startinsert does nothing
|
||||
feed('<F8>')
|
||||
eq('i', eval('mode(1)'))
|
||||
|
||||
-- :stopinsert works
|
||||
feed('<F9>')
|
||||
eq('n', eval('mode(1)'))
|
||||
end)
|
||||
|
||||
it('works in cmdline mode', function()
|
||||
cmdmap('<F2>', 'call setcmdpos(2)')
|
||||
feed(':text<F3>')
|
||||
eq('c', eval('m'))
|
||||
-- didn't leave cmdline mode
|
||||
eq('c', eval('mode(1)'))
|
||||
feed('<cr>')
|
||||
eq('n', eval('mode(1)'))
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:E492: Not an editor command: text} |
|
||||
]])
|
||||
|
||||
feed(':echo 2<F6>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
:echo 2 |
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
:echo 2^ |
|
||||
]])
|
||||
eq('E605: Exception not caught: very error', eval('v:errmsg'))
|
||||
-- didn't leave cmdline mode
|
||||
eq('c', eval('mode(1)'))
|
||||
feed('+2<cr>')
|
||||
screen:expect([[
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
:echo 2 |
|
||||
{2:Error detected while processing :} |
|
||||
{2:E605: Exception not caught: very error} |
|
||||
4 |
|
||||
{3:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
-- however, message scrolling may cause extra CR prompt
|
||||
-- This is consistent with output from async events.
|
||||
feed('<cr>')
|
||||
screen:expect([[
|
||||
^some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
eq('n', eval('mode(1)'))
|
||||
|
||||
feed(':let g:x = 3<F4>')
|
||||
screen:expect([[
|
||||
some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
:let g:x = 3^ |
|
||||
]])
|
||||
feed('+2<cr>')
|
||||
-- cursor was moved in the background
|
||||
screen:expect([[
|
||||
some short ^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
:let g:x = 3+2 |
|
||||
]])
|
||||
eq(5, eval('g:x'))
|
||||
|
||||
feed(':let g:y = 7<F8>')
|
||||
screen:expect([[
|
||||
some short lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
:let g:y = 7^ |
|
||||
]])
|
||||
eq('c', eval('mode(1)'))
|
||||
feed('+2<cr>')
|
||||
-- startinsert takes effect after leaving cmdline mode
|
||||
screen:expect([[
|
||||
some short ^lines |
|
||||
of test text |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{4:-- INSERT --} |
|
||||
]])
|
||||
eq('i', eval('mode(1)'))
|
||||
eq(9, eval('g:y'))
|
||||
|
||||
end)
|
||||
|
||||
end)
|
||||
|
Loading…
Reference in New Issue