mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
TextYankPost: add information to v:event and update tests
This commit is contained in:
parent
7ab9ff88e6
commit
2359f6f144
@ -308,8 +308,7 @@ Name triggered by ~
|
||||
|InsertCharPre| when a character was typed in Insert mode, before
|
||||
inserting it
|
||||
|
||||
|TextDeletePost| when some text is deleted (dw, dd, D)
|
||||
|TextYankPost| when some text is yanked (yw, yd, Y)
|
||||
|TextYankPost| when some text is yanked or deleted
|
||||
|
||||
|TextChanged| after a change was made to the text in Normal mode
|
||||
|TextChangedI| after a change was made to the text in Insert mode
|
||||
@ -725,24 +724,18 @@ InsertCharPre When a character is typed in Insert mode,
|
||||
It is not allowed to change the text |textlock|.
|
||||
The event is not triggered when 'paste' is
|
||||
set.
|
||||
*TextDeletePost*
|
||||
TextDeletePost Just after a delete (|d|) command is issued.
|
||||
Not issued if the black hole register
|
||||
(|quote_|) is used. The affected text can be
|
||||
accessed by several ways, through @"
|
||||
(|quotequote| or |v:register|: >
|
||||
:echo @"
|
||||
:echo eval('@' . v:register)
|
||||
<
|
||||
*TextYankPost*
|
||||
TextYankPost Just after a |yank| (|y|) command is issued.
|
||||
Not issued if the black hole register
|
||||
(|quote_|) is used. The affected text can be
|
||||
accessed by several ways, through @"
|
||||
(|quotequote| or |v:register|: >
|
||||
:echo @"
|
||||
:echo eval('@' . v:register)
|
||||
<
|
||||
TextYankPost Just after a |yank| or |deleting| command, but not
|
||||
if the black hole register |quote_| is used nor
|
||||
for |setreg()|. Pattern must be * because its
|
||||
meaning may change in the future.
|
||||
Sets these |v:event| keys:
|
||||
operator
|
||||
regcontents
|
||||
regname
|
||||
regtype
|
||||
Recursion is ignored.
|
||||
It is not allowed to change the text |textlock|.
|
||||
*InsertEnter*
|
||||
InsertEnter Just before starting Insert mode. Also for
|
||||
Replace mode and Virtual Replace mode. The
|
||||
|
@ -1389,7 +1389,19 @@ v:errors Errors found by assert functions, such as |assert_true()|.
|
||||
*v:event* *event-variable*
|
||||
v:event Dictionary of event data for the current |autocommand|. The
|
||||
available keys differ per event type and are specified at the
|
||||
documentation for each |event|.
|
||||
documentation for each |event|. The possible keys are:
|
||||
operator The operation performed. Unlike
|
||||
|v:operator|, it is set also for an Ex
|
||||
mode command. For instance, |:yank| is
|
||||
translated to "|y|".
|
||||
regcontents Text stored in the register as a
|
||||
|readfile()|-style list of lines.
|
||||
regname Requested register (e.g "x" for "xyy)
|
||||
or the empty string for an unnamed
|
||||
operation.
|
||||
regtype Type of register as returned by
|
||||
|getregtype()|.
|
||||
|
||||
*v:exception* *exception-variable*
|
||||
v:exception The value of the exception most recently caught and not
|
||||
finished. See also |v:throwpoint| and |throw-variables|.
|
||||
|
@ -83,8 +83,7 @@ return {
|
||||
'TermResponse', -- after setting "v:termresponse"
|
||||
'TextChanged', -- text was modified
|
||||
'TextChangedI', -- text was modified in Insert mode
|
||||
'TextDeletePost', -- after a delete command was done (dd, dw, D)
|
||||
'TextYankPost', -- after a yank command was done (yy, yw, Y)
|
||||
'TextYankPost', -- after a yank or delete was done (y, d, c)
|
||||
'User', -- user defined autocommand
|
||||
'VimEnter', -- after starting Vim
|
||||
'VimLeave', -- before exiting Vim
|
||||
|
@ -1410,8 +1410,9 @@ int op_delete(oparg_T *oap)
|
||||
op_yank_reg(oap, false, reg, false);
|
||||
}
|
||||
|
||||
if(oap->regname == 0) {
|
||||
if (oap->regname == 0) {
|
||||
set_clipboard(0, reg);
|
||||
yank_do_autocmd(oap, reg);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1586,10 +1587,6 @@ int op_delete(oparg_T *oap)
|
||||
|
||||
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
|
||||
|
||||
textlock++;
|
||||
apply_autocmds(EVENT_TEXTDELETEPOST, NULL, NULL, false, curbuf);
|
||||
textlock--;
|
||||
|
||||
setmarks:
|
||||
if (oap->motion_type == MBLOCK) {
|
||||
curbuf->b_op_end.lnum = oap->end.lnum;
|
||||
@ -2314,12 +2311,7 @@ bool op_yank(oparg_T *oap, bool message)
|
||||
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
|
||||
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
|
||||
set_clipboard(oap->regname, reg);
|
||||
|
||||
if (message) {
|
||||
textlock++;
|
||||
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
|
||||
textlock--;
|
||||
}
|
||||
yank_do_autocmd(oap, reg);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2536,6 +2528,74 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx)
|
||||
*pnew = NUL;
|
||||
}
|
||||
|
||||
/// Execute autocommands for TextYankPost.
|
||||
///
|
||||
/// @param oap Operator arguments.
|
||||
/// @param reg The yank register used.
|
||||
static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
static bool recursive = false;
|
||||
|
||||
if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
|
||||
// No autocommand was defined
|
||||
// or we yanked from this autocommand.
|
||||
return;
|
||||
}
|
||||
|
||||
recursive = true;
|
||||
|
||||
// set v:event to a dictionary with information about the yank
|
||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
||||
|
||||
// the yanked text
|
||||
list_T *list = list_alloc();
|
||||
for (linenr_T i = 0; i < reg->y_size; i++) {
|
||||
list_append_string(list, reg->y_array[i], -1);
|
||||
}
|
||||
list->lv_lock = VAR_FIXED;
|
||||
dict_add_list(dict, "regcontents", list);
|
||||
|
||||
// the register type
|
||||
char buf[NUMBUFLEN+2];
|
||||
buf[0] = NUL;
|
||||
buf[1] = NUL;
|
||||
switch (reg->y_type) {
|
||||
case MLINE:
|
||||
buf[0] = 'V';
|
||||
break;
|
||||
case MCHAR:
|
||||
buf[0] = 'v';
|
||||
break;
|
||||
case MBLOCK:
|
||||
buf[0] = Ctrl_V;
|
||||
snprintf(buf + 1, ARRAY_SIZE(buf) - 1, "%" PRId64,
|
||||
(int64_t)(reg->y_width + 1));
|
||||
break;
|
||||
case MAUTO:
|
||||
assert(false);
|
||||
}
|
||||
dict_add_nr_str(dict, "regtype", 0, (char_u *)buf);
|
||||
|
||||
// name of requested register or the empty string for an unnamed operation.
|
||||
buf[0] = (char)oap->regname;
|
||||
buf[1] = NUL;
|
||||
dict_add_nr_str(dict, "regname", 0, (char_u *)buf);
|
||||
|
||||
// kind of operation (yank/delete/change)
|
||||
buf[0] = get_op_char(oap->op_type);
|
||||
buf[1] = NUL;
|
||||
dict_add_nr_str(dict, "operator", 0, (char_u *)buf);
|
||||
|
||||
dict_set_keys_readonly(dict);
|
||||
textlock++;
|
||||
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
|
||||
textlock--;
|
||||
dict_clear(dict);
|
||||
|
||||
recursive = false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put contents of register "regname" into the text.
|
||||
|
@ -1,27 +0,0 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
|
||||
local feed, execute = helpers.feed, helpers.execute
|
||||
|
||||
|
||||
describe('TextDeletePost', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
describe('au TextDeletePost', function()
|
||||
it('is executed after delete', function()
|
||||
feed('ifoo<ESC>')
|
||||
execute('let g:foo = 0')
|
||||
execute('autocmd! TextDeletePost * let g:foo = 1')
|
||||
feed('dd')
|
||||
eq(1, eval('g:foo'))
|
||||
end)
|
||||
it('is not executed after yank', function()
|
||||
feed('ifoo<ESC>')
|
||||
execute('let g:foo = 0')
|
||||
execute('autocmd! TextDeletePost * let g:foo = 1')
|
||||
feed('yy')
|
||||
eq(0, eval('g:foo'))
|
||||
end)
|
||||
end)
|
||||
end)
|
@ -1,27 +0,0 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
|
||||
local feed, execute = helpers.feed, helpers.execute
|
||||
|
||||
|
||||
describe('TextYankPost', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
describe('autocmd TextYankPost', function()
|
||||
it('is executed after yank', function()
|
||||
feed('ifoo<ESC>')
|
||||
execute('let g:foo = 0')
|
||||
execute('autocmd! TextYankPost * let g:foo = 1')
|
||||
feed('yy')
|
||||
eq(1, eval('g:foo'))
|
||||
end)
|
||||
it('is not executed after delete', function()
|
||||
feed('ifoo<ESC>')
|
||||
execute('let g:foo = 0')
|
||||
execute('autocmd! TextYankPost * let g:foo = 1')
|
||||
feed('dd')
|
||||
eq(0, eval('g:foo'))
|
||||
end)
|
||||
end)
|
||||
end)
|
216
test/functional/autocmd/textyankpost_spec.lua
Normal file
216
test/functional/autocmd/textyankpost_spec.lua
Normal file
@ -0,0 +1,216 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local clear, eval, eq, insert = helpers.clear, helpers.eval, helpers.eq, helpers.insert
|
||||
local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command
|
||||
local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq
|
||||
|
||||
describe('TextYankPost', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
|
||||
-- emulate the clipboard so system clipboard isn't affected
|
||||
execute('let &rtp = "test/functional/fixtures,".&rtp')
|
||||
|
||||
execute('let g:count = 0')
|
||||
execute('autocmd TextYankPost * let g:event = copy(v:event)')
|
||||
execute('autocmd TextYankPost * let g:count += 1')
|
||||
|
||||
curbufmeths.set_line_slice(0, -1, true, true, {
|
||||
'foo\0bar',
|
||||
'baz text',
|
||||
})
|
||||
end)
|
||||
|
||||
it('is executed after yank and handles register types', function()
|
||||
feed('yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(1, eval('g:count'))
|
||||
|
||||
-- v:event is cleared after the autocommand is done
|
||||
eq({}, eval('v:event'))
|
||||
|
||||
feed('+yw')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'baz ' },
|
||||
regname = '',
|
||||
regtype = 'v'
|
||||
}, eval('g:event'))
|
||||
eq(2, eval('g:count'))
|
||||
|
||||
feed('<c-v>eky')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo', 'baz' },
|
||||
regname = '',
|
||||
regtype = "\0223" -- ^V + block width
|
||||
}, eval('g:event'))
|
||||
eq(3, eval('g:count'))
|
||||
end)
|
||||
|
||||
it('makes v:event immutable', function()
|
||||
feed('yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
|
||||
execute('set debug=msg')
|
||||
-- the regcontents should not be changed without copy.
|
||||
local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])')
|
||||
eq(status,false)
|
||||
neq(nil, string.find(err, ':E742:'))
|
||||
|
||||
-- can't mutate keys inside the autocommand
|
||||
execute('autocmd! TextYankPost * let v:event.regcontents = 0')
|
||||
status, err = pcall(command,'normal yy')
|
||||
eq(status,false)
|
||||
neq(nil, string.find(err, ':E46:'))
|
||||
|
||||
-- can't add keys inside the autocommand
|
||||
execute('autocmd! TextYankPost * let v:event.mykey = 0')
|
||||
status, err = pcall(command,'normal yy')
|
||||
eq(status,false)
|
||||
neq(nil, string.find(err, ':E742:'))
|
||||
end)
|
||||
|
||||
it('is not invoked recursively', function()
|
||||
execute('autocmd TextYankPost * normal "+yy')
|
||||
feed('yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(1, eval('g:count'))
|
||||
eq({ 'foo\nbar' }, funcs.getreg('+',1,1))
|
||||
end)
|
||||
|
||||
it('is executed after delete and change', function()
|
||||
feed('dw')
|
||||
eq({
|
||||
operator = 'd',
|
||||
regcontents = { 'foo' },
|
||||
regname = '',
|
||||
regtype = 'v'
|
||||
}, eval('g:event'))
|
||||
eq(1, eval('g:count'))
|
||||
|
||||
feed('dd')
|
||||
eq({
|
||||
operator = 'd',
|
||||
regcontents = { '\nbar' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(2, eval('g:count'))
|
||||
|
||||
feed('cwspam<esc>')
|
||||
eq({
|
||||
operator = 'c',
|
||||
regcontents = { 'baz' },
|
||||
regname = '',
|
||||
regtype = 'v'
|
||||
}, eval('g:event'))
|
||||
eq(3, eval('g:count'))
|
||||
end)
|
||||
|
||||
it('is not executed after black-hole operation', function()
|
||||
feed('"_dd')
|
||||
eq(0, eval('g:count'))
|
||||
|
||||
feed('"_cwgood<esc>')
|
||||
eq(0, eval('g:count'))
|
||||
|
||||
expect([[
|
||||
good text]])
|
||||
feed('"_yy')
|
||||
eq(0, eval('g:count'))
|
||||
|
||||
execute('delete _')
|
||||
eq(0, eval('g:count'))
|
||||
end)
|
||||
|
||||
it('gives the correct register name', function()
|
||||
feed('$"byiw')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'bar' },
|
||||
regname = 'b',
|
||||
regtype = 'v'
|
||||
}, eval('g:event'))
|
||||
|
||||
feed('"*yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '*',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
|
||||
execute("set clipboard=unnamed")
|
||||
|
||||
-- regname still shows the name the user requested
|
||||
feed('yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
|
||||
feed('"*yy')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '*',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
end)
|
||||
|
||||
it('works with Ex commands', function()
|
||||
execute('1delete +')
|
||||
eq({
|
||||
operator = 'd',
|
||||
regcontents = { 'foo\nbar' },
|
||||
regname = '+',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(1, eval('g:count'))
|
||||
|
||||
execute('yank')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'baz text' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(2, eval('g:count'))
|
||||
|
||||
execute('normal yw')
|
||||
eq({
|
||||
operator = 'y',
|
||||
regcontents = { 'baz ' },
|
||||
regname = '',
|
||||
regtype = 'v'
|
||||
}, eval('g:event'))
|
||||
eq(3, eval('g:count'))
|
||||
|
||||
execute('normal! dd')
|
||||
eq({
|
||||
operator = 'd',
|
||||
regcontents = { 'baz text' },
|
||||
regname = '',
|
||||
regtype = 'V'
|
||||
}, eval('g:event'))
|
||||
eq(4, eval('g:count'))
|
||||
end)
|
||||
|
||||
end)
|
Loading…
Reference in New Issue
Block a user