Merge #4419 'implement <Cmd> key'

This commit is contained in:
Justin M. Keyes 2018-03-24 17:45:48 +01:00 committed by GitHub
commit 6a7c904648
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 916 additions and 24 deletions

View File

@ -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*

View File

@ -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:

View File

@ -8156,6 +8156,10 @@ static void ex_startinsert(exarg_T *eap)
restart_edit = 'i';
curwin->w_curswant = 0; /* avoid MAXCOL */
}
if (VIsual_active) {
showmode();
}
}
/*

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"));

View File

@ -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.
};

View File

@ -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 */

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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)