aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2016-02-29 15:28:10 +0100
committerBjörn Linse <bjorn.linse@gmail.com>2016-02-29 16:07:50 +0100
commit2359f6f144206707e2db78f5c3cfd6644f9ffd03 (patch)
treef9fc24093054aa322ffbe726d8c33b3a2473799c
parent7ab9ff88e6c7d234c5e9189521da539d20b5bfa7 (diff)
downloadrneovim-2359f6f144206707e2db78f5c3cfd6644f9ffd03.tar.gz
rneovim-2359f6f144206707e2db78f5c3cfd6644f9ffd03.tar.bz2
rneovim-2359f6f144206707e2db78f5c3cfd6644f9ffd03.zip
TextYankPost: add information to v:event and update tests
-rw-r--r--runtime/doc/autocmd.txt31
-rw-r--r--runtime/doc/eval.txt14
-rw-r--r--src/nvim/auevents.lua3
-rw-r--r--src/nvim/ops.c82
-rw-r--r--test/functional/autocmd/text_deletepost.lua27
-rw-r--r--test/functional/autocmd/text_yankpost.lua27
-rw-r--r--test/functional/autocmd/textyankpost_spec.lua216
7 files changed, 313 insertions, 87 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 5f36079a39..ec5818e16f 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -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
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index bc3dda97a5..51e2b505f5 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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|.
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index a36d5232b5..8d891effae 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -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
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 42b72cc33b..f4ae8b6082 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -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.
diff --git a/test/functional/autocmd/text_deletepost.lua b/test/functional/autocmd/text_deletepost.lua
deleted file mode 100644
index 15c4eafdd4..0000000000
--- a/test/functional/autocmd/text_deletepost.lua
+++ /dev/null
@@ -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)
diff --git a/test/functional/autocmd/text_yankpost.lua b/test/functional/autocmd/text_yankpost.lua
deleted file mode 100644
index 67f3735fa2..0000000000
--- a/test/functional/autocmd/text_yankpost.lua
+++ /dev/null
@@ -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)
diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua
new file mode 100644
index 0000000000..965b19581a
--- /dev/null
+++ b/test/functional/autocmd/textyankpost_spec.lua
@@ -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)