blob: c0191c407f818a403ef0998f2f996bcfd57a7e45 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar4b779472005-10-04 09:12:31 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar76b92b22006-03-24 22:46:53 +000011 * popupmnu.c: Popup menu (PUM)
Bram Moolenaar4b779472005-10-04 09:12:31 +000012 */
13#include "vim.h"
14
15#if defined(FEAT_INS_EXPAND) || defined(PROTO)
16
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000017static pumitem_T *pum_array = NULL; /* items of displayed pum */
Bram Moolenaar4b779472005-10-04 09:12:31 +000018static int pum_size; /* nr of items in "pum_array" */
19static int pum_selected; /* index of selected item or -1 */
20static int pum_first = 0; /* index of top item */
21
22static int pum_height; /* nr of displayed pum items */
23static int pum_width; /* width of displayed pum items */
Bram Moolenaar8b6144b2006-02-08 09:20:24 +000024static int pum_base_width; /* width of pum items base */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000025static int pum_kind_width; /* width of pum items kind column */
Bram Moolenaar51b0f372017-11-18 18:52:04 +010026static int pum_extra_width; /* width of extra stuff */
Bram Moolenaar4b779472005-10-04 09:12:31 +000027static int pum_scrollbar; /* TRUE when scrollbar present */
28
29static int pum_row; /* top row of pum */
30static int pum_col; /* left column of pum */
31
Bram Moolenaar0e6e1792018-06-17 17:10:59 +020032static win_T *pum_window = NULL;
Bram Moolenaar491ac282018-06-17 14:47:55 +020033static int pum_win_row;
34static int pum_win_height;
35static int pum_win_col;
36static int pum_win_wcol;
37static int pum_win_width;
38
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000039static int pum_do_redraw = FALSE; /* do redraw anyway */
40
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010041static int pum_set_selected(int n, int repeat);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000042
Bram Moolenaar4b779472005-10-04 09:12:31 +000043#define PUM_DEF_HEIGHT 10
Bram Moolenaar4b779472005-10-04 09:12:31 +000044
Bram Moolenaar51b0f372017-11-18 18:52:04 +010045 static void
46pum_compute_size(void)
47{
48 int i;
49 int w;
50
51 /* Compute the width of the widest match and the widest extra. */
52 pum_base_width = 0;
53 pum_kind_width = 0;
54 pum_extra_width = 0;
55 for (i = 0; i < pum_size; ++i)
56 {
57 w = vim_strsize(pum_array[i].pum_text);
58 if (pum_base_width < w)
59 pum_base_width = w;
60 if (pum_array[i].pum_kind != NULL)
61 {
62 w = vim_strsize(pum_array[i].pum_kind) + 1;
63 if (pum_kind_width < w)
64 pum_kind_width = w;
65 }
66 if (pum_array[i].pum_extra != NULL)
67 {
68 w = vim_strsize(pum_array[i].pum_extra) + 1;
69 if (pum_extra_width < w)
70 pum_extra_width = w;
71 }
72 }
73}
74
Bram Moolenaar4b779472005-10-04 09:12:31 +000075/*
76 * Show the popup menu with items "array[size]".
77 * "array" must remain valid until pum_undisplay() is called!
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010078 * When possible the leftmost character is aligned with cursor column.
Bram Moolenaar4b779472005-10-04 09:12:31 +000079 * The menu appears above the screen line "row" or at "row" + "height" - 1.
80 */
81 void
Bram Moolenaar05540972016-01-30 20:31:25 +010082pum_display(
83 pumitem_T *array,
84 int size,
85 int selected) /* index of initially selected item, none if
Bram Moolenaar1f35bf92006-03-07 22:38:47 +000086 out of range */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000087{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000088 int def_width;
89 int max_width;
Bram Moolenaarc1a11ed2008-06-24 22:09:24 +000090 int context_lines;
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010091 int cursor_col;
Bram Moolenaar91e44a32016-11-04 20:08:52 +010092 int above_row;
93 int below_row;
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000094 int redo_count = 0;
Bram Moolenaar4033c552017-09-16 20:54:51 +020095#if defined(FEAT_QUICKFIX)
Bram Moolenaar91e44a32016-11-04 20:08:52 +010096 win_T *pvwin;
Bram Moolenaaraab33832016-11-04 22:08:29 +010097#endif
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000098
Bram Moolenaara5e66212017-09-29 22:42:33 +020099 do
100 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100101 def_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200102 above_row = 0;
103 below_row = cmdline_row;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000104
Bram Moolenaara5e66212017-09-29 22:42:33 +0200105 /* Pretend the pum is already there to avoid that must_redraw is set
106 * when 'cuc' is on. */
107 pum_array = (pumitem_T *)1;
108 validate_cursor_col();
109 pum_array = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000110
Bram Moolenaar491ac282018-06-17 14:47:55 +0200111 // Remember the essential parts of the window position and size, so we
112 // can decide when to reposition the popup menu.
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200113 pum_window = curwin;
Bram Moolenaar491ac282018-06-17 14:47:55 +0200114 pum_win_row = curwin->w_wrow + W_WINROW(curwin);
115 pum_win_height = curwin->w_height;
116 pum_win_col = curwin->w_wincol;
117 pum_win_wcol = curwin->w_wcol;
118 pum_win_width = curwin->w_width;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000119
Bram Moolenaar4033c552017-09-16 20:54:51 +0200120#if defined(FEAT_QUICKFIX)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200121 FOR_ALL_WINDOWS(pvwin)
122 if (pvwin->w_p_pvw)
123 break;
124 if (pvwin != NULL)
125 {
126 if (W_WINROW(pvwin) < W_WINROW(curwin))
127 above_row = W_WINROW(pvwin) + pvwin->w_height;
128 else if (W_WINROW(pvwin) > W_WINROW(curwin) + curwin->w_height)
129 below_row = W_WINROW(pvwin);
130 }
Bram Moolenaar0106e3d2016-02-23 18:55:43 +0100131#endif
Bram Moolenaarefd2bf12006-03-16 21:41:35 +0000132
Bram Moolenaara5e66212017-09-29 22:42:33 +0200133 /*
134 * Figure out the size and position of the pum.
135 */
136 if (size < PUM_DEF_HEIGHT)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000137 pum_height = size;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000138 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200139 pum_height = PUM_DEF_HEIGHT;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000140 if (p_ph > 0 && pum_height > p_ph)
141 pum_height = p_ph;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000142
Bram Moolenaara750ac22018-09-09 15:27:59 +0200143 /* Put the pum below "pum_win_row" if possible. If there are few lines
144 * decide on where there is more room. */
Bram Moolenaar491ac282018-06-17 14:47:55 +0200145 if (pum_win_row + 2 >= below_row - pum_height
146 && pum_win_row - above_row > (below_row - above_row) / 2)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200147 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200148 /* pum above "pum_win_row" */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200149
150 /* Leave two lines of context if possible */
151 if (curwin->w_wrow - curwin->w_cline_row >= 2)
152 context_lines = 2;
153 else
154 context_lines = curwin->w_wrow - curwin->w_cline_row;
155
Bram Moolenaar491ac282018-06-17 14:47:55 +0200156 if (pum_win_row >= size + context_lines)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200157 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200158 pum_row = pum_win_row - size - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200159 pum_height = size;
160 }
161 else
162 {
163 pum_row = 0;
Bram Moolenaar491ac282018-06-17 14:47:55 +0200164 pum_height = pum_win_row - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200165 }
166 if (p_ph > 0 && pum_height > p_ph)
167 {
168 pum_row += pum_height - p_ph;
169 pum_height = p_ph;
170 }
171 }
172 else
173 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200174 /* pum below "pum_win_row" */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200175
176 /* Leave two lines of context if possible */
177 if (curwin->w_cline_row
178 + curwin->w_cline_height - curwin->w_wrow >= 3)
179 context_lines = 3;
180 else
181 context_lines = curwin->w_cline_row
182 + curwin->w_cline_height - curwin->w_wrow;
183
Bram Moolenaar491ac282018-06-17 14:47:55 +0200184 pum_row = pum_win_row + context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200185 if (size > below_row - pum_row)
186 pum_height = below_row - pum_row;
187 else
188 pum_height = size;
189 if (p_ph > 0 && pum_height > p_ph)
190 pum_height = p_ph;
191 }
192
193 /* don't display when we only have room for one line */
194 if (pum_height < 1 || (pum_height == 1 && size > 1))
195 return;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000196
Bram Moolenaar4033c552017-09-16 20:54:51 +0200197#if defined(FEAT_QUICKFIX)
Bram Moolenaar614ab8a2018-12-01 11:59:00 +0100198 // If there is a preview window above avoid drawing over it.
199 if (pvwin != NULL && pum_row < above_row && pum_height > above_row)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000200 {
Bram Moolenaar614ab8a2018-12-01 11:59:00 +0100201 pum_row = above_row;
202 pum_height = pum_win_row - above_row;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000203 }
Bram Moolenaara5e66212017-09-29 22:42:33 +0200204#endif
205
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100206 pum_array = array;
207 pum_size = size;
208 pum_compute_size();
209 max_width = pum_base_width;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000210
Bram Moolenaara5e66212017-09-29 22:42:33 +0200211 /* Calculate column */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000212#ifdef FEAT_RIGHTLEFT
213 if (curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100214 cursor_col = curwin->w_wincol + curwin->w_width
215 - curwin->w_wcol - 1;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000216 else
217#endif
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100218 cursor_col = curwin->w_wincol + curwin->w_wcol;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000219
Bram Moolenaara5e66212017-09-29 22:42:33 +0200220 /* if there are more items than room we need a scrollbar */
221 if (pum_height < size)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000222 {
Bram Moolenaara5e66212017-09-29 22:42:33 +0200223 pum_scrollbar = 1;
224 ++max_width;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000225 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000226 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200227 pum_scrollbar = 0;
228
229 if (def_width < max_width)
230 def_width = max_width;
231
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100232 if (((cursor_col < Columns - p_pw
233 || cursor_col < Columns - max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000234#ifdef FEAT_RIGHTLEFT
Bram Moolenaara5e66212017-09-29 22:42:33 +0200235 && !curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100236 || (curwin->w_p_rl
237 && (cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000238#endif
Bram Moolenaara5e66212017-09-29 22:42:33 +0200239 ))
240 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100241 /* align pum with "cursor_col" */
242 pum_col = cursor_col;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000243
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100244 /* start with the maximum space available */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200245#ifdef FEAT_RIGHTLEFT
246 if (curwin->w_p_rl)
247 pum_width = pum_col - pum_scrollbar + 1;
248 else
249#endif
250 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000251
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100252 if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100253 && pum_width > p_pw)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200254 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100255 /* the width is more than needed for the items, make it
256 * narrower */
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100257 pum_width = max_width + pum_kind_width + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100258 if (pum_width < p_pw)
259 pum_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200260 }
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100261 else if (((cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100262#ifdef FEAT_RIGHTLEFT
263 && !curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100264 || (curwin->w_p_rl && (cursor_col < Columns - p_pw
265 || cursor_col < Columns - max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100266#endif
267 ))
268 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100269 /* align pum edge with "cursor_col" */
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100270#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100271 if (curwin->w_p_rl
Bram Moolenaarbb008dd2018-02-24 18:59:55 +0100272 && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100273 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100274 pum_col = cursor_col + max_width + pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100275 if (pum_col >= Columns)
276 pum_col = Columns - 1;
277 }
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100278 else if (!curwin->w_p_rl)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100279#endif
280 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100281 if (curwin->w_wincol > Columns - max_width - pum_scrollbar
282 && max_width <= p_pw)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100283 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100284 /* use full width to end of the screen */
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100285 pum_col = Columns - max_width - pum_scrollbar;
286 if (pum_col < 0)
287 pum_col = 0;
288 }
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100289 }
290
291#ifdef FEAT_RIGHTLEFT
292 if (curwin->w_p_rl)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100293 pum_width = pum_col - pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100294 else
295#endif
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100296 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100297
Bram Moolenaar42443c72018-02-10 18:28:52 +0100298 if (pum_width < p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100299 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100300 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100301#ifdef FEAT_RIGHTLEFT
302 if (curwin->w_p_rl)
303 {
304 if (pum_width > pum_col)
305 pum_width = pum_col;
306 }
307 else
308#endif
309 {
310 if (pum_width >= Columns - pum_col)
311 pum_width = Columns - pum_col - 1;
312 }
313 }
314 else if (pum_width > max_width + pum_kind_width
315 + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100316 && pum_width > p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100317 {
318 pum_width = max_width + pum_kind_width
319 + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100320 if (pum_width < p_pw)
321 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100322 }
323 }
324
Bram Moolenaara5e66212017-09-29 22:42:33 +0200325 }
326 else if (Columns < def_width)
327 {
328 /* not enough room, will use what we have */
329#ifdef FEAT_RIGHTLEFT
330 if (curwin->w_p_rl)
331 pum_col = Columns - 1;
332 else
333#endif
334 pum_col = 0;
335 pum_width = Columns - 1;
336 }
337 else
338 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100339 if (max_width > p_pw)
340 max_width = p_pw; /* truncate */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200341#ifdef FEAT_RIGHTLEFT
342 if (curwin->w_p_rl)
343 pum_col = max_width - 1;
344 else
345#endif
346 pum_col = Columns - max_width;
347 pum_width = max_width - pum_scrollbar;
348 }
349
Bram Moolenaara5e66212017-09-29 22:42:33 +0200350 /* Set selected item and redraw. If the window size changed need to
351 * redo the positioning. Limit this to two times, when there is not
352 * much room the window size will keep changing. */
353 } while (pum_set_selected(selected, redo_count) && ++redo_count <= 2);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000354}
355
356/*
357 * Redraw the popup menu, using "pum_first" and "pum_selected".
358 */
359 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100360pum_redraw(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000361{
362 int row = pum_row;
363 int col;
364 int attr_norm = highlight_attr[HLF_PNI];
365 int attr_select = highlight_attr[HLF_PSI];
366 int attr_scroll = highlight_attr[HLF_PSB];
367 int attr_thumb = highlight_attr[HLF_PST];
368 int attr;
369 int i;
370 int idx;
371 char_u *s;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000372 char_u *p = NULL;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000373 int totwidth, width, w;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000374 int thumb_pos = 0;
375 int thumb_heigth = 1;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000376 int round;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000377 int n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000378
Bram Moolenaar4c1e6262013-11-06 04:04:33 +0100379 /* Never display more than we have */
380 if (pum_first > pum_size - pum_height)
381 pum_first = pum_size - pum_height;
382
Bram Moolenaar4b779472005-10-04 09:12:31 +0000383 if (pum_scrollbar)
384 {
385 thumb_heigth = pum_height * pum_height / pum_size;
386 if (thumb_heigth == 0)
387 thumb_heigth = 1;
388 thumb_pos = (pum_first * (pum_height - thumb_heigth)
389 + (pum_size - pum_height) / 2)
390 / (pum_size - pum_height);
391 }
392
393 for (i = 0; i < pum_height; ++i)
394 {
395 idx = i + pum_first;
396 attr = (idx == pum_selected) ? attr_select : attr_norm;
397
398 /* prepend a space if there is room */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000399#ifdef FEAT_RIGHTLEFT
400 if (curwin->w_p_rl)
401 {
Bram Moolenaar02631462017-09-22 15:20:32 +0200402 if (pum_col < curwin->w_wincol + curwin->w_width - 1)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000403 screen_putchar(' ', row, pum_col + 1, attr);
404 }
405 else
406#endif
407 if (pum_col > 0)
408 screen_putchar(' ', row, pum_col - 1, attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000409
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000410 /* Display each entry, use two spaces for a Tab.
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000411 * Do this 3 times: For the main text, kind and extra info */
Bram Moolenaar4b779472005-10-04 09:12:31 +0000412 col = pum_col;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000413 totwidth = 0;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000414 for (round = 1; round <= 3; ++round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000415 {
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000416 width = 0;
417 s = NULL;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000418 switch (round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000419 {
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000420 case 1: p = pum_array[idx].pum_text; break;
421 case 2: p = pum_array[idx].pum_kind; break;
422 case 3: p = pum_array[idx].pum_extra; break;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000423 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000424 if (p != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100425 for ( ; ; MB_PTR_ADV(p))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000426 {
427 if (s == NULL)
428 s = p;
429 w = ptr2cells(p);
430 if (*p == NUL || *p == TAB || totwidth + w > pum_width)
431 {
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000432 /* Display the text that fits or comes before a Tab.
433 * First convert it to printable characters. */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000434 char_u *st;
435 int saved = *p;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000436
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100437 if (saved != NUL)
438 *p = NUL;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000439 st = transstr(s);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100440 if (saved != NUL)
441 *p = saved;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000442#ifdef FEAT_RIGHTLEFT
443 if (curwin->w_p_rl)
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000444 {
Bram Moolenaarabc97732007-08-08 20:49:37 +0000445 if (st != NULL)
446 {
447 char_u *rt = reverse_text(st);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000448
449 if (rt != NULL)
450 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100451 char_u *rt_start = rt;
452 int size;
453
454 size = vim_strsize(rt);
455 if (size > pum_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000456 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100457 do
458 {
459 size -= has_mbyte
460 ? (*mb_ptr2cells)(rt) : 1;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100461 MB_PTR_ADV(rt);
Bram Moolenaard836bb92010-01-19 18:06:03 +0100462 } while (size > pum_width);
463
464 if (size < pum_width)
465 {
466 /* Most left character requires
467 * 2-cells but only 1 cell is
468 * available on screen. Put a
469 * '<' on the left of the pum
470 * item */
471 *(--rt) = '<';
472 size++;
473 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000474 }
Bram Moolenaard836bb92010-01-19 18:06:03 +0100475 screen_puts_len(rt, (int)STRLEN(rt),
476 row, col - size + 1, attr);
477 vim_free(rt_start);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000478 }
479 vim_free(st);
480 }
481 col -= width;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000482 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000483 else
484#endif
485 {
486 if (st != NULL)
487 {
488 screen_puts_len(st, (int)STRLEN(st), row, col,
489 attr);
490 vim_free(st);
491 }
492 col += width;
493 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000494
495 if (*p != TAB)
496 break;
497
498 /* Display two spaces for a Tab. */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000499#ifdef FEAT_RIGHTLEFT
500 if (curwin->w_p_rl)
501 {
502 screen_puts_len((char_u *)" ", 2, row, col - 1,
503 attr);
504 col -= 2;
505 }
506 else
507#endif
508 {
509 screen_puts_len((char_u *)" ", 2, row, col, attr);
510 col += 2;
511 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000512 totwidth += 2;
513 s = NULL; /* start text at next char */
514 width = 0;
515 }
516 else
517 width += w;
518 }
519
520 if (round > 1)
521 n = pum_kind_width + 1;
522 else
523 n = 1;
524
525 /* Stop when there is nothing more to display. */
526 if (round == 3
527 || (round == 2 && pum_array[idx].pum_extra == NULL)
528 || (round == 1 && pum_array[idx].pum_kind == NULL
529 && pum_array[idx].pum_extra == NULL)
530 || pum_base_width + n >= pum_width)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000531 break;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000532#ifdef FEAT_RIGHTLEFT
533 if (curwin->w_p_rl)
534 {
535 screen_fill(row, row + 1, pum_col - pum_base_width - n + 1,
536 col + 1, ' ', ' ', attr);
537 col = pum_col - pum_base_width - n + 1;
538 }
539 else
540#endif
541 {
542 screen_fill(row, row + 1, col, pum_col + pum_base_width + n,
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000543 ' ', ' ', attr);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000544 col = pum_col + pum_base_width + n;
545 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000546 totwidth = pum_base_width + n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000547 }
548
Bram Moolenaarabc97732007-08-08 20:49:37 +0000549#ifdef FEAT_RIGHTLEFT
550 if (curwin->w_p_rl)
551 screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ',
552 ' ', attr);
553 else
554#endif
555 screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ',
556 attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000557 if (pum_scrollbar > 0)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000558 {
559#ifdef FEAT_RIGHTLEFT
560 if (curwin->w_p_rl)
561 screen_putchar(' ', row, pum_col - pum_width,
562 i >= thumb_pos && i < thumb_pos + thumb_heigth
Bram Moolenaar4b779472005-10-04 09:12:31 +0000563 ? attr_thumb : attr_scroll);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000564 else
565#endif
566 screen_putchar(' ', row, pum_col + pum_width,
567 i >= thumb_pos && i < thumb_pos + thumb_heigth
568 ? attr_thumb : attr_scroll);
569 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000570
571 ++row;
572 }
573}
574
Bram Moolenaar4b779472005-10-04 09:12:31 +0000575/*
576 * Set the index of the currently selected item. The menu will scroll when
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000577 * necessary. When "n" is out of range don't scroll.
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000578 * This may be repeated when the preview window is used:
579 * "repeat" == 0: open preview window normally
580 * "repeat" == 1: open preview window but don't set the size
581 * "repeat" == 2: don't open preview window
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000582 * Returns TRUE when the window was resized and the location of the popup menu
583 * must be recomputed.
Bram Moolenaar4b779472005-10-04 09:12:31 +0000584 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000585 static int
Bram Moolenaar05540972016-01-30 20:31:25 +0100586pum_set_selected(int n, int repeat)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000587{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000588 int resized = FALSE;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000589 int context = pum_height / 2;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000590
Bram Moolenaar4b779472005-10-04 09:12:31 +0000591 pum_selected = n;
592
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000593 if (pum_selected >= 0 && pum_selected < pum_size)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000594 {
Bram Moolenaare3226be2005-12-18 22:10:00 +0000595 if (pum_first > pum_selected - 4)
596 {
597 /* scroll down; when we did a jump it's probably a PageUp then
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000598 * scroll a whole page */
Bram Moolenaare3226be2005-12-18 22:10:00 +0000599 if (pum_first > pum_selected - 2)
600 {
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000601 pum_first -= pum_height - 2;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000602 if (pum_first < 0)
603 pum_first = 0;
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000604 else if (pum_first > pum_selected)
605 pum_first = pum_selected;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000606 }
607 else
608 pum_first = pum_selected;
609 }
610 else if (pum_first < pum_selected - pum_height + 5)
611 {
612 /* scroll up; when we did a jump it's probably a PageDown then
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000613 * scroll a whole page */
Bram Moolenaare3226be2005-12-18 22:10:00 +0000614 if (pum_first < pum_selected - pum_height + 1 + 2)
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000615 {
616 pum_first += pum_height - 2;
617 if (pum_first < pum_selected - pum_height + 1)
618 pum_first = pum_selected - pum_height + 1;
619 }
Bram Moolenaare3226be2005-12-18 22:10:00 +0000620 else
621 pum_first = pum_selected - pum_height + 1;
622 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000623
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000624 /* Give a few lines of context when possible. */
625 if (context > 3)
626 context = 3;
627 if (pum_height > 2)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000628 {
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000629 if (pum_first > pum_selected - context)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000630 {
631 /* scroll down */
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000632 pum_first = pum_selected - context;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000633 if (pum_first < 0)
634 pum_first = 0;
635 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000636 else if (pum_first < pum_selected + context - pum_height + 1)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000637 {
638 /* scroll up */
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000639 pum_first = pum_selected + context - pum_height + 1;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000640 }
641 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000642
Bram Moolenaar4033c552017-09-16 20:54:51 +0200643#if defined(FEAT_QUICKFIX)
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000644 /*
645 * Show extra info in the preview window if there is something and
646 * 'completeopt' contains "preview".
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000647 * Skip this when tried twice already.
648 * Skip this also when there is not much room.
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000649 * NOTE: Be very careful not to sync undo!
650 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000651 if (pum_array[pum_selected].pum_info != NULL
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000652 && Rows > 10
653 && repeat <= 1
654 && vim_strchr(p_cot, 'p') != NULL)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000655 {
656 win_T *curwin_save = curwin;
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200657 tabpage_T *curtab_save = curtab;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000658 int res = OK;
659
Bram Moolenaaree236d02010-10-27 17:11:15 +0200660 /* Open a preview window. 3 lines by default. Prefer
661 * 'previewheight' if set and smaller. */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000662 g_do_tagpreview = 3;
Bram Moolenaaree236d02010-10-27 17:11:15 +0200663 if (p_pvh > 0 && p_pvh < g_do_tagpreview)
664 g_do_tagpreview = p_pvh;
Bram Moolenaarc8045152014-07-09 19:58:24 +0200665 ++RedrawingDisabled;
Bram Moolenaare7d13762015-10-30 14:23:33 +0100666 /* Prevent undo sync here, if an autocommand syncs undo weird
667 * things can happen to the undo tree. */
668 ++no_u_sync;
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000669 resized = prepare_tagpreview(FALSE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100670 --no_u_sync;
Bram Moolenaarc8045152014-07-09 19:58:24 +0200671 --RedrawingDisabled;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000672 g_do_tagpreview = 0;
673
674 if (curwin->w_p_pvw)
675 {
Bram Moolenaar50e53762016-10-27 14:49:15 +0200676 if (!resized
677 && curbuf->b_nwindows == 1
678 && curbuf->b_fname == NULL
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000679 && curbuf->b_p_bt[0] == 'n' && curbuf->b_p_bt[2] == 'f'
680 && curbuf->b_p_bh[0] == 'w')
681 {
682 /* Already a "wipeout" buffer, make it empty. */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100683 while (!BUFEMPTY())
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000684 ml_delete((linenr_T)1, FALSE);
685 }
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000686 else
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000687 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000688 /* Don't want to sync undo in the current buffer. */
689 ++no_u_sync;
Bram Moolenaar701f7af2008-11-15 13:12:07 +0000690 res = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 0, NULL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000691 --no_u_sync;
692 if (res == OK)
693 {
694 /* Edit a new, empty buffer. Set options for a "wipeout"
695 * buffer. */
696 set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL);
697 set_option_value((char_u *)"bt", 0L,
698 (char_u *)"nofile", OPT_LOCAL);
699 set_option_value((char_u *)"bh", 0L,
700 (char_u *)"wipe", OPT_LOCAL);
701 set_option_value((char_u *)"diff", 0L,
Bram Moolenaar7f514742007-06-28 19:33:43 +0000702 NULL, OPT_LOCAL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000703 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000704 }
705 if (res == OK)
706 {
707 char_u *p, *e;
708 linenr_T lnum = 0;
709
710 for (p = pum_array[pum_selected].pum_info; *p != NUL; )
711 {
712 e = vim_strchr(p, '\n');
713 if (e == NULL)
714 {
715 ml_append(lnum++, p, 0, FALSE);
716 break;
717 }
718 else
719 {
720 *e = NUL;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000721 ml_append(lnum++, p, (int)(e - p + 1), FALSE);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000722 *e = '\n';
723 p = e + 1;
724 }
725 }
726
727 /* Increase the height of the preview window to show the
728 * text, but no more than 'previewheight' lines. */
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000729 if (repeat == 0)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000730 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000731 if (lnum > p_pvh)
732 lnum = p_pvh;
733 if (curwin->w_height < lnum)
734 {
735 win_setheight((int)lnum);
736 resized = TRUE;
737 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000738 }
739
740 curbuf->b_changed = 0;
741 curbuf->b_p_ma = FALSE;
Bram Moolenaar9c27fc32010-03-17 14:48:24 +0100742 curwin->w_cursor.lnum = 1;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000743 curwin->w_cursor.col = 0;
744
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200745 if ((curwin != curwin_save && win_valid(curwin_save))
746 || (curtab != curtab_save
747 && valid_tabpage(curtab_save)))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000748 {
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200749 if (curtab != curtab_save && valid_tabpage(curtab_save))
750 goto_tabpage_tp(curtab_save, FALSE, FALSE);
751
Bram Moolenaar2bace3e2014-07-23 21:10:43 +0200752 /* When the first completion is done and the preview
753 * window is not resized, skip the preview window's
754 * status line redrawing. */
755 if (ins_compl_active() && !resized)
756 curwin->w_redr_status = FALSE;
757
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000758 /* Return cursor to where we were */
759 validate_cursor();
Bram Moolenaar600dddc2006-03-12 22:05:10 +0000760 redraw_later(SOME_VALID);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000761
762 /* When the preview window was resized we need to
763 * update the view on the buffer. Only go back to
764 * the window when needed, otherwise it will always be
765 * redraw. */
766 if (resized)
767 {
Bram Moolenaare7d13762015-10-30 14:23:33 +0100768 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000769 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100770 --no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000771 update_topline();
772 }
773
774 /* Update the screen before drawing the popup menu.
775 * Enable updating the status lines. */
776 pum_do_redraw = TRUE;
777 update_screen(0);
778 pum_do_redraw = FALSE;
779
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000780 if (!resized && win_valid(curwin_save))
Bram Moolenaare7d13762015-10-30 14:23:33 +0100781 {
782 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000783 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100784 --no_u_sync;
785 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000786
787 /* May need to update the screen again when there are
788 * autocommands involved. */
789 pum_do_redraw = TRUE;
790 update_screen(0);
791 pum_do_redraw = FALSE;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000792 }
793 }
794 }
795 }
796#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +0000797 }
798
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000799 if (!resized)
800 pum_redraw();
801
802 return resized;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000803}
804
805/*
806 * Undisplay the popup menu (later).
807 */
808 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100809pum_undisplay(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000810{
811 pum_array = NULL;
Bram Moolenaar0b565e52018-05-14 23:08:32 +0200812 redraw_all_later(NOT_VALID);
Bram Moolenaar2d694602006-08-22 19:48:48 +0000813 redraw_tabline = TRUE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000814 status_redraw_all();
Bram Moolenaar4b779472005-10-04 09:12:31 +0000815}
816
817/*
818 * Clear the popup menu. Currently only resets the offset to the first
819 * displayed item.
820 */
821 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100822pum_clear(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000823{
824 pum_first = 0;
825}
826
827/*
828 * Return TRUE if the popup menu is displayed.
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000829 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
Bram Moolenaar4b779472005-10-04 09:12:31 +0000830 */
831 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100832pum_visible(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000833{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000834 return !pum_do_redraw && pum_array != NULL;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000835}
836
Bram Moolenaare3226be2005-12-18 22:10:00 +0000837/*
Bram Moolenaar491ac282018-06-17 14:47:55 +0200838 * Reposition the popup menu to adjust for window layout changes.
839 */
840 void
841pum_may_redraw(void)
842{
843 pumitem_T *array = pum_array;
844 int len = pum_size;
845 int selected = pum_selected;
846
847 if (!pum_visible())
848 return; // nothing to do
849
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200850 if (pum_window != curwin
851 || (pum_win_row == curwin->w_wrow + W_WINROW(curwin)
852 && pum_win_height == curwin->w_height
853 && pum_win_col == curwin->w_wincol
854 && pum_win_width == curwin->w_width))
Bram Moolenaar491ac282018-06-17 14:47:55 +0200855 {
856 // window position didn't change, redraw in the same position
857 pum_redraw();
858 }
859 else
860 {
861 int wcol = curwin->w_wcol;
862
863 // Window layout changed, recompute the position.
864 // Use the remembered w_wcol value, the cursor may have moved when a
865 // completion was inserted, but we want the menu in the same position.
866 pum_undisplay();
867 curwin->w_wcol = pum_win_wcol;
868 curwin->w_valid |= VALID_WCOL;
869 pum_display(array, len, selected);
870 curwin->w_wcol = wcol;
871 }
872}
873
874/*
Bram Moolenaare3226be2005-12-18 22:10:00 +0000875 * Return the height of the popup menu, the number of entries visible.
876 * Only valid when pum_visible() returns TRUE!
877 */
878 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100879pum_get_height(void)
Bram Moolenaare3226be2005-12-18 22:10:00 +0000880{
881 return pum_height;
882}
883
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100884# if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
885 static void
886pum_position_at_mouse(int min_width)
887{
888 if (Rows - mouse_row > pum_size)
889 {
890 /* Enough space below the mouse row. */
891 pum_row = mouse_row + 1;
892 if (pum_height > Rows - pum_row)
893 pum_height = Rows - pum_row;
894 }
895 else
896 {
897 /* Show above the mouse row, reduce height if it does not fit. */
898 pum_row = mouse_row - pum_size;
899 if (pum_row < 0)
900 {
901 pum_height += pum_row;
902 pum_row = 0;
903 }
904 }
905 if (Columns - mouse_col >= pum_base_width
906 || Columns - mouse_col > min_width)
907 /* Enough space to show at mouse column. */
908 pum_col = mouse_col;
909 else
910 /* Not enough space, right align with window. */
911 pum_col = Columns - (pum_base_width > min_width
912 ? min_width : pum_base_width);
913
914 pum_width = Columns - pum_col;
915 if (pum_width > pum_base_width + 1)
916 pum_width = pum_base_width + 1;
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200917
918 // Do not redraw at cursor position.
919 pum_window = NULL;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100920}
921
922# endif
923
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100924# if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100925static pumitem_T *balloon_array = NULL;
926static int balloon_arraysize;
927static int balloon_mouse_row = 0;
928static int balloon_mouse_col = 0;
929
Bram Moolenaar246fe032017-11-19 19:56:27 +0100930#define BALLOON_MIN_WIDTH 50
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100931#define BALLOON_MIN_HEIGHT 10
932
Bram Moolenaar246fe032017-11-19 19:56:27 +0100933typedef struct {
934 char_u *start;
935 int bytelen;
936 int cells;
937 int indent;
938} balpart_T;
939
940/*
941 * Split a string into parts to display in the balloon.
942 * Aimed at output from gdb. Attempts to split at white space, preserve quoted
943 * strings and make a struct look good.
944 * Resulting array is stored in "array" and returns the size of the array.
945 */
946 int
947split_message(char_u *mesg, pumitem_T **array)
948{
949 garray_T ga;
950 char_u *p;
951 balpart_T *item;
952 int quoted = FALSE;
953 int height;
954 int line;
955 int item_idx;
956 int indent = 0;
957 int max_cells = 0;
958 int max_height = Rows / 2 - 2;
959 int long_item_count = 0;
960 int split_long_items = FALSE;
961
962 ga_init2(&ga, sizeof(balpart_T), 20);
963 p = mesg;
964
965 while (*p != NUL)
966 {
967 if (ga_grow(&ga, 1) == FAIL)
968 goto failed;
969 item = ((balpart_T *)ga.ga_data) + ga.ga_len;
970 item->start = p;
971 item->indent = indent;
972 item->cells = indent * 2;
973 ++ga.ga_len;
974 while (*p != NUL)
975 {
976 if (*p == '"')
977 quoted = !quoted;
978 else if (*p == '\\' && p[1] != NUL)
979 ++p;
980 else if (!quoted)
981 {
982 if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}')
983 {
984 /* Looks like a good point to break. */
985 if (*p == '{')
986 ++indent;
987 else if (*p == '}' && indent > 0)
988 --indent;
989 ++item->cells;
990 p = skipwhite(p + 1);
991 break;
992 }
993 }
994 item->cells += ptr2cells(p);
995 p += MB_PTR2LEN(p);
996 }
997 item->bytelen = p - item->start;
998 if (item->cells > max_cells)
999 max_cells = item->cells;
Bram Moolenaara3571eb2017-11-26 16:53:16 +01001000 long_item_count += (item->cells - 1) / BALLOON_MIN_WIDTH;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001001 }
1002
1003 height = 2 + ga.ga_len;
1004
1005 /* If there are long items and the height is below the limit: split lines */
1006 if (long_item_count > 0 && height + long_item_count <= max_height)
1007 {
1008 split_long_items = TRUE;
1009 height += long_item_count;
1010 }
1011
1012 /* Limit to half the window height, it has to fit above or below the mouse
1013 * position. */
1014 if (height > max_height)
1015 height = max_height;
1016 *array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * height);
1017 if (*array == NULL)
1018 goto failed;
1019
1020 /* Add an empty line above and below, looks better. */
1021 (*array)->pum_text = vim_strsave((char_u *)"");
1022 (*array + height - 1)->pum_text = vim_strsave((char_u *)"");
1023
1024 for (line = 1, item_idx = 0; line < height - 1; ++item_idx)
1025 {
1026 int skip;
1027 int thislen;
1028 int copylen;
1029 int ind;
1030 int cells;
1031
1032 item = ((balpart_T *)ga.ga_data) + item_idx;
1033 for (skip = 0; skip < item->bytelen; skip += thislen)
1034 {
1035 if (split_long_items && item->cells >= BALLOON_MIN_WIDTH)
1036 {
1037 cells = item->indent * 2;
1038 for (p = item->start + skip; p < item->start + item->bytelen;
1039 p += MB_PTR2LEN(p))
1040 if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH)
1041 break;
1042 thislen = p - (item->start + skip);
1043 }
1044 else
1045 thislen = item->bytelen;
1046
1047 /* put indent at the start */
1048 p = alloc(thislen + item->indent * 2 + 1);
1049 for (ind = 0; ind < item->indent * 2; ++ind)
1050 p[ind] = ' ';
1051
1052 /* exclude spaces at the end of the string */
1053 for (copylen = thislen; copylen > 0; --copylen)
1054 if (item->start[skip + copylen - 1] != ' ')
1055 break;
1056
1057 vim_strncpy(p + ind, item->start + skip, copylen);
1058 (*array)[line].pum_text = p;
1059 item->indent = 0; /* wrapped line has no indent */
1060 ++line;
1061 }
1062 }
1063 ga_clear(&ga);
1064 return height;
1065
1066failed:
1067 ga_clear(&ga);
1068 return 0;
1069}
1070
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001071 void
1072ui_remove_balloon(void)
1073{
1074 if (balloon_array != NULL)
1075 {
1076 pum_undisplay();
1077 while (balloon_arraysize > 0)
1078 vim_free(balloon_array[--balloon_arraysize].pum_text);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001079 VIM_CLEAR(balloon_array);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001080 }
1081}
1082
1083/*
1084 * Terminal version of a balloon, uses the popup menu code.
1085 */
1086 void
Bram Moolenaar246fe032017-11-19 19:56:27 +01001087ui_post_balloon(char_u *mesg, list_T *list)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001088{
1089 ui_remove_balloon();
1090
Bram Moolenaar246fe032017-11-19 19:56:27 +01001091 if (mesg == NULL && list == NULL)
1092 return;
1093 if (list != NULL)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001094 {
Bram Moolenaar246fe032017-11-19 19:56:27 +01001095 listitem_T *li;
1096 int idx;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001097
Bram Moolenaar246fe032017-11-19 19:56:27 +01001098 balloon_arraysize = list->lv_len;
1099 balloon_array = (pumitem_T *)alloc_clear(
1100 (unsigned)sizeof(pumitem_T) * list->lv_len);
1101 if (balloon_array == NULL)
1102 return;
1103 for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx)
1104 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001105 char_u *text = tv_get_string_chk(&li->li_tv);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001106
1107 balloon_array[idx].pum_text = vim_strsave(
1108 text == NULL ? (char_u *)"" : text);
1109 }
1110 }
1111 else
1112 balloon_arraysize = split_message(mesg, &balloon_array);
1113
1114 if (balloon_arraysize > 0)
1115 {
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001116 pum_array = balloon_array;
1117 pum_size = balloon_arraysize;
1118 pum_compute_size();
1119 pum_scrollbar = 0;
1120 pum_height = balloon_arraysize;
1121
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001122 pum_position_at_mouse(BALLOON_MIN_WIDTH);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001123 pum_selected = -1;
1124 pum_first = 0;
1125 pum_redraw();
1126 }
1127}
1128
1129/*
1130 * Called when the mouse moved, may remove any displayed balloon.
1131 */
1132 void
1133ui_may_remove_balloon(void)
1134{
1135 if (mouse_row != balloon_mouse_row || mouse_col != balloon_mouse_col)
1136 ui_remove_balloon();
1137}
1138# endif
Bram Moolenaara8f04aa2018-02-10 15:36:55 +01001139
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001140# if defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
1141/*
1142 * Select the pum entry at the mouse position.
1143 */
1144 static void
1145pum_select_mouse_pos(void)
1146{
1147 int idx = mouse_row - pum_row;
1148
1149 if (idx < 0 || idx >= pum_size)
1150 pum_selected = -1;
1151 else if (*pum_array[idx].pum_text != NUL)
1152 pum_selected = idx;
1153}
1154
1155/*
1156 * Execute the currently selected popup menu item.
1157 */
1158 static void
Bram Moolenaar987723e2018-03-06 11:43:04 +01001159pum_execute_menu(vimmenu_T *menu, int mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001160{
1161 vimmenu_T *mp;
1162 int idx = 0;
1163 exarg_T ea;
1164
1165 for (mp = menu->children; mp != NULL; mp = mp->next)
Bram Moolenaar987723e2018-03-06 11:43:04 +01001166 if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001167 {
1168 vim_memset(&ea, 0, sizeof(ea));
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001169 execute_menu(&ea, mp, -1);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001170 break;
1171 }
1172}
1173
1174/*
1175 * Open the terminal version of the popup menu and don't return until it is
1176 * closed.
1177 */
1178 void
1179pum_show_popupmenu(vimmenu_T *menu)
1180{
1181 vimmenu_T *mp;
1182 int idx = 0;
1183 pumitem_T *array;
1184#ifdef FEAT_BEVAL_TERM
1185 int save_bevalterm = p_bevalterm;
1186#endif
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001187 int mode;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001188
1189 pum_undisplay();
1190 pum_size = 0;
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001191 mode = get_menu_mode_flag();
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001192
1193 for (mp = menu->children; mp != NULL; mp = mp->next)
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001194 if (menu_is_separator(mp->dname)
1195 || (mp->modes & mp->enabled & mode))
1196 ++pum_size;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001197
Bram Moolenaarf42b45d2019-01-06 13:11:05 +01001198 // When there are only Terminal mode menus, using "popup Edit" results in
1199 // pum_size being zero.
1200 if (pum_size <= 0)
1201 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001202 emsg(e_menuothermode);
Bram Moolenaarf42b45d2019-01-06 13:11:05 +01001203 return;
1204 }
1205
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001206 array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * pum_size);
1207 if (array == NULL)
1208 return;
1209
1210 for (mp = menu->children; mp != NULL; mp = mp->next)
1211 if (menu_is_separator(mp->dname))
1212 array[idx++].pum_text = (char_u *)"";
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001213 else if (mp->modes & mp->enabled & mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001214 array[idx++].pum_text = mp->dname;
1215
1216 pum_array = array;
1217 pum_compute_size();
1218 pum_scrollbar = 0;
1219 pum_height = pum_size;
1220 pum_position_at_mouse(20);
1221
1222 pum_selected = -1;
1223 pum_first = 0;
1224# ifdef FEAT_BEVAL_TERM
1225 p_bevalterm = TRUE; /* track mouse movement */
1226 mch_setmouse(TRUE);
1227# endif
1228
1229 for (;;)
1230 {
1231 int c;
1232
1233 pum_redraw();
Bram Moolenaar987723e2018-03-06 11:43:04 +01001234 setcursor_mayforce(TRUE);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001235 out_flush();
1236
1237 c = vgetc();
Bram Moolenaar52f18a12018-03-07 22:09:11 +01001238 if (c == ESC || c == Ctrl_C)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001239 break;
1240 else if (c == CAR || c == NL)
1241 {
1242 /* enter: select current item, if any, and close */
Bram Moolenaar987723e2018-03-06 11:43:04 +01001243 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001244 break;
1245 }
1246 else if (c == 'k' || c == K_UP || c == K_MOUSEUP)
1247 {
1248 /* cursor up: select previous item */
1249 while (pum_selected > 0)
1250 {
1251 --pum_selected;
1252 if (*array[pum_selected].pum_text != NUL)
1253 break;
1254 }
1255 }
1256 else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN)
1257 {
1258 /* cursor down: select next item */
1259 while (pum_selected < pum_size - 1)
1260 {
1261 ++pum_selected;
1262 if (*array[pum_selected].pum_text != NUL)
1263 break;
1264 }
1265 }
1266 else if (c == K_RIGHTMOUSE)
1267 {
1268 /* Right mouse down: reposition the menu. */
1269 vungetc(c);
1270 break;
1271 }
1272 else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE)
1273 {
Bram Moolenaar52f18a12018-03-07 22:09:11 +01001274 /* mouse moved: select item in the mouse row */
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001275 pum_select_mouse_pos();
1276 }
1277 else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE)
1278 {
1279 /* left mouse click: select clicked item, if any, and close;
1280 * right mouse release: select clicked item, close if any */
1281 pum_select_mouse_pos();
1282 if (pum_selected >= 0)
1283 {
Bram Moolenaar987723e2018-03-06 11:43:04 +01001284 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001285 break;
1286 }
1287 if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM)
1288 break;
1289 }
1290 }
1291
1292 vim_free(array);
1293 pum_undisplay();
1294# ifdef FEAT_BEVAL_TERM
1295 p_bevalterm = save_bevalterm;
1296 mch_setmouse(TRUE);
1297# endif
1298}
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001299
1300 void
1301pum_make_popup(char_u *path_name, int use_mouse_pos)
1302{
1303 vimmenu_T *menu;
1304
1305 if (!use_mouse_pos)
1306 {
1307 /* Hack: set mouse position at the cursor so that the menu pops up
1308 * around there. */
1309 mouse_row = curwin->w_winrow + curwin->w_wrow;
1310 mouse_col = curwin->w_wincol + curwin->w_wcol;
1311 }
1312
1313 menu = gui_find_menu(path_name);
1314 if (menu != NULL)
1315 pum_show_popupmenu(menu);
1316}
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001317# endif
1318
Bram Moolenaar4b779472005-10-04 09:12:31 +00001319#endif