forked from aniani/vim
patch 8.0.1394: cannot intercept a yank command
Problem: Cannot intercept a yank command. Solution: Add the TextYankPost autocommand event. (Philippe Vaucher et al., closes #2333)
This commit is contained in:
@@ -330,6 +330,7 @@ Name triggered by ~
|
||||
|
||||
|TextChanged| after a change was made to the text in Normal mode
|
||||
|TextChangedI| after a change was made to the text in Insert mode
|
||||
|TextYankPost| after text is yanked or deleted
|
||||
|
||||
|ColorScheme| after loading a color scheme
|
||||
|
||||
@@ -956,6 +957,26 @@ TextChangedI After a change was made to the text in the
|
||||
current buffer in Insert mode.
|
||||
Not triggered when the popup menu is visible.
|
||||
Otherwise the same as TextChanged.
|
||||
|TextYankPost|
|
||||
TextYankPost After text has been yanked or deleted in the
|
||||
current buffer. The following values of
|
||||
|v:event| can be used to determine the operation
|
||||
that triggered this autocmd:
|
||||
operator The operation performed.
|
||||
regcontents Text that was stored in the
|
||||
register, as a list of lines,
|
||||
like with: >
|
||||
getreg(r, 1, 1)
|
||||
< regname Name of the |register| or
|
||||
empty string for the unnamed
|
||||
register.
|
||||
regtype Type of the register, see
|
||||
|getregtype()|.
|
||||
Not triggered when |quote_| is used nor when
|
||||
called recursively.
|
||||
It is not allowed to change the buffer text,
|
||||
see |textlock|.
|
||||
|
||||
*User*
|
||||
User Never executed automatically. To be used for
|
||||
autocommands that are only executed with
|
||||
|
@@ -1554,6 +1554,12 @@ 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
|
||||
list by the assert function.
|
||||
|
||||
*v:event* *event-variable*
|
||||
v:event Dictionary containing information about the current
|
||||
|autocommand|. The dictionary is emptied when the |autocommand|
|
||||
finishes, please refer to |dict-identity| for how to get an
|
||||
independent copy of it.
|
||||
|
||||
*v:exception* *exception-variable*
|
||||
v:exception The value of the exception most recently caught and not
|
||||
finished. See also |v:throwpoint| and |throw-variables|.
|
||||
|
36
src/dict.c
36
src/dict.c
@@ -47,6 +47,16 @@ dict_alloc(void)
|
||||
return d;
|
||||
}
|
||||
|
||||
dict_T *
|
||||
dict_alloc_lock(int lock)
|
||||
{
|
||||
dict_T *d = dict_alloc();
|
||||
|
||||
if (d != NULL)
|
||||
d->dv_lock = lock;
|
||||
return d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an empty dict for a return value.
|
||||
* Returns OK or FAIL.
|
||||
@@ -54,13 +64,12 @@ dict_alloc(void)
|
||||
int
|
||||
rettv_dict_alloc(typval_T *rettv)
|
||||
{
|
||||
dict_T *d = dict_alloc();
|
||||
dict_T *d = dict_alloc_lock(0);
|
||||
|
||||
if (d == NULL)
|
||||
return FAIL;
|
||||
|
||||
rettv_dict_set(rettv, d);
|
||||
rettv->v_lock = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -80,7 +89,7 @@ rettv_dict_set(typval_T *rettv, dict_T *d)
|
||||
* Free a Dictionary, including all non-container items it contains.
|
||||
* Ignores the reference count.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
dict_free_contents(dict_T *d)
|
||||
{
|
||||
int todo;
|
||||
@@ -102,6 +111,8 @@ dict_free_contents(dict_T *d)
|
||||
--todo;
|
||||
}
|
||||
}
|
||||
|
||||
/* The hashtab is still locked, it has to be re-initialized anyway */
|
||||
hash_clear(&d->dv_hashtab);
|
||||
}
|
||||
|
||||
@@ -846,4 +857,23 @@ dict_list(typval_T *argvars, typval_T *rettv, int what)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make each item in the dict readonly (not the value of the item).
|
||||
*/
|
||||
void
|
||||
dict_set_items_ro(dict_T *di)
|
||||
{
|
||||
int todo = (int)di->dv_hashtab.ht_used;
|
||||
hashitem_T *hi;
|
||||
|
||||
/* Set readonly */
|
||||
for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
|
||||
{
|
||||
if (HASHITEM_EMPTY(hi))
|
||||
continue;
|
||||
--todo;
|
||||
HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(FEAT_EVAL) */
|
||||
|
28
src/eval.c
28
src/eval.c
@@ -192,6 +192,7 @@ static struct vimvar
|
||||
{VV_NAME("termu7resp", VAR_STRING), VV_RO},
|
||||
{VV_NAME("termstyleresp", VAR_STRING), VV_RO},
|
||||
{VV_NAME("termblinkresp", VAR_STRING), VV_RO},
|
||||
{VV_NAME("event", VAR_DICT), VV_RO},
|
||||
};
|
||||
|
||||
/* shorthand */
|
||||
@@ -319,8 +320,9 @@ eval_init(void)
|
||||
|
||||
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
|
||||
set_vim_var_nr(VV_HLSEARCH, 1L);
|
||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
|
||||
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
|
||||
set_vim_var_list(VV_ERRORS, list_alloc());
|
||||
set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));
|
||||
|
||||
set_vim_var_nr(VV_FALSE, VVAL_FALSE);
|
||||
set_vim_var_nr(VV_TRUE, VVAL_TRUE);
|
||||
@@ -6632,6 +6634,16 @@ get_vim_var_list(int idx)
|
||||
return vimvars[idx].vv_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get Dict v: variable value. Caller must take care of reference count when
|
||||
* needed.
|
||||
*/
|
||||
dict_T *
|
||||
get_vim_var_dict(int idx)
|
||||
{
|
||||
return vimvars[idx].vv_dict;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set v:char to character "c".
|
||||
*/
|
||||
@@ -6706,25 +6718,13 @@ set_vim_var_list(int idx, list_T *val)
|
||||
void
|
||||
set_vim_var_dict(int idx, dict_T *val)
|
||||
{
|
||||
int todo;
|
||||
hashitem_T *hi;
|
||||
|
||||
clear_tv(&vimvars[idx].vv_di.di_tv);
|
||||
vimvars[idx].vv_type = VAR_DICT;
|
||||
vimvars[idx].vv_dict = val;
|
||||
if (val != NULL)
|
||||
{
|
||||
++val->dv_refcount;
|
||||
|
||||
/* Set readonly */
|
||||
todo = (int)val->dv_hashtab.ht_used;
|
||||
for (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;
|
||||
}
|
||||
dict_set_items_ro(val);
|
||||
}
|
||||
}
|
||||
|
||||
|
11
src/fileio.c
11
src/fileio.c
@@ -6478,6 +6478,7 @@ buf_modname(
|
||||
/*
|
||||
* Like fgets(), but if the file line is too long, it is truncated and the
|
||||
* rest of the line is thrown away. Returns TRUE for end-of-file.
|
||||
* If the line is truncated then buf[size - 2] will not be NUL.
|
||||
*/
|
||||
int
|
||||
vim_fgets(char_u *buf, int size, FILE *fp)
|
||||
@@ -7856,6 +7857,7 @@ static struct event_name
|
||||
{"WinEnter", EVENT_WINENTER},
|
||||
{"WinLeave", EVENT_WINLEAVE},
|
||||
{"VimResized", EVENT_VIMRESIZED},
|
||||
{"TextYankPost", EVENT_TEXTYANKPOST},
|
||||
{NULL, (event_T)0}
|
||||
};
|
||||
|
||||
@@ -9399,6 +9401,15 @@ has_funcundefined(void)
|
||||
return (first_autopat[(int)EVENT_FUNCUNDEFINED] != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE when there is a TextYankPost autocommand defined.
|
||||
*/
|
||||
int
|
||||
has_textyankpost(void)
|
||||
{
|
||||
return (first_autopat[(int)EVENT_TEXTYANKPOST] != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute autocommands for "event" and file name "fname".
|
||||
* Return TRUE if some commands were executed.
|
||||
|
67
src/ops.c
67
src/ops.c
@@ -1645,6 +1645,63 @@ shift_delete_registers()
|
||||
y_regs[1].y_array = NULL; /* set register one to empty */
|
||||
}
|
||||
|
||||
static void
|
||||
yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
|
||||
{
|
||||
static int recursive = FALSE;
|
||||
dict_T *v_event;
|
||||
list_T *list;
|
||||
int n;
|
||||
char_u buf[NUMBUFLEN + 2];
|
||||
long reglen = 0;
|
||||
|
||||
if (recursive)
|
||||
return;
|
||||
|
||||
v_event = get_vim_var_dict(VV_EVENT);
|
||||
|
||||
list = list_alloc();
|
||||
for (n = 0; n < reg->y_size; n++)
|
||||
list_append_string(list, reg->y_array[n], -1);
|
||||
list->lv_lock = VAR_FIXED;
|
||||
dict_add_list(v_event, "regcontents", list);
|
||||
|
||||
buf[0] = (char_u)oap->regname;
|
||||
buf[1] = NUL;
|
||||
dict_add_nr_str(v_event, "regname", 0, buf);
|
||||
|
||||
buf[0] = get_op_char(oap->op_type);
|
||||
buf[1] = get_extra_op_char(oap->op_type);
|
||||
buf[2] = NUL;
|
||||
dict_add_nr_str(v_event, "operator", 0, buf);
|
||||
|
||||
buf[0] = NUL;
|
||||
buf[1] = NUL;
|
||||
switch (get_reg_type(oap->regname, ®len))
|
||||
{
|
||||
case MLINE: buf[0] = 'V'; break;
|
||||
case MCHAR: buf[0] = 'v'; break;
|
||||
case MBLOCK:
|
||||
vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V,
|
||||
reglen + 1);
|
||||
break;
|
||||
}
|
||||
dict_add_nr_str(v_event, "regtype", 0, buf);
|
||||
|
||||
/* Lock the dictionary and its keys */
|
||||
dict_set_items_ro(v_event);
|
||||
|
||||
recursive = TRUE;
|
||||
textlock++;
|
||||
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, FALSE, curbuf);
|
||||
textlock--;
|
||||
recursive = FALSE;
|
||||
|
||||
/* Empty the dictionary, v:event is still valid */
|
||||
dict_free_contents(v_event);
|
||||
hash_init(&v_event->dv_hashtab);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a delete operation.
|
||||
*
|
||||
@@ -1798,6 +1855,11 @@ op_delete(oparg_T *oap)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FEAT_AUTOCMD
|
||||
if (did_yank && has_textyankpost())
|
||||
yank_do_autocmd(oap, y_current);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3270,6 +3332,11 @@ op_yank(oparg_T *oap, int deleting, int mess)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_AUTOCMD
|
||||
if (!deleting && has_textyankpost())
|
||||
yank_do_autocmd(oap, y_current);
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
|
||||
fail: /* free the allocated lines */
|
||||
|
@@ -1,7 +1,9 @@
|
||||
/* dict.c */
|
||||
dict_T *dict_alloc(void);
|
||||
dict_T *dict_alloc_lock(int lock);
|
||||
int rettv_dict_alloc(typval_T *rettv);
|
||||
void rettv_dict_set(typval_T *rettv, dict_T *d);
|
||||
void dict_free_contents(dict_T *d);
|
||||
void dict_unref(dict_T *d);
|
||||
int dict_free_nonref(int copyID);
|
||||
void dict_free_items(int copyID);
|
||||
@@ -23,4 +25,5 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
|
||||
dictitem_T *dict_lookup(hashitem_T *hi);
|
||||
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
|
||||
void dict_list(typval_T *argvars, typval_T *rettv, int what);
|
||||
void dict_set_items_ro(dict_T *di);
|
||||
/* vim: set ft=c : */
|
||||
|
@@ -64,6 +64,7 @@ void set_vim_var_nr(int idx, varnumber_T val);
|
||||
varnumber_T get_vim_var_nr(int idx);
|
||||
char_u *get_vim_var_str(int idx);
|
||||
list_T *get_vim_var_list(int idx);
|
||||
dict_T * get_vim_var_dict(int idx);
|
||||
void set_vim_var_char(int c);
|
||||
void set_vcount(long count, long count1, int set_prevcount);
|
||||
void set_vim_var_string(int idx, char_u *val, int len);
|
||||
|
@@ -51,6 +51,7 @@ int has_textchangedI(void);
|
||||
int has_insertcharpre(void);
|
||||
int has_cmdundefined(void);
|
||||
int has_funcundefined(void);
|
||||
int has_textyankpost(void);
|
||||
void block_autocmds(void);
|
||||
void unblock_autocmds(void);
|
||||
int is_autocmd_blocked(void);
|
||||
|
@@ -1124,3 +1124,42 @@ func Test_Filter_noshelltemp()
|
||||
let &shelltemp = shelltemp
|
||||
bwipe!
|
||||
endfunc
|
||||
|
||||
func Test_TextYankPost()
|
||||
enew!
|
||||
call setline(1, ['foo'])
|
||||
|
||||
let g:event = []
|
||||
au TextYankPost * let g:event = copy(v:event)
|
||||
|
||||
call assert_equal({}, v:event)
|
||||
call assert_fails('let v:event = {}', 'E46:')
|
||||
call assert_fails('let v:event.mykey = 0', 'E742:')
|
||||
|
||||
norm "ayiw
|
||||
call assert_equal(
|
||||
\{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
|
||||
\g:event)
|
||||
norm y_
|
||||
call assert_equal(
|
||||
\{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'},
|
||||
\g:event)
|
||||
call feedkeys("\<C-V>y", 'x')
|
||||
call assert_equal(
|
||||
\{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"},
|
||||
\g:event)
|
||||
norm "xciwbar
|
||||
call assert_equal(
|
||||
\{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
|
||||
\g:event)
|
||||
norm "bdiw
|
||||
call assert_equal(
|
||||
\{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
|
||||
\g:event)
|
||||
|
||||
call assert_equal({}, v:event)
|
||||
|
||||
au! TextYankPost
|
||||
unlet g:event
|
||||
bwipe!
|
||||
endfunc
|
||||
|
@@ -771,6 +771,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1394,
|
||||
/**/
|
||||
1393,
|
||||
/**/
|
||||
|
@@ -1339,6 +1339,7 @@ enum auto_event
|
||||
EVENT_TEXTCHANGEDI, /* text was modified in Insert mode*/
|
||||
EVENT_CMDUNDEFINED, /* command undefined */
|
||||
EVENT_OPTIONSET, /* option was set */
|
||||
EVENT_TEXTYANKPOST, /* after some text was yanked */
|
||||
NUM_EVENTS /* MUST be the last one */
|
||||
};
|
||||
|
||||
@@ -1988,7 +1989,8 @@ typedef int sock_T;
|
||||
#define VV_TERMU7RESP 83
|
||||
#define VV_TERMSTYLERESP 84
|
||||
#define VV_TERMBLINKRESP 85
|
||||
#define VV_LEN 86 /* number of v: vars */
|
||||
#define VV_EVENT 86
|
||||
#define VV_LEN 87 /* number of v: vars */
|
||||
|
||||
/* used for v_number in VAR_SPECIAL */
|
||||
#define VVAL_FALSE 0L
|
||||
|
Reference in New Issue
Block a user