aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/autocmd.txt2
-rw-r--r--runtime/doc/eval.txt6
-rw-r--r--src/nvim/edit.c19
-rw-r--r--src/nvim/eval.c40
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/macros.h3
-rw-r--r--test/functional/viml/completion_spec.lua56
7 files changed, 121 insertions, 6 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 9224151cee..af9676272f 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -506,6 +506,8 @@ ColorScheme After loading a color scheme. |:colorscheme|
CompleteDone After Insert mode completion is done. Either
when something was completed or abandoning
completion. |ins-completion|
+ The |v:completed_item| variable contains the
+ completed item.
*CursorHold*
CursorHold When the user doesn't press a key for the time
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index d83d4c0666..81a965d0c6 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1326,6 +1326,12 @@ v:cmdbang Set like v:cmdarg for a file read/write command. When a "!"
can only be used in autocommands. For user commands |<bang>|
can be used.
+ *v:completed_item* *completed_item-variable*
+v:completed_item
+ Dictionary containing the most recent |complete-items| after
+ |CompleteDone|. Empty if the completion failed, or after
+ leaving and re-entering insert mode.
+
*v:count* *count-variable*
v:count The count given for the last Normal mode command. Can be used
to get the count before a mapping. Read-only. Example: >
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index ffbe0e0348..31a5c54c20 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -2734,6 +2734,8 @@ static void ins_compl_clear(void)
xfree(compl_orig_text);
compl_orig_text = NULL;
compl_enter_selects = FALSE;
+ // clear v:completed_item
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
}
/*
@@ -3815,6 +3817,8 @@ static void ins_compl_delete(void)
// TODO: is this sufficient for redrawing? Redrawing everything causes
// flicker, thus we can't do that.
changed_cline_bef_curs();
+ // clear v:completed_item
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
}
/* Insert the new text being completed. */
@@ -3825,6 +3829,21 @@ static void ins_compl_insert(void)
compl_used_match = FALSE;
else
compl_used_match = TRUE;
+
+ // Set completed item.
+ // { word, abbr, menu, kind, info }
+ dict_T *dict = dict_alloc();
+ dict_add_nr_str(dict, "word", 0L,
+ EMPTY_IF_NULL(compl_shown_match->cp_str));
+ dict_add_nr_str(dict, "abbr", 0L,
+ EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR]));
+ dict_add_nr_str(dict, "menu", 0L,
+ EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU]));
+ dict_add_nr_str(dict, "kind", 0L,
+ EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND]));
+ dict_add_nr_str(dict, "info", 0L,
+ EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO]));
+ set_vim_var_dict(VV_COMPLETED_ITEM, dict);
}
/*
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index e7cb830e67..6bd2b23c73 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -426,7 +426,8 @@ static struct vimvar {
{VV_NAME("oldfiles", VAR_LIST), 0},
{VV_NAME("windowid", VAR_NUMBER), VV_RO},
{VV_NAME("progpath", VAR_STRING), VV_RO},
- {VV_NAME("command_output", VAR_STRING), 0}
+ {VV_NAME("command_output", VAR_STRING), 0},
+ {VV_NAME("completed_item", VAR_DICT), VV_RO},
};
/* shorthand */
@@ -435,6 +436,7 @@ static struct vimvar {
#define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string
#define vv_list vv_di.di_tv.vval.v_list
+#define vv_dict vv_di.di_tv.vval.v_dict
#define vv_tv vv_di.di_tv
static dictitem_T vimvars_var; /* variable used for v: */
@@ -16281,7 +16283,7 @@ void set_vim_var_nr(int idx, long val)
/*
* Get number v: variable value.
*/
-long get_vim_var_nr(int idx)
+long get_vim_var_nr(int idx) FUNC_ATTR_PURE
{
return vimvars[idx].vv_nr;
}
@@ -16289,7 +16291,7 @@ long get_vim_var_nr(int idx)
/*
* Get string v: variable value. Uses a static buffer, can only be used once.
*/
-char_u *get_vim_var_str(int idx) FUNC_ATTR_NONNULL_RET
+char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
return get_tv_string(&vimvars[idx].vv_tv);
}
@@ -16298,11 +16300,18 @@ char_u *get_vim_var_str(int idx) FUNC_ATTR_NONNULL_RET
* Get List v: variable value. Caller must take care of reference count when
* needed.
*/
-list_T *get_vim_var_list(int idx)
+list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
return vimvars[idx].vv_list;
}
+/// Get Dictionary v: variable value. Caller must take care of reference count
+/// when needed.
+dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
+{
+ return vimvars[idx].vv_dict;
+}
+
/*
* Set v:char to character "c".
*/
@@ -16334,8 +16343,7 @@ void set_vcount(long count, long count1, int set_prevcount)
/*
* Set string v: variable to a copy of "val".
*/
-void
-set_vim_var_string (
+void set_vim_var_string (
int idx,
char_u *val,
int len /* length of "val" to use or -1 (whole string) */
@@ -16365,6 +16373,26 @@ void set_vim_var_list(int idx, list_T *val)
++val->lv_refcount;
}
+/// Set Dictionary v: variable to "val".
+void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL
+{
+ dict_unref(vimvars[idx].vv_dict);
+
+ // Set readonly
+ int todo = (int)val->dv_hashtab.ht_used;
+ 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;
+ }
+
+ vimvars[idx].vv_dict = val;
+ ++val->dv_refcount;
+}
+
/*
* Set v:register if needed.
*/
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 61a65df7c6..75e3b247f3 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -64,6 +64,7 @@ enum {
VV_WINDOWID,
VV_PROGPATH,
VV_COMMAND_OUTPUT,
+ VV_COMPLETED_ITEM,
VV_LEN, /* number of v: vars */
};
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 51542bd3a7..bbbce7ab58 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -63,6 +63,9 @@
# define ASCII_ISALPHA(c) (ASCII_ISUPPER(c) || ASCII_ISLOWER(c))
# define ASCII_ISALNUM(c) (ASCII_ISALPHA(c) || ascii_isdigit(c))
+/* Returns empty string if it is NULL. */
+#define EMPTY_IF_NULL(x) ((x) ? (x) : (char_u *)"")
+
/* macro version of chartab().
* Only works with values 0-255!
* Doesn't work for UTF-8 mode with chars >= 0x80. */
diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua
new file mode 100644
index 0000000000..df4018e707
--- /dev/null
+++ b/test/functional/viml/completion_spec.lua
@@ -0,0 +1,56 @@
+local helpers = require('test.functional.helpers')
+local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute
+local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq
+local execute, source = helpers.execute, helpers.source
+
+describe("completion", function()
+ before_each(function()
+ clear()
+ end)
+
+ describe("v:completed_item", function()
+ it('returns expected dict in normal completion', function()
+ feed('ifoo<ESC>o<C-x><C-n><ESC>')
+ eq('foo', eval('getline(2)'))
+ eq({word = 'foo', abbr = '', menu = '', info = '', kind = ''},
+ eval('v:completed_item'))
+ end)
+ it('is readonly', function()
+ feed('ifoo<ESC>o<C-x><C-n><ESC>')
+
+ execute('let v:completed_item.word = "bar"')
+ neq(nil, string.find(eval('v:errmsg'), '^E46: '))
+ execute('let v:errmsg = ""')
+
+ execute('let v:completed_item.abbr = "bar"')
+ neq(nil, string.find(eval('v:errmsg'), '^E46: '))
+ execute('let v:errmsg = ""')
+
+ execute('let v:completed_item.menu = "bar"')
+ neq(nil, string.find(eval('v:errmsg'), '^E46: '))
+ execute('let v:errmsg = ""')
+
+ execute('let v:completed_item.info = "bar"')
+ neq(nil, string.find(eval('v:errmsg'), '^E46: '))
+ execute('let v:errmsg = ""')
+
+ execute('let v:completed_item.kind = "bar"')
+ neq(nil, string.find(eval('v:errmsg'), '^E46: '))
+ execute('let v:errmsg = ""')
+ end)
+ it('returns expected dict in omni completion', function()
+ source([[
+ function! TestOmni(findstart, base) abort
+ return a:findstart ? 0 : [{'word': 'foo', 'abbr': 'bar',
+ \ 'menu': 'baz', 'info': 'foobar', 'kind': 'foobaz'}]
+ endfunction
+ setlocal omnifunc=TestOmni
+ ]])
+ feed('i<C-x><C-o><ESC>')
+ eq('foo', eval('getline(1)'))
+ eq({word = 'foo', abbr = 'bar', menu = 'baz',
+ info = 'foobar', kind = 'foobaz'},
+ eval('v:completed_item'))
+ end)
+ end)
+end)