blob: c481d4be738879896388e611a59862cd2a60782c [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
44#define PUM_DEF_WIDTH 15
45
Bram Moolenaar51b0f372017-11-18 18:52:04 +010046 static void
47pum_compute_size(void)
48{
49 int i;
50 int w;
51
52 /* Compute the width of the widest match and the widest extra. */
53 pum_base_width = 0;
54 pum_kind_width = 0;
55 pum_extra_width = 0;
56 for (i = 0; i < pum_size; ++i)
57 {
58 w = vim_strsize(pum_array[i].pum_text);
59 if (pum_base_width < w)
60 pum_base_width = w;
61 if (pum_array[i].pum_kind != NULL)
62 {
63 w = vim_strsize(pum_array[i].pum_kind) + 1;
64 if (pum_kind_width < w)
65 pum_kind_width = w;
66 }
67 if (pum_array[i].pum_extra != NULL)
68 {
69 w = vim_strsize(pum_array[i].pum_extra) + 1;
70 if (pum_extra_width < w)
71 pum_extra_width = w;
72 }
73 }
74}
75
Bram Moolenaar4b779472005-10-04 09:12:31 +000076/*
77 * Show the popup menu with items "array[size]".
78 * "array" must remain valid until pum_undisplay() is called!
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010079 * When possible the leftmost character is aligned with cursor column.
Bram Moolenaar4b779472005-10-04 09:12:31 +000080 * The menu appears above the screen line "row" or at "row" + "height" - 1.
81 */
82 void
Bram Moolenaar05540972016-01-30 20:31:25 +010083pum_display(
84 pumitem_T *array,
85 int size,
86 int selected) /* index of initially selected item, none if
Bram Moolenaar1f35bf92006-03-07 22:38:47 +000087 out of range */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000088{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000089 int def_width;
90 int max_width;
Bram Moolenaarc1a11ed2008-06-24 22:09:24 +000091 int context_lines;
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010092 int cursor_col;
Bram Moolenaar91e44a32016-11-04 20:08:52 +010093 int above_row;
94 int below_row;
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000095 int redo_count = 0;
Bram Moolenaar4033c552017-09-16 20:54:51 +020096#if defined(FEAT_QUICKFIX)
Bram Moolenaar91e44a32016-11-04 20:08:52 +010097 win_T *pvwin;
Bram Moolenaaraab33832016-11-04 22:08:29 +010098#endif
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000099
Bram Moolenaara5e66212017-09-29 22:42:33 +0200100 do
101 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100102 def_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200103 above_row = 0;
104 below_row = cmdline_row;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000105
Bram Moolenaara5e66212017-09-29 22:42:33 +0200106 /* Pretend the pum is already there to avoid that must_redraw is set
107 * when 'cuc' is on. */
108 pum_array = (pumitem_T *)1;
109 validate_cursor_col();
110 pum_array = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000111
Bram Moolenaar491ac282018-06-17 14:47:55 +0200112 // Remember the essential parts of the window position and size, so we
113 // can decide when to reposition the popup menu.
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200114 pum_window = curwin;
Bram Moolenaar491ac282018-06-17 14:47:55 +0200115 pum_win_row = curwin->w_wrow + W_WINROW(curwin);
116 pum_win_height = curwin->w_height;
117 pum_win_col = curwin->w_wincol;
118 pum_win_wcol = curwin->w_wcol;
119 pum_win_width = curwin->w_width;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000120
Bram Moolenaar4033c552017-09-16 20:54:51 +0200121#if defined(FEAT_QUICKFIX)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200122 FOR_ALL_WINDOWS(pvwin)
123 if (pvwin->w_p_pvw)
124 break;
125 if (pvwin != NULL)
126 {
127 if (W_WINROW(pvwin) < W_WINROW(curwin))
128 above_row = W_WINROW(pvwin) + pvwin->w_height;
129 else if (W_WINROW(pvwin) > W_WINROW(curwin) + curwin->w_height)
130 below_row = W_WINROW(pvwin);
131 }
Bram Moolenaar0106e3d2016-02-23 18:55:43 +0100132#endif
Bram Moolenaarefd2bf12006-03-16 21:41:35 +0000133
Bram Moolenaara5e66212017-09-29 22:42:33 +0200134 /*
135 * Figure out the size and position of the pum.
136 */
137 if (size < PUM_DEF_HEIGHT)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000138 pum_height = size;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000139 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200140 pum_height = PUM_DEF_HEIGHT;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000141 if (p_ph > 0 && pum_height > p_ph)
142 pum_height = p_ph;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000143
Bram Moolenaara750ac22018-09-09 15:27:59 +0200144 /* Put the pum below "pum_win_row" if possible. If there are few lines
145 * decide on where there is more room. */
Bram Moolenaar491ac282018-06-17 14:47:55 +0200146 if (pum_win_row + 2 >= below_row - pum_height
147 && pum_win_row - above_row > (below_row - above_row) / 2)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200148 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200149 /* pum above "pum_win_row" */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200150
151 /* Leave two lines of context if possible */
152 if (curwin->w_wrow - curwin->w_cline_row >= 2)
153 context_lines = 2;
154 else
155 context_lines = curwin->w_wrow - curwin->w_cline_row;
156
Bram Moolenaar491ac282018-06-17 14:47:55 +0200157 if (pum_win_row >= size + context_lines)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200158 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200159 pum_row = pum_win_row - size - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200160 pum_height = size;
161 }
162 else
163 {
164 pum_row = 0;
Bram Moolenaar491ac282018-06-17 14:47:55 +0200165 pum_height = pum_win_row - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200166 }
167 if (p_ph > 0 && pum_height > p_ph)
168 {
169 pum_row += pum_height - p_ph;
170 pum_height = p_ph;
171 }
172 }
173 else
174 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200175 /* pum below "pum_win_row" */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200176
177 /* Leave two lines of context if possible */
178 if (curwin->w_cline_row
179 + curwin->w_cline_height - curwin->w_wrow >= 3)
180 context_lines = 3;
181 else
182 context_lines = curwin->w_cline_row
183 + curwin->w_cline_height - curwin->w_wrow;
184
Bram Moolenaar491ac282018-06-17 14:47:55 +0200185 pum_row = pum_win_row + context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200186 if (size > below_row - pum_row)
187 pum_height = below_row - pum_row;
188 else
189 pum_height = size;
190 if (p_ph > 0 && pum_height > p_ph)
191 pum_height = p_ph;
192 }
193
194 /* don't display when we only have room for one line */
195 if (pum_height < 1 || (pum_height == 1 && size > 1))
196 return;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000197
Bram Moolenaar4033c552017-09-16 20:54:51 +0200198#if defined(FEAT_QUICKFIX)
Bram Moolenaara750ac22018-09-09 15:27:59 +0200199 // If there is a preview window at the above avoid drawing over it.
200 // Do keep at least 10 entries.
201 if (pvwin != NULL && pum_row < above_row && pum_height > 10)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000202 {
Bram Moolenaara750ac22018-09-09 15:27:59 +0200203 if (pum_win_row - above_row < 10)
204 {
205 pum_row = pum_win_row - 10;
206 pum_height = 10;
207 }
208 else
209 {
210 pum_row = above_row;
211 pum_height = pum_win_row - above_row;
212 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000213 }
Bram Moolenaara5e66212017-09-29 22:42:33 +0200214#endif
215
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100216 pum_array = array;
217 pum_size = size;
218 pum_compute_size();
219 max_width = pum_base_width;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000220
Bram Moolenaara5e66212017-09-29 22:42:33 +0200221 /* Calculate column */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000222#ifdef FEAT_RIGHTLEFT
223 if (curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100224 cursor_col = curwin->w_wincol + curwin->w_width
225 - curwin->w_wcol - 1;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000226 else
227#endif
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100228 cursor_col = curwin->w_wincol + curwin->w_wcol;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000229
Bram Moolenaara5e66212017-09-29 22:42:33 +0200230 /* if there are more items than room we need a scrollbar */
231 if (pum_height < size)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000232 {
Bram Moolenaara5e66212017-09-29 22:42:33 +0200233 pum_scrollbar = 1;
234 ++max_width;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000235 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000236 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200237 pum_scrollbar = 0;
238
239 if (def_width < max_width)
240 def_width = max_width;
241
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100242 if (((cursor_col < Columns - p_pw
243 || cursor_col < Columns - max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000244#ifdef FEAT_RIGHTLEFT
Bram Moolenaara5e66212017-09-29 22:42:33 +0200245 && !curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100246 || (curwin->w_p_rl
247 && (cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000248#endif
Bram Moolenaara5e66212017-09-29 22:42:33 +0200249 ))
250 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100251 /* align pum with "cursor_col" */
252 pum_col = cursor_col;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000253
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100254 /* start with the maximum space available */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200255#ifdef FEAT_RIGHTLEFT
256 if (curwin->w_p_rl)
257 pum_width = pum_col - pum_scrollbar + 1;
258 else
259#endif
260 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000261
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100262 if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100263 && pum_width > p_pw)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200264 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100265 /* the width is more than needed for the items, make it
266 * narrower */
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100267 pum_width = max_width + pum_kind_width + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100268 if (pum_width < p_pw)
269 pum_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200270 }
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100271 else if (((cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100272#ifdef FEAT_RIGHTLEFT
273 && !curwin->w_p_rl)
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100274 || (curwin->w_p_rl && (cursor_col < Columns - p_pw
275 || cursor_col < Columns - max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100276#endif
277 ))
278 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100279 /* align pum edge with "cursor_col" */
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100280#ifdef FEAT_RIGHTLEFT
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100281 if (curwin->w_p_rl
Bram Moolenaarbb008dd2018-02-24 18:59:55 +0100282 && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100283 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100284 pum_col = cursor_col + max_width + pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100285 if (pum_col >= Columns)
286 pum_col = Columns - 1;
287 }
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100288 else if (!curwin->w_p_rl)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100289#endif
290 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100291 if (curwin->w_wincol > Columns - max_width - pum_scrollbar
292 && max_width <= p_pw)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100293 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100294 /* use full width to end of the screen */
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100295 pum_col = Columns - max_width - pum_scrollbar;
296 if (pum_col < 0)
297 pum_col = 0;
298 }
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100299 }
300
301#ifdef FEAT_RIGHTLEFT
302 if (curwin->w_p_rl)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100303 pum_width = pum_col - pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100304 else
305#endif
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100306 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100307
Bram Moolenaar42443c72018-02-10 18:28:52 +0100308 if (pum_width < p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100309 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100310 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100311#ifdef FEAT_RIGHTLEFT
312 if (curwin->w_p_rl)
313 {
314 if (pum_width > pum_col)
315 pum_width = pum_col;
316 }
317 else
318#endif
319 {
320 if (pum_width >= Columns - pum_col)
321 pum_width = Columns - pum_col - 1;
322 }
323 }
324 else if (pum_width > max_width + pum_kind_width
325 + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100326 && pum_width > p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100327 {
328 pum_width = max_width + pum_kind_width
329 + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100330 if (pum_width < p_pw)
331 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100332 }
333 }
334
Bram Moolenaara5e66212017-09-29 22:42:33 +0200335 }
336 else if (Columns < def_width)
337 {
338 /* not enough room, will use what we have */
339#ifdef FEAT_RIGHTLEFT
340 if (curwin->w_p_rl)
341 pum_col = Columns - 1;
342 else
343#endif
344 pum_col = 0;
345 pum_width = Columns - 1;
346 }
347 else
348 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100349 if (max_width > p_pw)
350 max_width = p_pw; /* truncate */
Bram Moolenaara5e66212017-09-29 22:42:33 +0200351#ifdef FEAT_RIGHTLEFT
352 if (curwin->w_p_rl)
353 pum_col = max_width - 1;
354 else
355#endif
356 pum_col = Columns - max_width;
357 pum_width = max_width - pum_scrollbar;
358 }
359
Bram Moolenaara5e66212017-09-29 22:42:33 +0200360 /* Set selected item and redraw. If the window size changed need to
361 * redo the positioning. Limit this to two times, when there is not
362 * much room the window size will keep changing. */
363 } while (pum_set_selected(selected, redo_count) && ++redo_count <= 2);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000364}
365
366/*
367 * Redraw the popup menu, using "pum_first" and "pum_selected".
368 */
369 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100370pum_redraw(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000371{
372 int row = pum_row;
373 int col;
374 int attr_norm = highlight_attr[HLF_PNI];
375 int attr_select = highlight_attr[HLF_PSI];
376 int attr_scroll = highlight_attr[HLF_PSB];
377 int attr_thumb = highlight_attr[HLF_PST];
378 int attr;
379 int i;
380 int idx;
381 char_u *s;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000382 char_u *p = NULL;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000383 int totwidth, width, w;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000384 int thumb_pos = 0;
385 int thumb_heigth = 1;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000386 int round;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000387 int n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000388
Bram Moolenaar4c1e6262013-11-06 04:04:33 +0100389 /* Never display more than we have */
390 if (pum_first > pum_size - pum_height)
391 pum_first = pum_size - pum_height;
392
Bram Moolenaar4b779472005-10-04 09:12:31 +0000393 if (pum_scrollbar)
394 {
395 thumb_heigth = pum_height * pum_height / pum_size;
396 if (thumb_heigth == 0)
397 thumb_heigth = 1;
398 thumb_pos = (pum_first * (pum_height - thumb_heigth)
399 + (pum_size - pum_height) / 2)
400 / (pum_size - pum_height);
401 }
402
403 for (i = 0; i < pum_height; ++i)
404 {
405 idx = i + pum_first;
406 attr = (idx == pum_selected) ? attr_select : attr_norm;
407
408 /* prepend a space if there is room */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000409#ifdef FEAT_RIGHTLEFT
410 if (curwin->w_p_rl)
411 {
Bram Moolenaar02631462017-09-22 15:20:32 +0200412 if (pum_col < curwin->w_wincol + curwin->w_width - 1)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000413 screen_putchar(' ', row, pum_col + 1, attr);
414 }
415 else
416#endif
417 if (pum_col > 0)
418 screen_putchar(' ', row, pum_col - 1, attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000419
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000420 /* Display each entry, use two spaces for a Tab.
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000421 * Do this 3 times: For the main text, kind and extra info */
Bram Moolenaar4b779472005-10-04 09:12:31 +0000422 col = pum_col;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000423 totwidth = 0;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000424 for (round = 1; round <= 3; ++round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000425 {
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000426 width = 0;
427 s = NULL;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000428 switch (round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000429 {
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000430 case 1: p = pum_array[idx].pum_text; break;
431 case 2: p = pum_array[idx].pum_kind; break;
432 case 3: p = pum_array[idx].pum_extra; break;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000433 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000434 if (p != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100435 for ( ; ; MB_PTR_ADV(p))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000436 {
437 if (s == NULL)
438 s = p;
439 w = ptr2cells(p);
440 if (*p == NUL || *p == TAB || totwidth + w > pum_width)
441 {
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000442 /* Display the text that fits or comes before a Tab.
443 * First convert it to printable characters. */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000444 char_u *st;
445 int saved = *p;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000446
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100447 if (saved != NUL)
448 *p = NUL;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000449 st = transstr(s);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100450 if (saved != NUL)
451 *p = saved;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000452#ifdef FEAT_RIGHTLEFT
453 if (curwin->w_p_rl)
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000454 {
Bram Moolenaarabc97732007-08-08 20:49:37 +0000455 if (st != NULL)
456 {
457 char_u *rt = reverse_text(st);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000458
459 if (rt != NULL)
460 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100461 char_u *rt_start = rt;
462 int size;
463
464 size = vim_strsize(rt);
465 if (size > pum_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000466 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100467 do
468 {
469 size -= has_mbyte
470 ? (*mb_ptr2cells)(rt) : 1;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100471 MB_PTR_ADV(rt);
Bram Moolenaard836bb92010-01-19 18:06:03 +0100472 } while (size > pum_width);
473
474 if (size < pum_width)
475 {
476 /* Most left character requires
477 * 2-cells but only 1 cell is
478 * available on screen. Put a
479 * '<' on the left of the pum
480 * item */
481 *(--rt) = '<';
482 size++;
483 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000484 }
Bram Moolenaard836bb92010-01-19 18:06:03 +0100485 screen_puts_len(rt, (int)STRLEN(rt),
486 row, col - size + 1, attr);
487 vim_free(rt_start);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000488 }
489 vim_free(st);
490 }
491 col -= width;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000492 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000493 else
494#endif
495 {
496 if (st != NULL)
497 {
498 screen_puts_len(st, (int)STRLEN(st), row, col,
499 attr);
500 vim_free(st);
501 }
502 col += width;
503 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000504
505 if (*p != TAB)
506 break;
507
508 /* Display two spaces for a Tab. */
Bram Moolenaarabc97732007-08-08 20:49:37 +0000509#ifdef FEAT_RIGHTLEFT
510 if (curwin->w_p_rl)
511 {
512 screen_puts_len((char_u *)" ", 2, row, col - 1,
513 attr);
514 col -= 2;
515 }
516 else
517#endif
518 {
519 screen_puts_len((char_u *)" ", 2, row, col, attr);
520 col += 2;
521 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000522 totwidth += 2;
523 s = NULL; /* start text at next char */
524 width = 0;
525 }
526 else
527 width += w;
528 }
529
530 if (round > 1)
531 n = pum_kind_width + 1;
532 else
533 n = 1;
534
535 /* Stop when there is nothing more to display. */
536 if (round == 3
537 || (round == 2 && pum_array[idx].pum_extra == NULL)
538 || (round == 1 && pum_array[idx].pum_kind == NULL
539 && pum_array[idx].pum_extra == NULL)
540 || pum_base_width + n >= pum_width)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000541 break;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000542#ifdef FEAT_RIGHTLEFT
543 if (curwin->w_p_rl)
544 {
545 screen_fill(row, row + 1, pum_col - pum_base_width - n + 1,
546 col + 1, ' ', ' ', attr);
547 col = pum_col - pum_base_width - n + 1;
548 }
549 else
550#endif
551 {
552 screen_fill(row, row + 1, col, pum_col + pum_base_width + n,
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000553 ' ', ' ', attr);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000554 col = pum_col + pum_base_width + n;
555 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000556 totwidth = pum_base_width + n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000557 }
558
Bram Moolenaarabc97732007-08-08 20:49:37 +0000559#ifdef FEAT_RIGHTLEFT
560 if (curwin->w_p_rl)
561 screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ',
562 ' ', attr);
563 else
564#endif
565 screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ',
566 attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000567 if (pum_scrollbar > 0)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000568 {
569#ifdef FEAT_RIGHTLEFT
570 if (curwin->w_p_rl)
571 screen_putchar(' ', row, pum_col - pum_width,
572 i >= thumb_pos && i < thumb_pos + thumb_heigth
Bram Moolenaar4b779472005-10-04 09:12:31 +0000573 ? attr_thumb : attr_scroll);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000574 else
575#endif
576 screen_putchar(' ', row, pum_col + pum_width,
577 i >= thumb_pos && i < thumb_pos + thumb_heigth
578 ? attr_thumb : attr_scroll);
579 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000580
581 ++row;
582 }
583}
584
Bram Moolenaar4b779472005-10-04 09:12:31 +0000585/*
586 * Set the index of the currently selected item. The menu will scroll when
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000587 * necessary. When "n" is out of range don't scroll.
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000588 * This may be repeated when the preview window is used:
589 * "repeat" == 0: open preview window normally
590 * "repeat" == 1: open preview window but don't set the size
591 * "repeat" == 2: don't open preview window
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000592 * Returns TRUE when the window was resized and the location of the popup menu
593 * must be recomputed.
Bram Moolenaar4b779472005-10-04 09:12:31 +0000594 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000595 static int
Bram Moolenaar05540972016-01-30 20:31:25 +0100596pum_set_selected(int n, int repeat)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000597{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000598 int resized = FALSE;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000599 int context = pum_height / 2;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000600
Bram Moolenaar4b779472005-10-04 09:12:31 +0000601 pum_selected = n;
602
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000603 if (pum_selected >= 0 && pum_selected < pum_size)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000604 {
Bram Moolenaare3226be2005-12-18 22:10:00 +0000605 if (pum_first > pum_selected - 4)
606 {
607 /* scroll down; when we did a jump it's probably a PageUp then
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000608 * scroll a whole page */
Bram Moolenaare3226be2005-12-18 22:10:00 +0000609 if (pum_first > pum_selected - 2)
610 {
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000611 pum_first -= pum_height - 2;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000612 if (pum_first < 0)
613 pum_first = 0;
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000614 else if (pum_first > pum_selected)
615 pum_first = pum_selected;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000616 }
617 else
618 pum_first = pum_selected;
619 }
620 else if (pum_first < pum_selected - pum_height + 5)
621 {
622 /* scroll up; when we did a jump it's probably a PageDown then
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000623 * scroll a whole page */
Bram Moolenaare3226be2005-12-18 22:10:00 +0000624 if (pum_first < pum_selected - pum_height + 1 + 2)
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000625 {
626 pum_first += pum_height - 2;
627 if (pum_first < pum_selected - pum_height + 1)
628 pum_first = pum_selected - pum_height + 1;
629 }
Bram Moolenaare3226be2005-12-18 22:10:00 +0000630 else
631 pum_first = pum_selected - pum_height + 1;
632 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000633
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000634 /* Give a few lines of context when possible. */
635 if (context > 3)
636 context = 3;
637 if (pum_height > 2)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000638 {
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000639 if (pum_first > pum_selected - context)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000640 {
641 /* scroll down */
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000642 pum_first = pum_selected - context;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000643 if (pum_first < 0)
644 pum_first = 0;
645 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000646 else if (pum_first < pum_selected + context - pum_height + 1)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000647 {
648 /* scroll up */
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000649 pum_first = pum_selected + context - pum_height + 1;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000650 }
651 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000652
Bram Moolenaar4033c552017-09-16 20:54:51 +0200653#if defined(FEAT_QUICKFIX)
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000654 /*
655 * Show extra info in the preview window if there is something and
656 * 'completeopt' contains "preview".
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000657 * Skip this when tried twice already.
658 * Skip this also when there is not much room.
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000659 * NOTE: Be very careful not to sync undo!
660 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000661 if (pum_array[pum_selected].pum_info != NULL
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000662 && Rows > 10
663 && repeat <= 1
664 && vim_strchr(p_cot, 'p') != NULL)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000665 {
666 win_T *curwin_save = curwin;
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200667 tabpage_T *curtab_save = curtab;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000668 int res = OK;
669
Bram Moolenaaree236d02010-10-27 17:11:15 +0200670 /* Open a preview window. 3 lines by default. Prefer
671 * 'previewheight' if set and smaller. */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000672 g_do_tagpreview = 3;
Bram Moolenaaree236d02010-10-27 17:11:15 +0200673 if (p_pvh > 0 && p_pvh < g_do_tagpreview)
674 g_do_tagpreview = p_pvh;
Bram Moolenaarc8045152014-07-09 19:58:24 +0200675 ++RedrawingDisabled;
Bram Moolenaare7d13762015-10-30 14:23:33 +0100676 /* Prevent undo sync here, if an autocommand syncs undo weird
677 * things can happen to the undo tree. */
678 ++no_u_sync;
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000679 resized = prepare_tagpreview(FALSE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100680 --no_u_sync;
Bram Moolenaarc8045152014-07-09 19:58:24 +0200681 --RedrawingDisabled;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000682 g_do_tagpreview = 0;
683
684 if (curwin->w_p_pvw)
685 {
Bram Moolenaar50e53762016-10-27 14:49:15 +0200686 if (!resized
687 && curbuf->b_nwindows == 1
688 && curbuf->b_fname == NULL
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000689 && curbuf->b_p_bt[0] == 'n' && curbuf->b_p_bt[2] == 'f'
690 && curbuf->b_p_bh[0] == 'w')
691 {
692 /* Already a "wipeout" buffer, make it empty. */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100693 while (!BUFEMPTY())
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000694 ml_delete((linenr_T)1, FALSE);
695 }
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000696 else
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000697 {
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000698 /* Don't want to sync undo in the current buffer. */
699 ++no_u_sync;
Bram Moolenaar701f7af2008-11-15 13:12:07 +0000700 res = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 0, NULL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000701 --no_u_sync;
702 if (res == OK)
703 {
704 /* Edit a new, empty buffer. Set options for a "wipeout"
705 * buffer. */
706 set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL);
707 set_option_value((char_u *)"bt", 0L,
708 (char_u *)"nofile", OPT_LOCAL);
709 set_option_value((char_u *)"bh", 0L,
710 (char_u *)"wipe", OPT_LOCAL);
711 set_option_value((char_u *)"diff", 0L,
Bram Moolenaar7f514742007-06-28 19:33:43 +0000712 NULL, OPT_LOCAL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000713 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000714 }
715 if (res == OK)
716 {
717 char_u *p, *e;
718 linenr_T lnum = 0;
719
720 for (p = pum_array[pum_selected].pum_info; *p != NUL; )
721 {
722 e = vim_strchr(p, '\n');
723 if (e == NULL)
724 {
725 ml_append(lnum++, p, 0, FALSE);
726 break;
727 }
728 else
729 {
730 *e = NUL;
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000731 ml_append(lnum++, p, (int)(e - p + 1), FALSE);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000732 *e = '\n';
733 p = e + 1;
734 }
735 }
736
737 /* Increase the height of the preview window to show the
738 * text, but no more than 'previewheight' lines. */
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000739 if (repeat == 0)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000740 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000741 if (lnum > p_pvh)
742 lnum = p_pvh;
743 if (curwin->w_height < lnum)
744 {
745 win_setheight((int)lnum);
746 resized = TRUE;
747 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000748 }
749
750 curbuf->b_changed = 0;
751 curbuf->b_p_ma = FALSE;
Bram Moolenaar9c27fc32010-03-17 14:48:24 +0100752 curwin->w_cursor.lnum = 1;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000753 curwin->w_cursor.col = 0;
754
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200755 if ((curwin != curwin_save && win_valid(curwin_save))
756 || (curtab != curtab_save
757 && valid_tabpage(curtab_save)))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000758 {
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200759 if (curtab != curtab_save && valid_tabpage(curtab_save))
760 goto_tabpage_tp(curtab_save, FALSE, FALSE);
761
Bram Moolenaar2bace3e2014-07-23 21:10:43 +0200762 /* When the first completion is done and the preview
763 * window is not resized, skip the preview window's
764 * status line redrawing. */
765 if (ins_compl_active() && !resized)
766 curwin->w_redr_status = FALSE;
767
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000768 /* Return cursor to where we were */
769 validate_cursor();
Bram Moolenaar600dddc2006-03-12 22:05:10 +0000770 redraw_later(SOME_VALID);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000771
772 /* When the preview window was resized we need to
773 * update the view on the buffer. Only go back to
774 * the window when needed, otherwise it will always be
775 * redraw. */
776 if (resized)
777 {
Bram Moolenaare7d13762015-10-30 14:23:33 +0100778 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000779 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100780 --no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000781 update_topline();
782 }
783
784 /* Update the screen before drawing the popup menu.
785 * Enable updating the status lines. */
786 pum_do_redraw = TRUE;
787 update_screen(0);
788 pum_do_redraw = FALSE;
789
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000790 if (!resized && win_valid(curwin_save))
Bram Moolenaare7d13762015-10-30 14:23:33 +0100791 {
792 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000793 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100794 --no_u_sync;
795 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000796
797 /* May need to update the screen again when there are
798 * autocommands involved. */
799 pum_do_redraw = TRUE;
800 update_screen(0);
801 pum_do_redraw = FALSE;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000802 }
803 }
804 }
805 }
806#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +0000807 }
808
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000809 if (!resized)
810 pum_redraw();
811
812 return resized;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000813}
814
815/*
816 * Undisplay the popup menu (later).
817 */
818 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100819pum_undisplay(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000820{
821 pum_array = NULL;
Bram Moolenaar0b565e52018-05-14 23:08:32 +0200822 redraw_all_later(NOT_VALID);
Bram Moolenaar2d694602006-08-22 19:48:48 +0000823 redraw_tabline = TRUE;
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +0000824 status_redraw_all();
Bram Moolenaar4b779472005-10-04 09:12:31 +0000825}
826
827/*
828 * Clear the popup menu. Currently only resets the offset to the first
829 * displayed item.
830 */
831 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100832pum_clear(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000833{
834 pum_first = 0;
835}
836
837/*
838 * Return TRUE if the popup menu is displayed.
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000839 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
Bram Moolenaar4b779472005-10-04 09:12:31 +0000840 */
841 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100842pum_visible(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000843{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000844 return !pum_do_redraw && pum_array != NULL;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000845}
846
Bram Moolenaare3226be2005-12-18 22:10:00 +0000847/*
Bram Moolenaar491ac282018-06-17 14:47:55 +0200848 * Reposition the popup menu to adjust for window layout changes.
849 */
850 void
851pum_may_redraw(void)
852{
853 pumitem_T *array = pum_array;
854 int len = pum_size;
855 int selected = pum_selected;
856
857 if (!pum_visible())
858 return; // nothing to do
859
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200860 if (pum_window != curwin
861 || (pum_win_row == curwin->w_wrow + W_WINROW(curwin)
862 && pum_win_height == curwin->w_height
863 && pum_win_col == curwin->w_wincol
864 && pum_win_width == curwin->w_width))
Bram Moolenaar491ac282018-06-17 14:47:55 +0200865 {
866 // window position didn't change, redraw in the same position
867 pum_redraw();
868 }
869 else
870 {
871 int wcol = curwin->w_wcol;
872
873 // Window layout changed, recompute the position.
874 // Use the remembered w_wcol value, the cursor may have moved when a
875 // completion was inserted, but we want the menu in the same position.
876 pum_undisplay();
877 curwin->w_wcol = pum_win_wcol;
878 curwin->w_valid |= VALID_WCOL;
879 pum_display(array, len, selected);
880 curwin->w_wcol = wcol;
881 }
882}
883
884/*
Bram Moolenaare3226be2005-12-18 22:10:00 +0000885 * Return the height of the popup menu, the number of entries visible.
886 * Only valid when pum_visible() returns TRUE!
887 */
888 int
Bram Moolenaar05540972016-01-30 20:31:25 +0100889pum_get_height(void)
Bram Moolenaare3226be2005-12-18 22:10:00 +0000890{
891 return pum_height;
892}
893
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100894# if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
895 static void
896pum_position_at_mouse(int min_width)
897{
898 if (Rows - mouse_row > pum_size)
899 {
900 /* Enough space below the mouse row. */
901 pum_row = mouse_row + 1;
902 if (pum_height > Rows - pum_row)
903 pum_height = Rows - pum_row;
904 }
905 else
906 {
907 /* Show above the mouse row, reduce height if it does not fit. */
908 pum_row = mouse_row - pum_size;
909 if (pum_row < 0)
910 {
911 pum_height += pum_row;
912 pum_row = 0;
913 }
914 }
915 if (Columns - mouse_col >= pum_base_width
916 || Columns - mouse_col > min_width)
917 /* Enough space to show at mouse column. */
918 pum_col = mouse_col;
919 else
920 /* Not enough space, right align with window. */
921 pum_col = Columns - (pum_base_width > min_width
922 ? min_width : pum_base_width);
923
924 pum_width = Columns - pum_col;
925 if (pum_width > pum_base_width + 1)
926 pum_width = pum_base_width + 1;
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200927
928 // Do not redraw at cursor position.
929 pum_window = NULL;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100930}
931
932# endif
933
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100934# if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100935static pumitem_T *balloon_array = NULL;
936static int balloon_arraysize;
937static int balloon_mouse_row = 0;
938static int balloon_mouse_col = 0;
939
Bram Moolenaar246fe032017-11-19 19:56:27 +0100940#define BALLOON_MIN_WIDTH 50
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100941#define BALLOON_MIN_HEIGHT 10
942
Bram Moolenaar246fe032017-11-19 19:56:27 +0100943typedef struct {
944 char_u *start;
945 int bytelen;
946 int cells;
947 int indent;
948} balpart_T;
949
950/*
951 * Split a string into parts to display in the balloon.
952 * Aimed at output from gdb. Attempts to split at white space, preserve quoted
953 * strings and make a struct look good.
954 * Resulting array is stored in "array" and returns the size of the array.
955 */
956 int
957split_message(char_u *mesg, pumitem_T **array)
958{
959 garray_T ga;
960 char_u *p;
961 balpart_T *item;
962 int quoted = FALSE;
963 int height;
964 int line;
965 int item_idx;
966 int indent = 0;
967 int max_cells = 0;
968 int max_height = Rows / 2 - 2;
969 int long_item_count = 0;
970 int split_long_items = FALSE;
971
972 ga_init2(&ga, sizeof(balpart_T), 20);
973 p = mesg;
974
975 while (*p != NUL)
976 {
977 if (ga_grow(&ga, 1) == FAIL)
978 goto failed;
979 item = ((balpart_T *)ga.ga_data) + ga.ga_len;
980 item->start = p;
981 item->indent = indent;
982 item->cells = indent * 2;
983 ++ga.ga_len;
984 while (*p != NUL)
985 {
986 if (*p == '"')
987 quoted = !quoted;
988 else if (*p == '\\' && p[1] != NUL)
989 ++p;
990 else if (!quoted)
991 {
992 if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}')
993 {
994 /* Looks like a good point to break. */
995 if (*p == '{')
996 ++indent;
997 else if (*p == '}' && indent > 0)
998 --indent;
999 ++item->cells;
1000 p = skipwhite(p + 1);
1001 break;
1002 }
1003 }
1004 item->cells += ptr2cells(p);
1005 p += MB_PTR2LEN(p);
1006 }
1007 item->bytelen = p - item->start;
1008 if (item->cells > max_cells)
1009 max_cells = item->cells;
Bram Moolenaara3571eb2017-11-26 16:53:16 +01001010 long_item_count += (item->cells - 1) / BALLOON_MIN_WIDTH;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001011 }
1012
1013 height = 2 + ga.ga_len;
1014
1015 /* If there are long items and the height is below the limit: split lines */
1016 if (long_item_count > 0 && height + long_item_count <= max_height)
1017 {
1018 split_long_items = TRUE;
1019 height += long_item_count;
1020 }
1021
1022 /* Limit to half the window height, it has to fit above or below the mouse
1023 * position. */
1024 if (height > max_height)
1025 height = max_height;
1026 *array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * height);
1027 if (*array == NULL)
1028 goto failed;
1029
1030 /* Add an empty line above and below, looks better. */
1031 (*array)->pum_text = vim_strsave((char_u *)"");
1032 (*array + height - 1)->pum_text = vim_strsave((char_u *)"");
1033
1034 for (line = 1, item_idx = 0; line < height - 1; ++item_idx)
1035 {
1036 int skip;
1037 int thislen;
1038 int copylen;
1039 int ind;
1040 int cells;
1041
1042 item = ((balpart_T *)ga.ga_data) + item_idx;
1043 for (skip = 0; skip < item->bytelen; skip += thislen)
1044 {
1045 if (split_long_items && item->cells >= BALLOON_MIN_WIDTH)
1046 {
1047 cells = item->indent * 2;
1048 for (p = item->start + skip; p < item->start + item->bytelen;
1049 p += MB_PTR2LEN(p))
1050 if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH)
1051 break;
1052 thislen = p - (item->start + skip);
1053 }
1054 else
1055 thislen = item->bytelen;
1056
1057 /* put indent at the start */
1058 p = alloc(thislen + item->indent * 2 + 1);
1059 for (ind = 0; ind < item->indent * 2; ++ind)
1060 p[ind] = ' ';
1061
1062 /* exclude spaces at the end of the string */
1063 for (copylen = thislen; copylen > 0; --copylen)
1064 if (item->start[skip + copylen - 1] != ' ')
1065 break;
1066
1067 vim_strncpy(p + ind, item->start + skip, copylen);
1068 (*array)[line].pum_text = p;
1069 item->indent = 0; /* wrapped line has no indent */
1070 ++line;
1071 }
1072 }
1073 ga_clear(&ga);
1074 return height;
1075
1076failed:
1077 ga_clear(&ga);
1078 return 0;
1079}
1080
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001081 void
1082ui_remove_balloon(void)
1083{
1084 if (balloon_array != NULL)
1085 {
1086 pum_undisplay();
1087 while (balloon_arraysize > 0)
1088 vim_free(balloon_array[--balloon_arraysize].pum_text);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001089 VIM_CLEAR(balloon_array);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001090 }
1091}
1092
1093/*
1094 * Terminal version of a balloon, uses the popup menu code.
1095 */
1096 void
Bram Moolenaar246fe032017-11-19 19:56:27 +01001097ui_post_balloon(char_u *mesg, list_T *list)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001098{
1099 ui_remove_balloon();
1100
Bram Moolenaar246fe032017-11-19 19:56:27 +01001101 if (mesg == NULL && list == NULL)
1102 return;
1103 if (list != NULL)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001104 {
Bram Moolenaar246fe032017-11-19 19:56:27 +01001105 listitem_T *li;
1106 int idx;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001107
Bram Moolenaar246fe032017-11-19 19:56:27 +01001108 balloon_arraysize = list->lv_len;
1109 balloon_array = (pumitem_T *)alloc_clear(
1110 (unsigned)sizeof(pumitem_T) * list->lv_len);
1111 if (balloon_array == NULL)
1112 return;
1113 for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx)
1114 {
1115 char_u *text = get_tv_string_chk(&li->li_tv);
1116
1117 balloon_array[idx].pum_text = vim_strsave(
1118 text == NULL ? (char_u *)"" : text);
1119 }
1120 }
1121 else
1122 balloon_arraysize = split_message(mesg, &balloon_array);
1123
1124 if (balloon_arraysize > 0)
1125 {
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001126 pum_array = balloon_array;
1127 pum_size = balloon_arraysize;
1128 pum_compute_size();
1129 pum_scrollbar = 0;
1130 pum_height = balloon_arraysize;
1131
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001132 pum_position_at_mouse(BALLOON_MIN_WIDTH);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001133 pum_selected = -1;
1134 pum_first = 0;
1135 pum_redraw();
1136 }
1137}
1138
1139/*
1140 * Called when the mouse moved, may remove any displayed balloon.
1141 */
1142 void
1143ui_may_remove_balloon(void)
1144{
1145 if (mouse_row != balloon_mouse_row || mouse_col != balloon_mouse_col)
1146 ui_remove_balloon();
1147}
1148# endif
Bram Moolenaara8f04aa2018-02-10 15:36:55 +01001149
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001150# if defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
1151/*
1152 * Select the pum entry at the mouse position.
1153 */
1154 static void
1155pum_select_mouse_pos(void)
1156{
1157 int idx = mouse_row - pum_row;
1158
1159 if (idx < 0 || idx >= pum_size)
1160 pum_selected = -1;
1161 else if (*pum_array[idx].pum_text != NUL)
1162 pum_selected = idx;
1163}
1164
1165/*
1166 * Execute the currently selected popup menu item.
1167 */
1168 static void
Bram Moolenaar987723e2018-03-06 11:43:04 +01001169pum_execute_menu(vimmenu_T *menu, int mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001170{
1171 vimmenu_T *mp;
1172 int idx = 0;
1173 exarg_T ea;
1174
1175 for (mp = menu->children; mp != NULL; mp = mp->next)
Bram Moolenaar987723e2018-03-06 11:43:04 +01001176 if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001177 {
1178 vim_memset(&ea, 0, sizeof(ea));
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001179 execute_menu(&ea, mp, -1);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001180 break;
1181 }
1182}
1183
1184/*
1185 * Open the terminal version of the popup menu and don't return until it is
1186 * closed.
1187 */
1188 void
1189pum_show_popupmenu(vimmenu_T *menu)
1190{
1191 vimmenu_T *mp;
1192 int idx = 0;
1193 pumitem_T *array;
1194#ifdef FEAT_BEVAL_TERM
1195 int save_bevalterm = p_bevalterm;
1196#endif
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001197 int mode;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001198
1199 pum_undisplay();
1200 pum_size = 0;
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001201 mode = get_menu_mode_flag();
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001202
1203 for (mp = menu->children; mp != NULL; mp = mp->next)
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001204 if (menu_is_separator(mp->dname)
1205 || (mp->modes & mp->enabled & mode))
1206 ++pum_size;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001207
1208 array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * pum_size);
1209 if (array == NULL)
1210 return;
1211
1212 for (mp = menu->children; mp != NULL; mp = mp->next)
1213 if (menu_is_separator(mp->dname))
1214 array[idx++].pum_text = (char_u *)"";
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001215 else if (mp->modes & mp->enabled & mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001216 array[idx++].pum_text = mp->dname;
1217
1218 pum_array = array;
1219 pum_compute_size();
1220 pum_scrollbar = 0;
1221 pum_height = pum_size;
1222 pum_position_at_mouse(20);
1223
1224 pum_selected = -1;
1225 pum_first = 0;
1226# ifdef FEAT_BEVAL_TERM
1227 p_bevalterm = TRUE; /* track mouse movement */
1228 mch_setmouse(TRUE);
1229# endif
1230
1231 for (;;)
1232 {
1233 int c;
1234
1235 pum_redraw();
Bram Moolenaar987723e2018-03-06 11:43:04 +01001236 setcursor_mayforce(TRUE);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001237 out_flush();
1238
1239 c = vgetc();
Bram Moolenaar52f18a12018-03-07 22:09:11 +01001240 if (c == ESC || c == Ctrl_C)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001241 break;
1242 else if (c == CAR || c == NL)
1243 {
1244 /* enter: select current item, if any, and close */
Bram Moolenaar987723e2018-03-06 11:43:04 +01001245 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001246 break;
1247 }
1248 else if (c == 'k' || c == K_UP || c == K_MOUSEUP)
1249 {
1250 /* cursor up: select previous item */
1251 while (pum_selected > 0)
1252 {
1253 --pum_selected;
1254 if (*array[pum_selected].pum_text != NUL)
1255 break;
1256 }
1257 }
1258 else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN)
1259 {
1260 /* cursor down: select next item */
1261 while (pum_selected < pum_size - 1)
1262 {
1263 ++pum_selected;
1264 if (*array[pum_selected].pum_text != NUL)
1265 break;
1266 }
1267 }
1268 else if (c == K_RIGHTMOUSE)
1269 {
1270 /* Right mouse down: reposition the menu. */
1271 vungetc(c);
1272 break;
1273 }
1274 else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE)
1275 {
Bram Moolenaar52f18a12018-03-07 22:09:11 +01001276 /* mouse moved: select item in the mouse row */
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001277 pum_select_mouse_pos();
1278 }
1279 else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE)
1280 {
1281 /* left mouse click: select clicked item, if any, and close;
1282 * right mouse release: select clicked item, close if any */
1283 pum_select_mouse_pos();
1284 if (pum_selected >= 0)
1285 {
Bram Moolenaar987723e2018-03-06 11:43:04 +01001286 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001287 break;
1288 }
1289 if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM)
1290 break;
1291 }
1292 }
1293
1294 vim_free(array);
1295 pum_undisplay();
1296# ifdef FEAT_BEVAL_TERM
1297 p_bevalterm = save_bevalterm;
1298 mch_setmouse(TRUE);
1299# endif
1300}
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001301
1302 void
1303pum_make_popup(char_u *path_name, int use_mouse_pos)
1304{
1305 vimmenu_T *menu;
1306
1307 if (!use_mouse_pos)
1308 {
1309 /* Hack: set mouse position at the cursor so that the menu pops up
1310 * around there. */
1311 mouse_row = curwin->w_winrow + curwin->w_wrow;
1312 mouse_col = curwin->w_wincol + curwin->w_wcol;
1313 }
1314
1315 menu = gui_find_menu(path_name);
1316 if (menu != NULL)
1317 pum_show_popupmenu(menu);
1318}
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001319# endif
1320
Bram Moolenaar4b779472005-10-04 09:12:31 +00001321#endif