patch 9.1.0754: fixed order of items in insert-mode completion menu
Problem: fixed order of items in insert-mode completion menu
Solution: Introduce the 'completeitemalign' option with default
value "abbr,kind,menu" (glepnir).
Adding an new option `completeitemalign` abbr is `cia` to custom
the complete-item order in popupmenu.
closes: #14006
closes: #15760
Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/insexpand.c b/src/insexpand.c
index 63bf070..a774a33 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -88,15 +88,6 @@
#endif
/*
- * Array indexes used for cp_text[].
- */
-#define CPT_ABBR 0 // "abbr"
-#define CPT_MENU 1 // "menu"
-#define CPT_KIND 2 // "kind"
-#define CPT_INFO 3 // "info"
-#define CPT_COUNT 4 // Number of entries
-
-/*
* Structure used to store one match for insert completion.
*/
typedef struct compl_S compl_T;
@@ -1338,8 +1329,7 @@
}
if (compl->cp_text[CPT_ABBR] != NULL)
- compl_match_array[i].pum_text =
- compl->cp_text[CPT_ABBR];
+ compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR];
else
compl_match_array[i].pum_text = compl->cp_str;
compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
diff --git a/src/option.h b/src/option.h
index fba2672..ef39031 100644
--- a/src/option.h
+++ b/src/option.h
@@ -513,6 +513,8 @@
EXTERN int p_confirm; // 'confirm'
#endif
EXTERN int p_cp; // 'compatible'
+EXTERN char_u *p_cia; // 'completeitemalign'
+EXTERN unsigned cia_flags; // order flags of 'completeitemalign'
EXTERN char_u *p_cot; // 'completeopt'
EXTERN unsigned cot_flags; // flags from 'completeopt'
// Keep in sync with p_cot_values in optionstr.c
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 8982ac6..4a5bd63 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -653,6 +653,10 @@
{(char_u *)0L, (char_u *)0L}
#endif
SCTX_INIT},
+ {"completeitemalign", "cia", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
+ (char_u *)&p_cia, PV_NONE, did_set_completeitemalign, NULL,
+ {(char_u *)"abbr,kind,menu", (char_u *)0L}
+ SCTX_INIT},
{"completeopt", "cot", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
(char_u *)&p_cot, PV_COT, did_set_completeopt, expand_set_completeopt,
{(char_u *)"menu,preview", (char_u *)0L}
diff --git a/src/optionstr.c b/src/optionstr.c
index b6249a2..bf7135a 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -1635,6 +1635,58 @@
matches);
}
+/*
+ * The 'completeitemalign' option is changed.
+ */
+ char *
+did_set_completeitemalign(optset_T *args UNUSED)
+{
+ char_u *p = p_cia;
+ unsigned new_cia_flags = 0;
+ int seen[3] = { FALSE, FALSE, FALSE };
+ int count = 0;
+ char_u buf[10];
+
+ while (*p)
+ {
+ copy_option_part(&p, buf, sizeof(buf), ",");
+ if (count >= 3)
+ return e_invalid_argument;
+
+ if (STRCMP(buf, "abbr") == 0)
+ {
+ if (seen[CPT_ABBR])
+ return e_invalid_argument;
+ new_cia_flags = new_cia_flags * 10 + CPT_ABBR;
+ seen[CPT_ABBR] = TRUE;
+ count++;
+ }
+ else if (STRCMP(buf, "kind") == 0)
+ {
+ if (seen[CPT_KIND])
+ return e_invalid_argument;
+ new_cia_flags = new_cia_flags * 10 + CPT_KIND;
+ seen[CPT_KIND] = TRUE;
+ count++;
+ }
+ else if (STRCMP(buf, "menu") == 0)
+ {
+ if (seen[CPT_MENU])
+ return e_invalid_argument;
+ new_cia_flags = new_cia_flags * 10 + CPT_MENU;
+ seen[CPT_MENU] = TRUE;
+ count++;
+ }
+ else
+ return e_invalid_argument;
+ }
+ if (new_cia_flags == 0 || count != 3)
+ return e_invalid_argument;
+
+ cia_flags = new_cia_flags;
+ return NULL;
+}
+
#if (defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)) || defined(PROTO)
/*
* The 'completepopup' option is changed.
diff --git a/src/popupmenu.c b/src/popupmenu.c
index da8241b..010a987 100644
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -536,6 +536,28 @@
}
}
+
+ static inline void
+pum_align_order(int *order)
+{
+ int is_default = cia_flags == 0;
+ order[0] = is_default ? CPT_ABBR : cia_flags / 100;
+ order[1] = is_default ? CPT_KIND : (cia_flags / 10) % 10;
+ order[2] = is_default ? CPT_MENU : cia_flags % 10;
+}
+
+ static inline char_u *
+pum_get_item(int index, int type)
+{
+ switch(type)
+ {
+ case CPT_ABBR: return pum_array[index].pum_text;
+ case CPT_KIND: return pum_array[index].pum_kind;
+ case CPT_MENU: return pum_array[index].pum_extra;
+ }
+ return NULL;
+}
+
/*
* Redraw the popup menu, using "pum_first" and "pum_selected".
*/
@@ -549,19 +571,25 @@
hlf_T *hlfs; // array used for highlights
hlf_T hlf;
int attr;
- int i;
+ int i, j;
int idx;
char_u *s;
char_u *p = NULL;
- int totwidth, width, w;
+ int totwidth, width, w; // total-width item-width char-width
int thumb_pos = 0;
int thumb_height = 1;
- int round;
+ int item_type;
+ int order[3];
+ int next_isempty = FALSE;
int n;
+ int items_width_array[3] = { pum_base_width, pum_kind_width,
+ pum_extra_width };
+ int basic_width; // first item width
+ int last_isabbr = FALSE;
hlf_T hlfsNorm[3];
hlf_T hlfsSel[3];
- // "word"
+ // "word"/"abbr"
hlfsNorm[0] = HLF_PNI;
hlfsSel[0] = HLF_PSI;
// "kind"
@@ -621,28 +649,24 @@
screen_putchar(' ', row, pum_col - 1, attr);
// Display each entry, use two spaces for a Tab.
- // Do this 3 times:
- // 0 - main text
- // 1 - kind
- // 2 - extra info
+ // Do this 3 times and order from p_cia
col = pum_col;
totwidth = 0;
- for (round = 0; round < 3; ++round)
+ pum_align_order(order);
+ basic_width = items_width_array[order[0]];
+ last_isabbr = order[2] == CPT_ABBR;
+ for (j = 0; j < 3; ++j)
{
- hlf = hlfs[round];
+ item_type = order[j];
+ hlf = hlfs[item_type];
attr = highlight_attr[hlf];
if (pum_array[idx].pum_user_hlattr > 0)
attr = hl_combine_attr(attr, pum_array[idx].pum_user_hlattr);
- if (round == 1 && pum_array[idx].pum_user_kind_hlattr > 0)
+ if (item_type == CPT_KIND && pum_array[idx].pum_user_kind_hlattr > 0)
attr = hl_combine_attr(attr, pum_array[idx].pum_user_kind_hlattr);
width = 0;
s = NULL;
- switch (round)
- {
- case 0: p = pum_array[idx].pum_text; break;
- case 1: p = pum_array[idx].pum_kind; break;
- case 2: p = pum_array[idx].pum_extra; break;
- }
+ p = pum_get_item(idx, item_type);
if (p != NULL)
for ( ; ; MB_PTR_ADV(p))
{
@@ -774,33 +798,35 @@
width += w;
}
- if (round > 0)
- n = pum_kind_width + 1;
+ if (j > 0)
+ n = items_width_array[order[1]] + (last_isabbr ? 0 : 1);
else
- n = 1;
+ n = order[j] == CPT_ABBR ? 1 : 0;
+
+ if (j + 1 < 3)
+ next_isempty = pum_get_item(idx, order[j + 1]) == NULL;
// Stop when there is nothing more to display.
- if (round == 2
- || (round == 1 && pum_array[idx].pum_extra == NULL)
- || (round == 0 && pum_array[idx].pum_kind == NULL
- && pum_array[idx].pum_extra == NULL)
+ if (j == 2
+ || (next_isempty && (j == 1 || (j == 0
+ && pum_get_item(idx, order[j + 2]) == NULL)))
|| pum_base_width + n >= pum_width)
break;
#ifdef FEAT_RIGHTLEFT
if (pum_rl)
{
- screen_fill(row, row + 1, pum_col - pum_base_width - n + 1,
+ screen_fill(row, row + 1, pum_col - basic_width - n + 1,
col + 1, ' ', ' ', attr);
- col = pum_col - pum_base_width - n;
+ col = pum_col - basic_width - n;
}
else
#endif
{
- screen_fill(row, row + 1, col, pum_col + pum_base_width + n,
+ screen_fill(row, row + 1, col, pum_col + basic_width + n,
' ', ' ', attr);
- col = pum_col + pum_base_width + n;
+ col = pum_col + basic_width + n;
}
- totwidth = pum_base_width + n;
+ totwidth = basic_width + n;
}
#ifdef FEAT_RIGHTLEFT
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 39c40f3..c034c11 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -42,6 +42,7 @@
int expand_set_complete(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_completeopt(optset_T *args);
int expand_set_completeopt(optexpand_T *args, int *numMatches, char_u ***matches);
+char *did_set_completeitemalign(optset_T *args);
char *did_set_completepopup(optset_T *args);
char *did_set_completeslash(optset_T *args);
int expand_set_completeslash(optexpand_T *args, int *numMatches, char_u ***matches);
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_01.dump b/src/testdir/dumps/Test_pum_completeitemalign_01.dump
new file mode 100644
index 0000000..2a2f6f0
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_01.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|f+0#0000001#e0e0e08|o@1| @1|S| |m|e|n|u| @3| +0#4040ff13#ffffff0@59
+|b+0#0000001#ffd7ff255|a|r| @1|T| |m|e|n|u| @3| +0#4040ff13#ffffff0@59
+|你*0#0000001#ffd7ff255|好| +&|C| |中*&|文| +&@3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_02.dump b/src/testdir/dumps/Test_pum_completeitemalign_02.dump
new file mode 100644
index 0000000..f006827
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_02.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|f+0#0000001#e0e0e08|o@1| @1|m|e|n|u| |S| @3| +0#4040ff13#ffffff0@59
+|b+0#0000001#ffd7ff255|a|r| @1|m|e|n|u| |T| @3| +0#4040ff13#ffffff0@59
+|你*0#0000001#ffd7ff255|好| +&|中*&|文| +&|C| @3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_03.dump b/src/testdir/dumps/Test_pum_completeitemalign_03.dump
new file mode 100644
index 0000000..a16954f
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_03.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|S+0#0000001#e0e0e08| |f|o@1| @1|m|e|n|u| @3| +0#4040ff13#ffffff0@59
+|T+0#0000001#ffd7ff255| |b|a|r| @1|m|e|n|u| @3| +0#4040ff13#ffffff0@59
+|C+0#0000001#ffd7ff255| |你*&|好| +&|中*&|文| +&@3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_04.dump b/src/testdir/dumps/Test_pum_completeitemalign_04.dump
new file mode 100644
index 0000000..8c9123af
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_04.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|S+0#0000001#e0e0e08| |m|e|n|u| |f|o@1| @4| +0#4040ff13#ffffff0@59
+|T+0#0000001#ffd7ff255| |m|e|n|u| |b|a|r| @4| +0#4040ff13#ffffff0@59
+|C+0#0000001#ffd7ff255| |中*&|文| +&|你*&|好| +&@3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_05.dump b/src/testdir/dumps/Test_pum_completeitemalign_05.dump
new file mode 100644
index 0000000..7209c85
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_05.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|m+0#0000001#e0e0e08|e|n|u| |f|o@1| @1|S| @3| +0#4040ff13#ffffff0@59
+|m+0#0000001#ffd7ff255|e|n|u| |b|a|r| @1|T| @3| +0#4040ff13#ffffff0@59
+|中*0#0000001#ffd7ff255|文| +&|你*&|好| +&|C| @3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_completeitemalign_06.dump b/src/testdir/dumps/Test_pum_completeitemalign_06.dump
new file mode 100644
index 0000000..03210ac
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_completeitemalign_06.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|m+0#0000001#e0e0e08|e|n|u| |S| |f|o@1| @4| +0#4040ff13#ffffff0@59
+|m+0#0000001#ffd7ff255|e|n|u| |T| |b|a|r| @4| +0#4040ff13#ffffff0@59
+|中*0#0000001#ffd7ff255|文| +&|C| |你*&|好| +&@3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index c601158..936a0fa 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -80,6 +80,7 @@
\ 'complete': [['', 'w,b'], ['xxx']],
\ 'concealcursor': [['', 'n', 'nvic'], ['xxx']],
\ 'completeopt': [['', 'menu', 'menu,longest'], ['xxx', 'menu,,,longest,']],
+ \ 'completeitemalign': [['abbr,kind,menu'], ['xxx','abbr,menu','abbr,menu,kind,abbr', 'abbr', 'abbr1234,kind', '']],
\ 'completepopup': [['', 'height:13', 'highlight:That', 'width:10,height:234,highlight:Mine'], ['height:yes', 'width:no', 'xxx', 'xxx:99', 'border:maybe', 'border:1']],
\ 'completeslash': [['', 'slash', 'backslash'], ['xxx']],
\ 'cryptmethod': [['', 'zip'], ['xxx']],
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index caec8ff..1758f74 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1581,4 +1581,64 @@
call StopVimInTerminal(buf)
endfunc
+func Test_pum_completeitemalign()
+ CheckScreendump
+ let lines =<< trim END
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return {
+ \ 'words': [
+ \ { 'word': 'foo', 'kind': 'S', 'menu': 'menu' },
+ \ { 'word': 'bar', 'kind': 'T', 'menu': 'menu' },
+ \ { 'word': '你好', 'kind': 'C', 'menu': '中文' },
+ \]}
+ endfunc
+ set omnifunc=Omni_test
+ command! -nargs=0 T1 set cia=abbr,kind,menu
+ command! -nargs=0 T2 set cia=abbr,menu,kind
+ command! -nargs=0 T3 set cia=kind,abbr,menu
+ command! -nargs=0 T4 set cia=kind,menu,abbr
+ command! -nargs=0 T5 set cia=menu,abbr,kind
+ command! -nargs=0 T6 set cia=menu,kind,abbr
+ command! -nargs=0 T7 set cia&
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call TermWait(buf)
+
+ " T1 is default
+ call term_sendkeys(buf, ":T1\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_01', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " T2
+ call term_sendkeys(buf, ":T2\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_02', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " T3
+ call term_sendkeys(buf, ":T3\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_03', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " T4
+ call term_sendkeys(buf, ":T4\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_04', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " T5
+ call term_sendkeys(buf, ":T5\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_05', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " T6
+ call term_sendkeys(buf, ":T6\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_completeitemalign_06', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>:T7\<CR>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index f5d85cb..8cc38cc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 754,
+/**/
753,
/**/
752,
diff --git a/src/vim.h b/src/vim.h
index 9c1434c..ebca6ae 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2373,6 +2373,17 @@
} funcerror_T;
/*
+ * Array indexes used for cp_text[].
+ */
+typedef enum {
+ CPT_ABBR, // "abbr"
+ CPT_KIND, // "kind"
+ CPT_MENU, // "menu"
+ CPT_INFO, // "info"
+ CPT_COUNT, // Number of entries
+} cpitem_T;
+
+/*
* Type for the callback function that is invoked after an option value is
* changed to validate and apply the new value.
*