mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #4304 from bfredl/yank
Add v:event variable and TextYankPost autocommand
This commit is contained in:
commit
1dd986562f
2
clint.py
2
clint.py
@ -2552,7 +2552,7 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
|||||||
|
|
||||||
# If should always have a brace
|
# If should always have a brace
|
||||||
for blockstart in ('if', 'while', 'for'):
|
for blockstart in ('if', 'while', 'for'):
|
||||||
if Match(r'\s*{0}[^{{]*$'.format(blockstart), line):
|
if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line):
|
||||||
pos = line.find(blockstart)
|
pos = line.find(blockstart)
|
||||||
pos = line.find('(', pos)
|
pos = line.find('(', pos)
|
||||||
if pos > 0:
|
if pos > 0:
|
||||||
|
@ -308,6 +308,8 @@ Name triggered by ~
|
|||||||
|InsertCharPre| when a character was typed in Insert mode, before
|
|InsertCharPre| when a character was typed in Insert mode, before
|
||||||
inserting it
|
inserting it
|
||||||
|
|
||||||
|
|TextYankPost| when some text is yanked or deleted
|
||||||
|
|
||||||
|TextChanged| after a change was made to the text in Normal mode
|
|TextChanged| after a change was made to the text in Normal mode
|
||||||
|TextChangedI| after a change was made to the text in Insert mode
|
|TextChangedI| after a change was made to the text in Insert mode
|
||||||
|
|
||||||
@ -722,6 +724,18 @@ InsertCharPre When a character is typed in Insert mode,
|
|||||||
It is not allowed to change the text |textlock|.
|
It is not allowed to change the text |textlock|.
|
||||||
The event is not triggered when 'paste' is
|
The event is not triggered when 'paste' is
|
||||||
set.
|
set.
|
||||||
|
*TextYankPost*
|
||||||
|
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*
|
||||||
InsertEnter Just before starting Insert mode. Also for
|
InsertEnter Just before starting Insert mode. Also for
|
||||||
Replace mode and Virtual Replace mode. The
|
Replace mode and Virtual Replace mode. The
|
||||||
|
@ -1386,6 +1386,22 @@ v:errors Errors found by assert functions, such as |assert_true()|.
|
|||||||
< If v:errors is set to anything but a list it is made an empty
|
< If v:errors is set to anything but a list it is made an empty
|
||||||
list by the assert function.
|
list by the assert function.
|
||||||
|
|
||||||
|
*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|. 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* *exception-variable*
|
||||||
v:exception The value of the exception most recently caught and not
|
v:exception The value of the exception most recently caught and not
|
||||||
finished. See also |v:throwpoint| and |throw-variables|.
|
finished. See also |v:throwpoint| and |throw-variables|.
|
||||||
|
@ -83,6 +83,7 @@ return {
|
|||||||
'TermResponse', -- after setting "v:termresponse"
|
'TermResponse', -- after setting "v:termresponse"
|
||||||
'TextChanged', -- text was modified
|
'TextChanged', -- text was modified
|
||||||
'TextChangedI', -- text was modified in Insert mode
|
'TextChangedI', -- text was modified in Insert mode
|
||||||
|
'TextYankPost', -- after a yank or delete was done (y, d, c)
|
||||||
'User', -- user defined autocommand
|
'User', -- user defined autocommand
|
||||||
'VimEnter', -- after starting Vim
|
'VimEnter', -- after starting Vim
|
||||||
'VimLeave', -- before exiting Vim
|
'VimLeave', -- before exiting Vim
|
||||||
|
@ -378,6 +378,7 @@ static struct vimvar {
|
|||||||
{ VV_NAME("option_type", VAR_STRING), VV_RO },
|
{ VV_NAME("option_type", VAR_STRING), VV_RO },
|
||||||
{ VV_NAME("errors", VAR_LIST), 0 },
|
{ VV_NAME("errors", VAR_LIST), 0 },
|
||||||
{ VV_NAME("msgpack_types", VAR_DICT), VV_RO },
|
{ VV_NAME("msgpack_types", VAR_DICT), VV_RO },
|
||||||
|
{ VV_NAME("event", VAR_DICT), VV_RO },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* shorthand */
|
/* shorthand */
|
||||||
@ -545,6 +546,10 @@ void eval_init(void)
|
|||||||
|
|
||||||
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
|
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
|
||||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
|
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
|
||||||
|
|
||||||
|
dict_T *v_event = dict_alloc();
|
||||||
|
v_event->dv_lock = VAR_FIXED;
|
||||||
|
set_vim_var_dict(VV_EVENT, v_event);
|
||||||
set_vim_var_list(VV_ERRORS, list_alloc());
|
set_vim_var_list(VV_ERRORS, list_alloc());
|
||||||
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
|
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
|
||||||
set_vim_var_nr(VV_HLSEARCH, 1L);
|
set_vim_var_nr(VV_HLSEARCH, 1L);
|
||||||
@ -6017,6 +6022,27 @@ static void rettv_dict_alloc(typval_T *rettv)
|
|||||||
++d->dv_refcount;
|
++d->dv_refcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
|
||||||
|
///
|
||||||
|
/// @param d The Dictionary to clear
|
||||||
|
void dict_clear(dict_T *d)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
hash_lock(&d->dv_hashtab);
|
||||||
|
assert(d->dv_hashtab.ht_locked > 0);
|
||||||
|
|
||||||
|
size_t todo = d->dv_hashtab.ht_used;
|
||||||
|
for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
|
||||||
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
|
dictitem_free(HI2DI(hi));
|
||||||
|
hash_remove(&d->dv_hashtab, hi);
|
||||||
|
todo--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_unlock(&d->dv_hashtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unreference a Dictionary: decrement the reference count and free it when it
|
* Unreference a Dictionary: decrement the reference count and free it when it
|
||||||
@ -6258,6 +6284,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set all existing keys in "dict" as read-only.
|
||||||
|
///
|
||||||
|
/// This does not protect against adding new keys to the Dictionary.
|
||||||
|
///
|
||||||
|
/// @param dict The dict whose keys should be frozen
|
||||||
|
void dict_set_keys_readonly(dict_T *dict)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
size_t todo = dict->dv_hashtab.ht_used;
|
||||||
|
for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) {
|
||||||
|
if (HASHITEM_EMPTY(hi)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
todo--;
|
||||||
|
HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the number of items in a Dictionary.
|
* Get the number of items in a Dictionary.
|
||||||
*/
|
*/
|
||||||
@ -10579,8 +10623,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
|
|||||||
{
|
{
|
||||||
char_u *strregname;
|
char_u *strregname;
|
||||||
int regname;
|
int regname;
|
||||||
char_u buf[NUMBUFLEN + 2];
|
|
||||||
long reglen = 0;
|
|
||||||
|
|
||||||
if (argvars[0].v_type != VAR_UNKNOWN) {
|
if (argvars[0].v_type != VAR_UNKNOWN) {
|
||||||
strregname = get_tv_string_chk(&argvars[0]);
|
strregname = get_tv_string_chk(&argvars[0]);
|
||||||
@ -10597,18 +10639,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
|
|||||||
if (regname == 0)
|
if (regname == 0)
|
||||||
regname = '"';
|
regname = '"';
|
||||||
|
|
||||||
buf[0] = NUL;
|
colnr_T reglen = 0;
|
||||||
buf[1] = NUL;
|
char buf[NUMBUFLEN + 2];
|
||||||
switch (get_reg_type(regname, ®len)) {
|
char_u reg_type = get_reg_type(regname, ®len);
|
||||||
case MLINE: buf[0] = 'V'; break;
|
format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
|
||||||
case MCHAR: buf[0] = 'v'; break;
|
|
||||||
case MBLOCK:
|
|
||||||
buf[0] = Ctrl_V;
|
|
||||||
sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
rettv->vval.v_string = vim_strsave(buf);
|
rettv->vval.v_string = (char_u *)xstrdup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -18162,14 +18199,7 @@ void set_vim_var_dict(int idx, dict_T *val)
|
|||||||
if (val != NULL) {
|
if (val != NULL) {
|
||||||
++val->dv_refcount;
|
++val->dv_refcount;
|
||||||
// Set readonly
|
// Set readonly
|
||||||
size_t todo = val->dv_hashtab.ht_used;
|
dict_set_keys_readonly(val);
|
||||||
for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) {
|
|
||||||
if (HASHITEM_EMPTY(hi)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
--todo;
|
|
||||||
HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,8 @@ enum {
|
|||||||
VV_OPTION_TYPE,
|
VV_OPTION_TYPE,
|
||||||
VV_ERRORS,
|
VV_ERRORS,
|
||||||
VV_MSGPACK_TYPES,
|
VV_MSGPACK_TYPES,
|
||||||
VV_LEN, /* number of v: vars */
|
VV_EVENT,
|
||||||
|
VV_LEN, // number of v: vars
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Maximum number of function arguments
|
/// Maximum number of function arguments
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "nvim/ex_cmds.h"
|
#include "nvim/ex_cmds.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
#include "nvim/ex_getln.h"
|
#include "nvim/ex_getln.h"
|
||||||
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/fold.h"
|
#include "nvim/fold.h"
|
||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
#include "nvim/indent.h"
|
#include "nvim/indent.h"
|
||||||
@ -1409,8 +1410,9 @@ int op_delete(oparg_T *oap)
|
|||||||
op_yank_reg(oap, false, reg, false);
|
op_yank_reg(oap, false, reg, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(oap->regname == 0) {
|
if (oap->regname == 0) {
|
||||||
set_clipboard(0, reg);
|
set_clipboard(0, reg);
|
||||||
|
yank_do_autocmd(oap, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2309,6 +2311,8 @@ bool op_yank(oparg_T *oap, bool message)
|
|||||||
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
|
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
|
||||||
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
|
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
|
||||||
set_clipboard(oap->regname, reg);
|
set_clipboard(oap->regname, reg);
|
||||||
|
yank_do_autocmd(oap, reg);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2524,6 +2528,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx)
|
|||||||
*pnew = NUL;
|
*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];
|
||||||
|
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
|
||||||
|
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.
|
* Put contents of register "regname" into the text.
|
||||||
@ -4631,7 +4687,7 @@ theend:
|
|||||||
* Used for getregtype()
|
* Used for getregtype()
|
||||||
* Returns MAUTO for error.
|
* Returns MAUTO for error.
|
||||||
*/
|
*/
|
||||||
char_u get_reg_type(int regname, long *reglen)
|
char_u get_reg_type(int regname, colnr_T *reg_width)
|
||||||
{
|
{
|
||||||
switch (regname) {
|
switch (regname) {
|
||||||
case '%': /* file name */
|
case '%': /* file name */
|
||||||
@ -4654,13 +4710,45 @@ char_u get_reg_type(int regname, long *reglen)
|
|||||||
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
|
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
|
||||||
|
|
||||||
if (reg->y_array != NULL) {
|
if (reg->y_array != NULL) {
|
||||||
if (reglen != NULL && reg->y_type == MBLOCK)
|
if (reg_width != NULL && reg->y_type == MBLOCK) {
|
||||||
*reglen = reg->y_width;
|
*reg_width = reg->y_width;
|
||||||
|
}
|
||||||
return reg->y_type;
|
return reg->y_type;
|
||||||
}
|
}
|
||||||
return MAUTO;
|
return MAUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format the register type as a string.
|
||||||
|
///
|
||||||
|
/// @param reg_type The register type.
|
||||||
|
/// @param reg_width The width, only used if "reg_type" is MBLOCK.
|
||||||
|
/// @param[out] buf Buffer to store formatted string. The allocated size should
|
||||||
|
/// be at least NUMBUFLEN+2 to always fit the value.
|
||||||
|
/// @param buf_len The allocated size of the buffer.
|
||||||
|
void format_reg_type(char_u reg_type, colnr_T reg_width,
|
||||||
|
char* buf, size_t buf_len)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
assert(buf_len > 1);
|
||||||
|
switch (reg_type) {
|
||||||
|
case MLINE:
|
||||||
|
buf[0] = 'V';
|
||||||
|
buf[1] = NUL;
|
||||||
|
break;
|
||||||
|
case MCHAR:
|
||||||
|
buf[0] = 'v';
|
||||||
|
buf[1] = NUL;
|
||||||
|
break;
|
||||||
|
case MBLOCK:
|
||||||
|
snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1);
|
||||||
|
break;
|
||||||
|
case MAUTO:
|
||||||
|
buf[0] = NUL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// When `flags` has `kGRegList` return a list with text `s`.
|
/// When `flags` has `kGRegList` return a list with text `s`.
|
||||||
/// Otherwise just return `s`.
|
/// Otherwise just return `s`.
|
||||||
///
|
///
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
#define NVIM_POS_H
|
#define NVIM_POS_H
|
||||||
|
|
||||||
typedef long linenr_T; // line number type
|
typedef long linenr_T; // line number type
|
||||||
typedef int colnr_T; // column number type
|
|
||||||
|
/// Column number type
|
||||||
|
typedef int colnr_T;
|
||||||
|
/// Format used to print values which have colnr_T type
|
||||||
|
#define PRIdCOLNR "d"
|
||||||
|
|
||||||
#define MAXLNUM 0x7fffffff // maximum (invalid) line number
|
#define MAXLNUM 0x7fffffff // maximum (invalid) line number
|
||||||
#define MAXCOL 0x7fffffff // maximum column number, 31 bits
|
#define MAXCOL 0x7fffffff // maximum column number, 31 bits
|
||||||
|
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)
|
15
test/functional/eval/vvar_event_spec.lua
Normal file
15
test/functional/eval/vvar_event_spec.lua
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
local helpers = require('test.functional.helpers')
|
||||||
|
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
|
||||||
|
local command = helpers.command
|
||||||
|
describe('v:event', function()
|
||||||
|
before_each(clear)
|
||||||
|
it('is empty before any autocommand', function()
|
||||||
|
eq({}, eval('v:event'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('is immutable', function()
|
||||||
|
eq(false, pcall(command, 'let v:event = {}'))
|
||||||
|
eq(false, pcall(command, 'let v:event.mykey = {}'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user