blob: 09ed57427f6378a6ff1634195543034a5765fc6b [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 Moolenaar30e8e732019-09-27 13:08:36 +020011 * popupmenu.c: Popup menu (PUM)
Bram Moolenaar4b779472005-10-04 09:12:31 +000012 */
13#include "vim.h"
14
Bram Moolenaar63d9e732019-12-05 21:10:38 +010015static pumitem_T *pum_array = NULL; // items of displayed pum
16static int pum_size; // nr of items in "pum_array"
17static int pum_selected; // index of selected item or -1
18static int pum_first = 0; // index of top item
Bram Moolenaar4b779472005-10-04 09:12:31 +000019
Bram Moolenaarae654382019-01-17 21:09:05 +010020static int call_update_screen = FALSE;
Luuk van Baaldcd40cf2023-04-23 16:24:08 +010021static int pum_in_cmdline = FALSE;
Bram Moolenaarae654382019-01-17 21:09:05 +010022
Bram Moolenaar63d9e732019-12-05 21:10:38 +010023static int pum_height; // nr of displayed pum items
24static int pum_width; // width of displayed pum items
25static int pum_base_width; // width of pum items base
26static int pum_kind_width; // width of pum items kind column
27static int pum_extra_width; // width of extra stuff
28static int pum_scrollbar; // TRUE when scrollbar present
Bram Moolenaar4b779472005-10-04 09:12:31 +000029
Bram Moolenaar63d9e732019-12-05 21:10:38 +010030static int pum_row; // top row of pum
31static int pum_col; // left column of pum
Bram Moolenaar4b779472005-10-04 09:12:31 +000032
Bram Moolenaar0e6e1792018-06-17 17:10:59 +020033static win_T *pum_window = NULL;
Bram Moolenaar491ac282018-06-17 14:47:55 +020034static int pum_win_row;
35static int pum_win_height;
36static int pum_win_col;
37static int pum_win_wcol;
38static int pum_win_width;
39
Bram Moolenaar04357fb2019-12-10 21:50:56 +010040// Some parts are not updated when a popup menu is visible. Setting this flag
41// makes pum_visible() return FALSE even when there is a popup menu.
42static int pum_pretend_not_visible = FALSE;
43
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010044static int pum_set_selected(int n, int repeat);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000045
Bram Moolenaar4b779472005-10-04 09:12:31 +000046#define PUM_DEF_HEIGHT 10
Bram Moolenaar4b779472005-10-04 09:12:31 +000047
Bram Moolenaar51b0f372017-11-18 18:52:04 +010048 static void
49pum_compute_size(void)
50{
51 int i;
52 int w;
53
Bram Moolenaar63d9e732019-12-05 21:10:38 +010054 // Compute the width of the widest match and the widest extra.
Bram Moolenaar51b0f372017-11-18 18:52:04 +010055 pum_base_width = 0;
56 pum_kind_width = 0;
57 pum_extra_width = 0;
58 for (i = 0; i < pum_size; ++i)
59 {
Bram Moolenaard58a6622020-05-02 14:52:57 +020060 if (pum_array[i].pum_text != NULL)
61 {
62 w = vim_strsize(pum_array[i].pum_text);
63 if (pum_base_width < w)
64 pum_base_width = w;
65 }
Bram Moolenaar51b0f372017-11-18 18:52:04 +010066 if (pum_array[i].pum_kind != NULL)
67 {
68 w = vim_strsize(pum_array[i].pum_kind) + 1;
69 if (pum_kind_width < w)
70 pum_kind_width = w;
71 }
72 if (pum_array[i].pum_extra != NULL)
73 {
74 w = vim_strsize(pum_array[i].pum_extra) + 1;
75 if (pum_extra_width < w)
76 pum_extra_width = w;
77 }
78 }
79}
80
Bram Moolenaar4b779472005-10-04 09:12:31 +000081/*
82 * Show the popup menu with items "array[size]".
83 * "array" must remain valid until pum_undisplay() is called!
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010084 * When possible the leftmost character is aligned with cursor column.
Bram Moolenaar4b779472005-10-04 09:12:31 +000085 * The menu appears above the screen line "row" or at "row" + "height" - 1.
86 */
87 void
Bram Moolenaar05540972016-01-30 20:31:25 +010088pum_display(
89 pumitem_T *array,
90 int size,
Bram Moolenaar63d9e732019-12-05 21:10:38 +010091 int selected) // index of initially selected item, none if
92 // out of range
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000093{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +000094 int def_width;
95 int max_width;
Bram Moolenaarc1a11ed2008-06-24 22:09:24 +000096 int context_lines;
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +010097 int cursor_col;
Bram Moolenaar91e44a32016-11-04 20:08:52 +010098 int above_row;
99 int below_row;
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000100 int redo_count = 0;
Bram Moolenaar4033c552017-09-16 20:54:51 +0200101#if defined(FEAT_QUICKFIX)
Bram Moolenaar91e44a32016-11-04 20:08:52 +0100102 win_T *pvwin;
Bram Moolenaaraab33832016-11-04 22:08:29 +0100103#endif
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100104#ifdef FEAT_RIGHTLEFT
Bram Moolenaar24959102022-05-07 20:01:16 +0100105 int right_left = State == MODE_CMDLINE ? FALSE : curwin->w_p_rl;
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100106#endif
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000107
Bram Moolenaara5e66212017-09-29 22:42:33 +0200108 do
109 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100110 def_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200111 above_row = 0;
112 below_row = cmdline_row;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000113
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100114 // Pretend the pum is already there to avoid that must_redraw is set
115 // when 'cuc' is on.
Bram Moolenaara5e66212017-09-29 22:42:33 +0200116 pum_array = (pumitem_T *)1;
117 validate_cursor_col();
118 pum_array = NULL;
Bram Moolenaar1e607892006-03-13 22:15:53 +0000119
Bram Moolenaar491ac282018-06-17 14:47:55 +0200120 // Remember the essential parts of the window position and size, so we
121 // can decide when to reposition the popup menu.
Bram Moolenaar0e6e1792018-06-17 17:10:59 +0200122 pum_window = curwin;
Bram Moolenaar24959102022-05-07 20:01:16 +0100123 if (State == MODE_CMDLINE)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000124 // cmdline completion popup menu
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000125 pum_win_row = cmdline_row;
126 else
127 pum_win_row = curwin->w_wrow + W_WINROW(curwin);
Bram Moolenaar491ac282018-06-17 14:47:55 +0200128 pum_win_height = curwin->w_height;
129 pum_win_col = curwin->w_wincol;
130 pum_win_wcol = curwin->w_wcol;
131 pum_win_width = curwin->w_width;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000132
Bram Moolenaar4033c552017-09-16 20:54:51 +0200133#if defined(FEAT_QUICKFIX)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200134 FOR_ALL_WINDOWS(pvwin)
135 if (pvwin->w_p_pvw)
136 break;
137 if (pvwin != NULL)
138 {
139 if (W_WINROW(pvwin) < W_WINROW(curwin))
140 above_row = W_WINROW(pvwin) + pvwin->w_height;
141 else if (W_WINROW(pvwin) > W_WINROW(curwin) + curwin->w_height)
142 below_row = W_WINROW(pvwin);
143 }
Bram Moolenaar0106e3d2016-02-23 18:55:43 +0100144#endif
Bram Moolenaarefd2bf12006-03-16 21:41:35 +0000145
Bram Moolenaara5e66212017-09-29 22:42:33 +0200146 /*
147 * Figure out the size and position of the pum.
148 */
149 if (size < PUM_DEF_HEIGHT)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000150 pum_height = size;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000151 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200152 pum_height = PUM_DEF_HEIGHT;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000153 if (p_ph > 0 && pum_height > p_ph)
154 pum_height = p_ph;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000155
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100156 // Put the pum below "pum_win_row" if possible. If there are few lines
157 // decide on where there is more room.
Bram Moolenaar491ac282018-06-17 14:47:55 +0200158 if (pum_win_row + 2 >= below_row - pum_height
159 && pum_win_row - above_row > (below_row - above_row) / 2)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200160 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100161 // pum above "pum_win_row"
Bram Moolenaara5e66212017-09-29 22:42:33 +0200162
Bram Moolenaar24959102022-05-07 20:01:16 +0100163 if (State == MODE_CMDLINE)
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100164 // for cmdline pum, no need for context lines
165 context_lines = 0;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200166 else
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100167 {
168 // Leave two lines of context if possible
169 if (curwin->w_wrow - curwin->w_cline_row >= 2)
170 context_lines = 2;
171 else
172 context_lines = curwin->w_wrow - curwin->w_cline_row;
173 }
Bram Moolenaara5e66212017-09-29 22:42:33 +0200174
Bram Moolenaar491ac282018-06-17 14:47:55 +0200175 if (pum_win_row >= size + context_lines)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200176 {
Bram Moolenaar491ac282018-06-17 14:47:55 +0200177 pum_row = pum_win_row - size - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200178 pum_height = size;
179 }
180 else
181 {
182 pum_row = 0;
Bram Moolenaar491ac282018-06-17 14:47:55 +0200183 pum_height = pum_win_row - context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200184 }
185 if (p_ph > 0 && pum_height > p_ph)
186 {
187 pum_row += pum_height - p_ph;
188 pum_height = p_ph;
189 }
190 }
191 else
192 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100193 // pum below "pum_win_row"
Bram Moolenaara5e66212017-09-29 22:42:33 +0200194
Bram Moolenaar24959102022-05-07 20:01:16 +0100195 if (State == MODE_CMDLINE)
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100196 // for cmdline pum, no need for context lines
197 context_lines = 0;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200198 else
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100199 {
200 // Leave two lines of context if possible
201 validate_cheight();
202 if (curwin->w_cline_row
203 + curwin->w_cline_height - curwin->w_wrow >= 3)
204 context_lines = 3;
205 else
206 context_lines = curwin->w_cline_row
207 + curwin->w_cline_height - curwin->w_wrow;
208 }
Bram Moolenaara5e66212017-09-29 22:42:33 +0200209
Bram Moolenaar491ac282018-06-17 14:47:55 +0200210 pum_row = pum_win_row + context_lines;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200211 if (size > below_row - pum_row)
212 pum_height = below_row - pum_row;
213 else
214 pum_height = size;
215 if (p_ph > 0 && pum_height > p_ph)
216 pum_height = p_ph;
217 }
218
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100219 // don't display when we only have room for one line
Bram Moolenaara5e66212017-09-29 22:42:33 +0200220 if (pum_height < 1 || (pum_height == 1 && size > 1))
221 return;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000222
Bram Moolenaar4033c552017-09-16 20:54:51 +0200223#if defined(FEAT_QUICKFIX)
Bram Moolenaar614ab8a2018-12-01 11:59:00 +0100224 // If there is a preview window above avoid drawing over it.
225 if (pvwin != NULL && pum_row < above_row && pum_height > above_row)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000226 {
Bram Moolenaar614ab8a2018-12-01 11:59:00 +0100227 pum_row = above_row;
228 pum_height = pum_win_row - above_row;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000229 }
Bram Moolenaara5e66212017-09-29 22:42:33 +0200230#endif
231
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100232 pum_array = array;
233 pum_size = size;
234 pum_compute_size();
235 max_width = pum_base_width;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000236
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100237 // Calculate column
Bram Moolenaar24959102022-05-07 20:01:16 +0100238 if (State == MODE_CMDLINE)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000239 // cmdline completion popup menu
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000240 cursor_col = cmdline_compl_startcol();
241 else
Bram Moolenaar6ac2e432023-03-31 19:32:29 +0100242 {
243 // w_wcol includes virtual text "above"
244 int wcol = curwin->w_wcol % curwin->w_width;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000245#ifdef FEAT_RIGHTLEFT
Bram Moolenaar6ac2e432023-03-31 19:32:29 +0100246 if (right_left)
247 cursor_col = curwin->w_wincol + curwin->w_width - wcol - 1;
248 else
Bram Moolenaarabc97732007-08-08 20:49:37 +0000249#endif
Bram Moolenaar6ac2e432023-03-31 19:32:29 +0100250 cursor_col = curwin->w_wincol + wcol;
251 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000252
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100253 // if there are more items than room we need a scrollbar
Bram Moolenaara5e66212017-09-29 22:42:33 +0200254 if (pum_height < size)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000255 {
Bram Moolenaara5e66212017-09-29 22:42:33 +0200256 pum_scrollbar = 1;
257 ++max_width;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000258 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000259 else
Bram Moolenaara5e66212017-09-29 22:42:33 +0200260 pum_scrollbar = 0;
261
262 if (def_width < max_width)
263 def_width = max_width;
264
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100265 if (((cursor_col < Columns - p_pw || cursor_col < Columns - max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000266#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100267 && !right_left)
268 || (right_left && (cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000269#endif
Bram Moolenaara5e66212017-09-29 22:42:33 +0200270 ))
271 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100272 // align pum with "cursor_col"
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100273 pum_col = cursor_col;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000274
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100275 // start with the maximum space available
Bram Moolenaara5e66212017-09-29 22:42:33 +0200276#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100277 if (right_left)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200278 pum_width = pum_col - pum_scrollbar + 1;
279 else
280#endif
281 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000282
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100283 if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100284 && pum_width > p_pw)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200285 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100286 // the width is more than needed for the items, make it
287 // narrower
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100288 pum_width = max_width + pum_kind_width + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100289 if (pum_width < p_pw)
290 pum_width = p_pw;
Bram Moolenaara5e66212017-09-29 22:42:33 +0200291 }
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100292 else if (((cursor_col > p_pw || cursor_col > max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100293#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100294 && !right_left)
295 || (right_left && (cursor_col < Columns - p_pw
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100296 || cursor_col < Columns - max_width)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100297#endif
298 ))
299 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100300 // align pum edge with "cursor_col"
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100301#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100302 if (right_left
Bram Moolenaarbb008dd2018-02-24 18:59:55 +0100303 && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100304 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100305 pum_col = cursor_col + max_width + pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100306 if (pum_col >= Columns)
307 pum_col = Columns - 1;
308 }
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100309 else if (!right_left)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100310#endif
311 {
Bram Moolenaar2b10bcb2018-02-24 21:25:44 +0100312 if (curwin->w_wincol > Columns - max_width - pum_scrollbar
313 && max_width <= p_pw)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100314 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100315 // use full width to end of the screen
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100316 pum_col = Columns - max_width - pum_scrollbar;
317 if (pum_col < 0)
318 pum_col = 0;
319 }
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100320 }
321
322#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100323 if (right_left)
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100324 pum_width = pum_col - pum_scrollbar + 1;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100325 else
326#endif
Bram Moolenaar4287ed32018-02-17 20:35:29 +0100327 pum_width = Columns - pum_col - pum_scrollbar;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100328
Bram Moolenaar42443c72018-02-10 18:28:52 +0100329 if (pum_width < p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100330 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100331 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100332#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100333 if (right_left)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100334 {
335 if (pum_width > pum_col)
336 pum_width = pum_col;
337 }
338 else
339#endif
340 {
341 if (pum_width >= Columns - pum_col)
342 pum_width = Columns - pum_col - 1;
343 }
344 }
345 else if (pum_width > max_width + pum_kind_width
346 + pum_extra_width + 1
Bram Moolenaar42443c72018-02-10 18:28:52 +0100347 && pum_width > p_pw)
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100348 {
349 pum_width = max_width + pum_kind_width
350 + pum_extra_width + 1;
Bram Moolenaar42443c72018-02-10 18:28:52 +0100351 if (pum_width < p_pw)
352 pum_width = p_pw;
Bram Moolenaara8f04aa2018-02-10 15:36:55 +0100353 }
354 }
355
Bram Moolenaara5e66212017-09-29 22:42:33 +0200356 }
357 else if (Columns < def_width)
358 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100359 // not enough room, will use what we have
Bram Moolenaara5e66212017-09-29 22:42:33 +0200360#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100361 if (right_left)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200362 pum_col = Columns - 1;
363 else
364#endif
365 pum_col = 0;
366 pum_width = Columns - 1;
367 }
368 else
369 {
Bram Moolenaar42443c72018-02-10 18:28:52 +0100370 if (max_width > p_pw)
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100371 max_width = p_pw; // truncate
Bram Moolenaara5e66212017-09-29 22:42:33 +0200372#ifdef FEAT_RIGHTLEFT
Yegappan Lakshmanan1104a6d2022-03-31 12:34:15 +0100373 if (right_left)
Bram Moolenaara5e66212017-09-29 22:42:33 +0200374 pum_col = max_width - 1;
375 else
376#endif
377 pum_col = Columns - max_width;
378 pum_width = max_width - pum_scrollbar;
379 }
380
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100381 // Set selected item and redraw. If the window size changed need to
382 // redo the positioning. Limit this to two times, when there is not
383 // much room the window size will keep changing.
Bram Moolenaara5e66212017-09-29 22:42:33 +0200384 } while (pum_set_selected(selected, redo_count) && ++redo_count <= 2);
Bram Moolenaar714cbe52020-11-16 19:12:00 +0100385
386 pum_redraw();
Bram Moolenaar4b779472005-10-04 09:12:31 +0000387}
388
389/*
Bram Moolenaarae654382019-01-17 21:09:05 +0100390 * Set a flag that when pum_redraw() is called it first calls update_screen().
391 * This will avoid clearing and redrawing the popup menu, prevent flicker.
392 */
393 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000394pum_call_update_screen(void)
Bram Moolenaarae654382019-01-17 21:09:05 +0100395{
396 call_update_screen = TRUE;
397
398 // Update the cursor position to be able to compute the popup menu
399 // position. The cursor line length may have changed because of the
400 // inserted completion.
Bram Moolenaarc07ff5c2019-01-30 21:41:14 +0100401 curwin->w_valid &= ~(VALID_CROW|VALID_CHEIGHT);
Bram Moolenaarae654382019-01-17 21:09:05 +0100402 validate_cursor();
403}
404
405/*
406 * Return TRUE if we are going to redraw the popup menu and the screen position
407 * "row"/"col" is under the popup menu.
408 */
409 int
Bakudankun65555002021-11-17 20:40:16 +0000410pum_under_menu(int row, int col, int only_redrawing)
Bram Moolenaarae654382019-01-17 21:09:05 +0100411{
Bakudankun65555002021-11-17 20:40:16 +0000412 return (!only_redrawing || pum_will_redraw)
Bram Moolenaarae654382019-01-17 21:09:05 +0100413 && row >= pum_row
414 && row < pum_row + pum_height
415 && col >= pum_col - 1
Bram Moolenaare0c03c82021-04-23 21:01:34 +0200416 && col < pum_col + pum_width + pum_scrollbar;
Bram Moolenaarae654382019-01-17 21:09:05 +0100417}
418
419/*
glepnir40c1c332024-06-11 19:37:04 +0200420 * displays text on the popup menu with specific attributes.
421 */
422 static void
423pum_screen_put_with_attr(int row, int col, char_u *text, int textlen, int attr)
424{
425 int i;
426 int leader_len;
427 int char_len;
428 int cells;
429 int new_attr;
430 char_u *rt_leader = NULL;
431 char_u *match_leader = NULL;
432 char_u *ptr = text;
433 garray_T *ga = NULL;
434 char_u *leader = ins_compl_leader();
435 int in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0;
436
glepnir1c296022024-06-13 17:21:24 +0200437 if (leader == NULL || *leader == NUL ||
438 (highlight_attr[HLF_PMSI] == highlight_attr[HLF_PSI] &&
439 highlight_attr[HLF_PMNI] == highlight_attr[HLF_PNI]))
glepnir40c1c332024-06-11 19:37:04 +0200440 {
441 screen_puts_len(text, textlen, row, col, attr);
442 return;
443 }
444
445#ifdef FEAT_RIGHTLEFT
glepnir1c296022024-06-13 17:21:24 +0200446 if (curwin->w_p_rl)
glepnir40c1c332024-06-11 19:37:04 +0200447 rt_leader = reverse_text(leader);
448#endif
449 match_leader = rt_leader != NULL ? rt_leader : leader;
glepnir1c296022024-06-13 17:21:24 +0200450 leader_len = (int)STRLEN(match_leader);
glepnir40c1c332024-06-11 19:37:04 +0200451
glepnir1c296022024-06-13 17:21:24 +0200452 if (in_fuzzy)
glepnir40c1c332024-06-11 19:37:04 +0200453 ga = fuzzy_match_str_with_pos(text, match_leader);
454
455 // Render text with proper attributes
456 while (*ptr != NUL && ptr < text + textlen)
457 {
458 char_len = mb_ptr2len(ptr);
459 cells = mb_ptr2cells(ptr);
460 new_attr = attr;
461
462 if (ga != NULL)
463 {
464 // Handle fuzzy matching
465 for (i = 0; i < ga->ga_len; i++)
466 {
zeertzjq2f95ca92024-06-13 17:14:27 +0200467 int_u *match_pos = ((int_u *)ga->ga_data) + i;
468 int_u actual_char_pos = 0;
glepnir40c1c332024-06-11 19:37:04 +0200469 char_u *temp_ptr = text;
470 while (temp_ptr < ptr)
471 {
472 temp_ptr += mb_ptr2len(temp_ptr);
473 actual_char_pos++;
474 }
475 if (actual_char_pos == match_pos[0])
476 {
477 new_attr = highlight_attr[(attr == highlight_attr[HLF_PSI]
478 ? HLF_PMSI : HLF_PMNI)];
479 break;
480 }
481 }
482 }
483 else if (!in_fuzzy && (ptr - text < leader_len) &&
484 (STRNCMP(text, match_leader, leader_len) == 0))
485 new_attr = highlight_attr[(attr == highlight_attr[HLF_PSI]
486 ? HLF_PMSI : HLF_PMNI)];
487
488 screen_puts_len(ptr, char_len, row, col, new_attr);
489 col += cells;
490 ptr += char_len;
491 }
492
493 if (ga != NULL)
494 {
495 ga_clear(ga);
496 vim_free(ga);
497 }
498 if (rt_leader)
499 vim_free(rt_leader);
500}
501
502/*
Bram Moolenaar4b779472005-10-04 09:12:31 +0000503 * Redraw the popup menu, using "pum_first" and "pum_selected".
504 */
505 void
Bram Moolenaar05540972016-01-30 20:31:25 +0100506pum_redraw(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000507{
508 int row = pum_row;
509 int col;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000510 int attr_scroll = highlight_attr[HLF_PSB];
511 int attr_thumb = highlight_attr[HLF_PST];
512 int attr;
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000513 int *attrs; // array used for highlights
Bram Moolenaar4b779472005-10-04 09:12:31 +0000514 int i;
515 int idx;
516 char_u *s;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000517 char_u *p = NULL;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000518 int totwidth, width, w;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000519 int thumb_pos = 0;
Bram Moolenaarbdace832019-03-02 10:13:42 +0100520 int thumb_height = 1;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000521 int round;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000522 int n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000523
Bram Moolenaare638acc2023-03-15 17:08:51 +0000524 int attrsNorm[3];
525 int attrsSel[3];
526 // "word"
527 attrsNorm[0] = highlight_attr[HLF_PNI];
528 attrsSel[0] = highlight_attr[HLF_PSI];
529 // "kind"
530 attrsNorm[1] = highlight_attr[HLF_PNK];
531 attrsSel[1] = highlight_attr[HLF_PSK];
532 // "extra text"
533 attrsNorm[2] = highlight_attr[HLF_PNX];
534 attrsSel[2] = highlight_attr[HLF_PSX];
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000535
Bram Moolenaarae654382019-01-17 21:09:05 +0100536 if (call_update_screen)
537 {
538 call_update_screen = FALSE;
Bram Moolenaar04357fb2019-12-10 21:50:56 +0100539 // Do not redraw in pum_may_redraw() and don't draw in the area where
540 // the popup menu will be.
541 pum_will_redraw = TRUE;
Bram Moolenaarae654382019-01-17 21:09:05 +0100542 update_screen(0);
Bram Moolenaar04357fb2019-12-10 21:50:56 +0100543 pum_will_redraw = FALSE;
Bram Moolenaarae654382019-01-17 21:09:05 +0100544 }
545
546 // never display more than we have
Bram Moolenaar4c1e6262013-11-06 04:04:33 +0100547 if (pum_first > pum_size - pum_height)
548 pum_first = pum_size - pum_height;
549
Bram Moolenaar4b779472005-10-04 09:12:31 +0000550 if (pum_scrollbar)
551 {
Bram Moolenaarbdace832019-03-02 10:13:42 +0100552 thumb_height = pum_height * pum_height / pum_size;
553 if (thumb_height == 0)
554 thumb_height = 1;
555 thumb_pos = (pum_first * (pum_height - thumb_height)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000556 + (pum_size - pum_height) / 2)
557 / (pum_size - pum_height);
558 }
559
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100560#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +0200561 // The popup menu is drawn over popup menus with zindex under
562 // POPUPMENU_ZINDEX.
563 screen_zindex = POPUPMENU_ZINDEX;
564#endif
565
Bram Moolenaar4b779472005-10-04 09:12:31 +0000566 for (i = 0; i < pum_height; ++i)
567 {
568 idx = i + pum_first;
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000569 attrs = (idx == pum_selected) ? attrsSel : attrsNorm;
570 attr = attrs[0]; // start with "word" highlight
Bram Moolenaar4b779472005-10-04 09:12:31 +0000571
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100572 // prepend a space if there is room
Bram Moolenaarabc97732007-08-08 20:49:37 +0000573#ifdef FEAT_RIGHTLEFT
574 if (curwin->w_p_rl)
575 {
Bram Moolenaar02631462017-09-22 15:20:32 +0200576 if (pum_col < curwin->w_wincol + curwin->w_width - 1)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000577 screen_putchar(' ', row, pum_col + 1, attr);
578 }
579 else
580#endif
581 if (pum_col > 0)
582 screen_putchar(' ', row, pum_col - 1, attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000583
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100584 // Display each entry, use two spaces for a Tab.
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000585 // Do this 3 times:
586 // 0 - main text
587 // 1 - kind
588 // 2 - extra info
Bram Moolenaar4b779472005-10-04 09:12:31 +0000589 col = pum_col;
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000590 totwidth = 0;
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000591 for (round = 0; round < 3; ++round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000592 {
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000593 attr = attrs[round];
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000594 width = 0;
595 s = NULL;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000596 switch (round)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000597 {
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000598 case 0: p = pum_array[idx].pum_text; break;
599 case 1: p = pum_array[idx].pum_kind; break;
600 case 2: p = pum_array[idx].pum_extra; break;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000601 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000602 if (p != NULL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100603 for ( ; ; MB_PTR_ADV(p))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000604 {
605 if (s == NULL)
606 s = p;
607 w = ptr2cells(p);
608 if (*p == NUL || *p == TAB || totwidth + w > pum_width)
609 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100610 // Display the text that fits or comes before a Tab.
611 // First convert it to printable characters.
Bram Moolenaarabc97732007-08-08 20:49:37 +0000612 char_u *st;
613 int saved = *p;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000614
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100615 if (saved != NUL)
616 *p = NUL;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000617 st = transstr(s);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +0100618 if (saved != NUL)
619 *p = saved;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000620#ifdef FEAT_RIGHTLEFT
621 if (curwin->w_p_rl)
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000622 {
Bram Moolenaarabc97732007-08-08 20:49:37 +0000623 if (st != NULL)
624 {
625 char_u *rt = reverse_text(st);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000626
627 if (rt != NULL)
628 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100629 char_u *rt_start = rt;
630 int size;
631
632 size = vim_strsize(rt);
633 if (size > pum_width)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000634 {
Bram Moolenaard836bb92010-01-19 18:06:03 +0100635 do
636 {
637 size -= has_mbyte
638 ? (*mb_ptr2cells)(rt) : 1;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100639 MB_PTR_ADV(rt);
Bram Moolenaard836bb92010-01-19 18:06:03 +0100640 } while (size > pum_width);
641
642 if (size < pum_width)
643 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100644 // Most left character requires
645 // 2-cells but only 1 cell is
646 // available on screen. Put a
647 // '<' on the left of the pum
648 // item
Bram Moolenaard836bb92010-01-19 18:06:03 +0100649 *(--rt) = '<';
650 size++;
651 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000652 }
glepnir40c1c332024-06-11 19:37:04 +0200653 pum_screen_put_with_attr(row, col -size + 1, rt, (int)STRLEN(rt), attr);
Bram Moolenaard836bb92010-01-19 18:06:03 +0100654 vim_free(rt_start);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000655 }
656 vim_free(st);
657 }
658 col -= width;
Bram Moolenaar7cc36e92007-03-27 10:42:05 +0000659 }
Bram Moolenaarabc97732007-08-08 20:49:37 +0000660 else
661#endif
662 {
663 if (st != NULL)
664 {
Bram Moolenaar714cbe52020-11-16 19:12:00 +0100665 int size = (int)STRLEN(st);
666 int cells = (*mb_string2cells)(st, size);
667
668 // only draw the text that fits
669 while (size > 0
670 && col + cells > pum_width + pum_col)
671 {
672 --size;
673 if (has_mbyte)
674 {
675 size -= (*mb_head_off)(st, st + size);
676 cells -= (*mb_ptr2cells)(st + size);
677 }
678 else
679 --cells;
680 }
glepnir40c1c332024-06-11 19:37:04 +0200681 pum_screen_put_with_attr(row, col, st, size, attr);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000682 vim_free(st);
683 }
684 col += width;
685 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000686
687 if (*p != TAB)
688 break;
689
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100690 // Display two spaces for a Tab.
Bram Moolenaarabc97732007-08-08 20:49:37 +0000691#ifdef FEAT_RIGHTLEFT
692 if (curwin->w_p_rl)
693 {
694 screen_puts_len((char_u *)" ", 2, row, col - 1,
695 attr);
696 col -= 2;
697 }
698 else
699#endif
700 {
701 screen_puts_len((char_u *)" ", 2, row, col, attr);
702 col += 2;
703 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000704 totwidth += 2;
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100705 s = NULL; // start text at next char
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000706 width = 0;
707 }
708 else
709 width += w;
710 }
711
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000712 if (round > 0)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000713 n = pum_kind_width + 1;
714 else
715 n = 1;
716
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100717 // Stop when there is nothing more to display.
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000718 if (round == 2
719 || (round == 1 && pum_array[idx].pum_extra == NULL)
720 || (round == 0 && pum_array[idx].pum_kind == NULL
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000721 && pum_array[idx].pum_extra == NULL)
722 || pum_base_width + n >= pum_width)
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000723 break;
Bram Moolenaarabc97732007-08-08 20:49:37 +0000724#ifdef FEAT_RIGHTLEFT
725 if (curwin->w_p_rl)
726 {
727 screen_fill(row, row + 1, pum_col - pum_base_width - n + 1,
728 col + 1, ' ', ' ', attr);
729 col = pum_col - pum_base_width - n + 1;
730 }
731 else
732#endif
733 {
734 screen_fill(row, row + 1, col, pum_col + pum_base_width + n,
Bram Moolenaar8b6144b2006-02-08 09:20:24 +0000735 ' ', ' ', attr);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000736 col = pum_col + pum_base_width + n;
737 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000738 totwidth = pum_base_width + n;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000739 }
740
Bram Moolenaarabc97732007-08-08 20:49:37 +0000741#ifdef FEAT_RIGHTLEFT
742 if (curwin->w_p_rl)
743 screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ',
744 ' ', attr);
745 else
746#endif
747 screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ',
748 attr);
Bram Moolenaar4b779472005-10-04 09:12:31 +0000749 if (pum_scrollbar > 0)
Bram Moolenaarabc97732007-08-08 20:49:37 +0000750 {
751#ifdef FEAT_RIGHTLEFT
752 if (curwin->w_p_rl)
753 screen_putchar(' ', row, pum_col - pum_width,
Bram Moolenaarbdace832019-03-02 10:13:42 +0100754 i >= thumb_pos && i < thumb_pos + thumb_height
Bram Moolenaar4b779472005-10-04 09:12:31 +0000755 ? attr_thumb : attr_scroll);
Bram Moolenaarabc97732007-08-08 20:49:37 +0000756 else
757#endif
758 screen_putchar(' ', row, pum_col + pum_width,
Bram Moolenaarbdace832019-03-02 10:13:42 +0100759 i >= thumb_pos && i < thumb_pos + thumb_height
Bram Moolenaarabc97732007-08-08 20:49:37 +0000760 ? attr_thumb : attr_scroll);
761 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000762
763 ++row;
764 }
Bram Moolenaar33796b32019-06-08 16:01:13 +0200765
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100766#ifdef FEAT_PROP_POPUP
Bram Moolenaar33796b32019-06-08 16:01:13 +0200767 screen_zindex = 0;
768#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +0000769}
770
Bram Moolenaar5a4c3082019-12-01 15:23:11 +0100771#if (defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)) || defined(PROTO)
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200772/*
773 * Position the info popup relative to the popup menu item.
774 */
775 void
776pum_position_info_popup(win_T *wp)
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200777{
Bram Moolenaar202c3f72019-11-21 12:12:35 +0100778 int col = pum_col + pum_width + pum_scrollbar + 1;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200779 int row = pum_row;
780 int botpos = POPPOS_BOTLEFT;
Bram Moolenaarde2396f2020-07-18 21:40:41 +0200781 int used_maxwidth_opt = FALSE;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200782
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200783 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200784 if (Columns - col < 20 && Columns - col < pum_col)
785 {
786 col = pum_col - 1;
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200787 wp->w_popup_pos = POPPOS_TOPRIGHT;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200788 botpos = POPPOS_BOTRIGHT;
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200789 wp->w_maxwidth = pum_col - 1;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200790 }
791 else
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200792 wp->w_maxwidth = Columns - col + 1;
793 wp->w_maxwidth -= popup_extra_width(wp);
Bram Moolenaarde2396f2020-07-18 21:40:41 +0200794 if (wp->w_maxwidth_opt > 0 && wp->w_maxwidth > wp->w_maxwidth_opt)
795 {
796 // option value overrules computed value
797 wp->w_maxwidth = wp->w_maxwidth_opt;
798 used_maxwidth_opt = TRUE;
799 }
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200800
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200801 row -= popup_top_extra(wp);
802 if (wp->w_popup_flags & POPF_INFO_MENU)
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200803 {
804 if (pum_row < pum_win_row)
805 {
806 // menu above cursor line, align with bottom
807 row += pum_height;
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200808 wp->w_popup_pos = botpos;
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200809 }
810 else
811 // menu below cursor line, align with top
812 row += 1;
813 }
814 else
815 // align with the selected item
816 row += pum_selected - pum_first + 1;
817
Bram Moolenaarbef93ac2019-12-06 20:17:35 +0100818 wp->w_popup_flags &= ~POPF_HIDDEN;
Bram Moolenaarde2396f2020-07-18 21:40:41 +0200819 if (wp->w_maxwidth < 10 && !used_maxwidth_opt)
Bram Moolenaarbef93ac2019-12-06 20:17:35 +0100820 // The popup is not going to fit or will overlap with the cursor
821 // position, hide the popup.
822 wp->w_popup_flags |= POPF_HIDDEN;
823 else
824 popup_set_wantpos_rowcol(wp, row, col);
Bram Moolenaarc1f87c92019-08-21 19:33:16 +0200825}
826#endif
827
Bram Moolenaar4b779472005-10-04 09:12:31 +0000828/*
829 * Set the index of the currently selected item. The menu will scroll when
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000830 * necessary. When "n" is out of range don't scroll.
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000831 * This may be repeated when the preview window is used:
832 * "repeat" == 0: open preview window normally
833 * "repeat" == 1: open preview window but don't set the size
834 * "repeat" == 2: don't open preview window
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000835 * Returns TRUE when the window was resized and the location of the popup menu
836 * must be recomputed.
Bram Moolenaar4b779472005-10-04 09:12:31 +0000837 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000838 static int
Bram Moolenaar9cb698d2019-08-21 15:30:45 +0200839pum_set_selected(int n, int repeat UNUSED)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000840{
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000841 int resized = FALSE;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000842 int context = pum_height / 2;
Bram Moolenaareaf35242019-08-20 23:14:15 +0200843#ifdef FEAT_QUICKFIX
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +0200844 int prev_selected = pum_selected;
zeertzjq529b9ad2024-06-05 20:27:06 +0200845 unsigned cur_cot_flags = get_cot_flags();
Bram Moolenaareaf35242019-08-20 23:14:15 +0200846#endif
Bram Moolenaar5a4c3082019-12-01 15:23:11 +0100847#if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200848 int has_info = FALSE;
849#endif
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000850
Bram Moolenaar4b779472005-10-04 09:12:31 +0000851 pum_selected = n;
852
Bram Moolenaar1f35bf92006-03-07 22:38:47 +0000853 if (pum_selected >= 0 && pum_selected < pum_size)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000854 {
Bram Moolenaare3226be2005-12-18 22:10:00 +0000855 if (pum_first > pum_selected - 4)
856 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100857 // scroll down; when we did a jump it's probably a PageUp then
858 // scroll a whole page
Bram Moolenaare3226be2005-12-18 22:10:00 +0000859 if (pum_first > pum_selected - 2)
860 {
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000861 pum_first -= pum_height - 2;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000862 if (pum_first < 0)
863 pum_first = 0;
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000864 else if (pum_first > pum_selected)
865 pum_first = pum_selected;
Bram Moolenaare3226be2005-12-18 22:10:00 +0000866 }
867 else
868 pum_first = pum_selected;
869 }
870 else if (pum_first < pum_selected - pum_height + 5)
871 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100872 // scroll up; when we did a jump it's probably a PageDown then
873 // scroll a whole page
Bram Moolenaare3226be2005-12-18 22:10:00 +0000874 if (pum_first < pum_selected - pum_height + 1 + 2)
Bram Moolenaar7df351e2006-01-23 22:30:28 +0000875 {
876 pum_first += pum_height - 2;
877 if (pum_first < pum_selected - pum_height + 1)
878 pum_first = pum_selected - pum_height + 1;
879 }
Bram Moolenaare3226be2005-12-18 22:10:00 +0000880 else
881 pum_first = pum_selected - pum_height + 1;
882 }
Bram Moolenaar4b779472005-10-04 09:12:31 +0000883
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100884 // Give a few lines of context when possible.
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000885 if (context > 3)
886 context = 3;
887 if (pum_height > 2)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000888 {
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000889 if (pum_first > pum_selected - context)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000890 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100891 // scroll down
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000892 pum_first = pum_selected - context;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000893 if (pum_first < 0)
894 pum_first = 0;
895 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000896 else if (pum_first < pum_selected + context - pum_height + 1)
Bram Moolenaar4b779472005-10-04 09:12:31 +0000897 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100898 // scroll up
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +0000899 pum_first = pum_selected + context - pum_height + 1;
Bram Moolenaar4b779472005-10-04 09:12:31 +0000900 }
901 }
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200902 // adjust for the number of lines displayed
903 if (pum_first > pum_size - pum_height)
904 pum_first = pum_size - pum_height;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000905
Bram Moolenaar4033c552017-09-16 20:54:51 +0200906#if defined(FEAT_QUICKFIX)
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000907 /*
908 * Show extra info in the preview window if there is something and
Bram Moolenaar202c3f72019-11-21 12:12:35 +0100909 * 'completeopt' contains "preview" or "popup" or "popuphidden".
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000910 * Skip this when tried twice already.
911 * Skip this also when there is not much room.
Bram Moolenaard2cec5b2006-03-28 21:08:56 +0000912 * NOTE: Be very careful not to sync undo!
913 */
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000914 if (pum_array[pum_selected].pum_info != NULL
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000915 && Rows > 10
916 && repeat <= 1
zeertzjq529b9ad2024-06-05 20:27:06 +0200917 && (cur_cot_flags & COT_ANY_PREVIEW))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000918 {
919 win_T *curwin_save = curwin;
Bram Moolenaar9ad89c62017-10-26 22:04:04 +0200920 tabpage_T *curtab_save = curtab;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100921# ifdef FEAT_PROP_POPUP
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200922 use_popup_T use_popup;
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200923# else
Bram Moolenaara2c2ae42019-11-30 20:58:46 +0100924# define use_popup USEPOPUP_NONE
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200925# endif
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100926# ifdef FEAT_PROP_POPUP
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200927 has_info = TRUE;
zeertzjq529b9ad2024-06-05 20:27:06 +0200928 if (cur_cot_flags & COT_POPUPHIDDEN)
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200929 use_popup = USEPOPUP_HIDDEN;
zeertzjq529b9ad2024-06-05 20:27:06 +0200930 else if (cur_cot_flags & COT_POPUP)
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200931 use_popup = USEPOPUP_NORMAL;
932 else
933 use_popup = USEPOPUP_NONE;
Bram Moolenaar2dfae042020-11-15 14:09:37 +0100934 if (use_popup != USEPOPUP_NONE)
935 // don't use WinEnter or WinLeave autocommands for the info
936 // popup
937 block_autocmds();
Bram Moolenaard570ab92019-09-03 23:20:05 +0200938# endif
Bram Moolenaardca7abe2019-10-20 18:17:57 +0200939 // Open a preview window and set "curwin" to it.
940 // 3 lines by default, prefer 'previewheight' if set and smaller.
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000941 g_do_tagpreview = 3;
Bram Moolenaaree236d02010-10-27 17:11:15 +0200942 if (p_pvh > 0 && p_pvh < g_do_tagpreview)
943 g_do_tagpreview = p_pvh;
Bram Moolenaarc8045152014-07-09 19:58:24 +0200944 ++RedrawingDisabled;
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200945 // Prevent undo sync here, if an autocommand syncs undo weird
946 // things can happen to the undo tree.
Bram Moolenaare7d13762015-10-30 14:23:33 +0100947 ++no_u_sync;
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200948 resized = prepare_tagpreview(FALSE, FALSE, use_popup);
Bram Moolenaare7d13762015-10-30 14:23:33 +0100949 --no_u_sync;
Bram Moolenaar79cdf022023-05-20 14:07:00 +0100950 if (RedrawingDisabled > 0)
951 --RedrawingDisabled;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000952 g_do_tagpreview = 0;
953
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200954 if (curwin->w_p_pvw
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100955# ifdef FEAT_PROP_POPUP
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200956 || (curwin->w_popup_flags & POPF_INFO)
957# endif
958 )
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000959 {
mathew20f61d92023-08-26 18:11:02 +0200960 int res = OK;
961
Bram Moolenaar50e53762016-10-27 14:49:15 +0200962 if (!resized
963 && curbuf->b_nwindows == 1
964 && curbuf->b_fname == NULL
Bram Moolenaar26910de2019-06-15 19:37:15 +0200965 && bt_nofile(curbuf)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000966 && curbuf->b_p_bh[0] == 'w')
967 {
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200968 // Already a "wipeout" buffer, make it empty.
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100969 while (!BUFEMPTY())
Bram Moolenaarca70c072020-05-30 20:30:46 +0200970 ml_delete((linenr_T)1);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000971 }
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000972 else
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000973 {
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200974 // Don't want to sync undo in the current buffer.
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000975 ++no_u_sync;
Bram Moolenaar701f7af2008-11-15 13:12:07 +0000976 res = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 0, NULL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000977 --no_u_sync;
978 if (res == OK)
979 {
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200980 // Edit a new, empty buffer. Set options for a "wipeout"
981 // buffer.
Bram Moolenaar31e5c602022-04-15 13:53:33 +0100982 set_option_value_give_err((char_u *)"swf",
983 0L, NULL, OPT_LOCAL);
984 set_option_value_give_err((char_u *)"bl",
985 0L, NULL, OPT_LOCAL);
986 set_option_value_give_err((char_u *)"bt",
987 0L, (char_u *)"nofile", OPT_LOCAL);
988 set_option_value_give_err((char_u *)"bh",
989 0L, (char_u *)"wipe", OPT_LOCAL);
990 set_option_value_give_err((char_u *)"diff",
991 0L, NULL, OPT_LOCAL);
Bram Moolenaar779b74b2006-04-10 14:55:34 +0000992 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +0000993 }
994 if (res == OK)
995 {
996 char_u *p, *e;
997 linenr_T lnum = 0;
998
999 for (p = pum_array[pum_selected].pum_info; *p != NUL; )
1000 {
1001 e = vim_strchr(p, '\n');
1002 if (e == NULL)
1003 {
1004 ml_append(lnum++, p, 0, FALSE);
1005 break;
1006 }
mathewc51fa7b2023-08-23 20:55:17 +02001007 *e = NUL;
1008 ml_append(lnum++, p, (int)(e - p + 1), FALSE);
1009 *e = '\n';
1010 p = e + 1;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001011 }
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001012 // delete the empty last line
Bram Moolenaarca70c072020-05-30 20:30:46 +02001013 ml_delete(curbuf->b_ml.ml_line_count);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001014
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001015 // Increase the height of the preview window to show the
1016 // text, but no more than 'previewheight' lines.
Bram Moolenaardca7abe2019-10-20 18:17:57 +02001017 if (repeat == 0 && use_popup == USEPOPUP_NONE)
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001018 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00001019 if (lnum > p_pvh)
1020 lnum = p_pvh;
1021 if (curwin->w_height < lnum)
1022 {
1023 win_setheight((int)lnum);
1024 resized = TRUE;
1025 }
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001026 }
1027
1028 curbuf->b_changed = 0;
1029 curbuf->b_p_ma = FALSE;
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +02001030 if (pum_selected != prev_selected)
1031 {
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001032# ifdef FEAT_PROP_POPUP
Bram Moolenaarf0bc15c2019-08-18 19:23:45 +02001033 curwin->w_firstline = 1;
1034# endif
1035 curwin->w_topline = 1;
1036 }
1037 else if (curwin->w_topline > curbuf->b_ml.ml_line_count)
1038 curwin->w_topline = curbuf->b_ml.ml_line_count;
1039 curwin->w_cursor.lnum = curwin->w_topline;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001040 curwin->w_cursor.col = 0;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001041# ifdef FEAT_PROP_POPUP
Bram Moolenaardca7abe2019-10-20 18:17:57 +02001042 if (use_popup != USEPOPUP_NONE)
Bram Moolenaarc1f87c92019-08-21 19:33:16 +02001043 {
Bram Moolenaardca7abe2019-10-20 18:17:57 +02001044 pum_position_info_popup(curwin);
Bram Moolenaarc1f87c92019-08-21 19:33:16 +02001045 if (win_valid(curwin_save))
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001046 redraw_win_later(curwin_save, UPD_SOME_VALID);
Bram Moolenaarc1f87c92019-08-21 19:33:16 +02001047 }
1048# endif
Bram Moolenaar9ad89c62017-10-26 22:04:04 +02001049 if ((curwin != curwin_save && win_valid(curwin_save))
1050 || (curtab != curtab_save
1051 && valid_tabpage(curtab_save)))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001052 {
Bram Moolenaarb80d2fb2021-04-27 20:06:57 +02001053 int save_redr_status;
1054
Bram Moolenaar9ad89c62017-10-26 22:04:04 +02001055 if (curtab != curtab_save && valid_tabpage(curtab_save))
1056 goto_tabpage_tp(curtab_save, FALSE, FALSE);
1057
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001058 // When the first completion is done and the preview
1059 // window is not resized, skip the preview window's
1060 // status line redrawing.
Bram Moolenaar2bace3e2014-07-23 21:10:43 +02001061 if (ins_compl_active() && !resized)
1062 curwin->w_redr_status = FALSE;
1063
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001064 // Return cursor to where we were
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001065 validate_cursor();
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001066 redraw_later(UPD_SOME_VALID);
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001067
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001068 // When the preview window was resized we need to
1069 // update the view on the buffer. Only go back to
1070 // the window when needed, otherwise it will always be
Bram Moolenaar5e5a98d2019-12-15 14:55:33 +01001071 // redrawn.
Bram Moolenaar5dd143e2019-08-15 21:34:34 +02001072 if (resized && win_valid(curwin_save))
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001073 {
Bram Moolenaare7d13762015-10-30 14:23:33 +01001074 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001075 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +01001076 --no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001077 update_topline();
1078 }
1079
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001080 // Update the screen before drawing the popup menu.
1081 // Enable updating the status lines.
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001082 pum_pretend_not_visible = TRUE;
Bram Moolenaarb80d2fb2021-04-27 20:06:57 +02001083
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001084 // But don't draw text at the new popup menu position,
Bram Moolenaar5e5a98d2019-12-15 14:55:33 +01001085 // it causes flicker. When resizing we need to draw
1086 // anyway, the position may change later.
Bram Moolenaarb80d2fb2021-04-27 20:06:57 +02001087 // Also do not redraw the status line of the original
1088 // current window here, to avoid it gets drawn with
1089 // StatusLineNC for a moment and cause flicker.
Bram Moolenaar5e5a98d2019-12-15 14:55:33 +01001090 pum_will_redraw = !resized;
Bram Moolenaarb80d2fb2021-04-27 20:06:57 +02001091 save_redr_status = curwin_save->w_redr_status;
1092 curwin_save->w_redr_status = FALSE;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001093 update_screen(0);
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001094 pum_pretend_not_visible = FALSE;
1095 pum_will_redraw = FALSE;
Bram Moolenaarb80d2fb2021-04-27 20:06:57 +02001096 curwin_save->w_redr_status = save_redr_status;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001097
Bram Moolenaard2cec5b2006-03-28 21:08:56 +00001098 if (!resized && win_valid(curwin_save))
Bram Moolenaare7d13762015-10-30 14:23:33 +01001099 {
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001100# ifdef FEAT_PROP_POPUP
Bram Moolenaardca7abe2019-10-20 18:17:57 +02001101 win_T *wp = curwin;
1102# endif
Bram Moolenaare7d13762015-10-30 14:23:33 +01001103 ++no_u_sync;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001104 win_enter(curwin_save, TRUE);
Bram Moolenaare7d13762015-10-30 14:23:33 +01001105 --no_u_sync;
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001106# ifdef FEAT_PROP_POPUP
Bram Moolenaardca7abe2019-10-20 18:17:57 +02001107 if (use_popup == USEPOPUP_HIDDEN && win_valid(wp))
1108 popup_hide(wp);
1109# endif
Bram Moolenaare7d13762015-10-30 14:23:33 +01001110 }
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001111
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001112 // May need to update the screen again when there are
1113 // autocommands involved.
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001114 pum_pretend_not_visible = TRUE;
Bram Moolenaar5e5a98d2019-12-15 14:55:33 +01001115 pum_will_redraw = !resized;
Bram Moolenaar7d47b6e2006-03-15 22:59:18 +00001116 update_screen(0);
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001117 pum_pretend_not_visible = FALSE;
1118 pum_will_redraw = FALSE;
Bram Moolenaarae654382019-01-17 21:09:05 +01001119 call_update_screen = FALSE;
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001120 }
1121 }
1122 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001123# if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
Bram Moolenaar5dd143e2019-08-15 21:34:34 +02001124 if (WIN_IS_POPUP(curwin))
1125 // can't keep focus in a popup window
1126 win_enter(firstwin, TRUE);
1127# endif
Bram Moolenaar2dfae042020-11-15 14:09:37 +01001128# ifdef FEAT_PROP_POPUP
1129 if (use_popup != USEPOPUP_NONE)
1130 unblock_autocmds();
1131# endif
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001132 }
1133#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +00001134 }
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001135#if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001136 if (!has_info)
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02001137 // hide any popup info window
1138 popup_hide_info();
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001139#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +00001140
Bram Moolenaar96d2c5b2006-03-11 21:27:59 +00001141 return resized;
Bram Moolenaar4b779472005-10-04 09:12:31 +00001142}
1143
1144/*
1145 * Undisplay the popup menu (later).
1146 */
1147 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001148pum_undisplay(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +00001149{
1150 pum_array = NULL;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001151 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaar2d694602006-08-22 19:48:48 +00001152 redraw_tabline = TRUE;
Luuk van Baaldcd40cf2023-04-23 16:24:08 +01001153 if (pum_in_cmdline)
1154 {
1155 clear_cmdline = TRUE;
1156 pum_in_cmdline = FALSE;
1157 }
Bram Moolenaar1f4d4de2006-03-14 23:00:46 +00001158 status_redraw_all();
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01001159#if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02001160 // hide any popup info window
1161 popup_hide_info();
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001162#endif
Bram Moolenaar4b779472005-10-04 09:12:31 +00001163}
1164
1165/*
1166 * Clear the popup menu. Currently only resets the offset to the first
1167 * displayed item.
1168 */
1169 void
Bram Moolenaar05540972016-01-30 20:31:25 +01001170pum_clear(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +00001171{
1172 pum_first = 0;
1173}
1174
1175/*
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001176 * Return TRUE if the popup menu is displayed. Used to avoid some redrawing
1177 * that could overwrite it. Overruled when "pum_pretend_not_visible" is set,
1178 * used to redraw the status lines.
Bram Moolenaar4b779472005-10-04 09:12:31 +00001179 */
1180 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001181pum_visible(void)
Bram Moolenaar4b779472005-10-04 09:12:31 +00001182{
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001183 return !pum_pretend_not_visible && pum_array != NULL;
Bram Moolenaar4b779472005-10-04 09:12:31 +00001184}
1185
Bram Moolenaare3226be2005-12-18 22:10:00 +00001186/*
Bram Moolenaare0c03c82021-04-23 21:01:34 +02001187 * Return TRUE if the popup can be redrawn in the same position.
1188 */
1189 static int
1190pum_in_same_position(void)
1191{
1192 return pum_window != curwin
1193 || (pum_win_row == curwin->w_wrow + W_WINROW(curwin)
1194 && pum_win_height == curwin->w_height
1195 && pum_win_col == curwin->w_wincol
1196 && pum_win_width == curwin->w_width);
1197}
1198
1199/*
1200 * Return TRUE when pum_may_redraw() will call pum_redraw().
1201 * This means that the pum area should not be overwritten to avoid flicker.
1202 */
1203 int
1204pum_redraw_in_same_position(void)
1205{
1206 if (!pum_visible() || pum_will_redraw)
1207 return FALSE; // nothing to do
1208
1209 return pum_in_same_position();
1210}
1211
1212/*
Bram Moolenaar491ac282018-06-17 14:47:55 +02001213 * Reposition the popup menu to adjust for window layout changes.
1214 */
1215 void
1216pum_may_redraw(void)
1217{
1218 pumitem_T *array = pum_array;
1219 int len = pum_size;
1220 int selected = pum_selected;
1221
Bram Moolenaar04357fb2019-12-10 21:50:56 +01001222 if (!pum_visible() || pum_will_redraw)
Bram Moolenaar491ac282018-06-17 14:47:55 +02001223 return; // nothing to do
1224
Bram Moolenaare0c03c82021-04-23 21:01:34 +02001225 if (pum_in_same_position())
Bram Moolenaar491ac282018-06-17 14:47:55 +02001226 {
1227 // window position didn't change, redraw in the same position
1228 pum_redraw();
1229 }
1230 else
1231 {
1232 int wcol = curwin->w_wcol;
1233
1234 // Window layout changed, recompute the position.
1235 // Use the remembered w_wcol value, the cursor may have moved when a
1236 // completion was inserted, but we want the menu in the same position.
1237 pum_undisplay();
1238 curwin->w_wcol = pum_win_wcol;
1239 curwin->w_valid |= VALID_WCOL;
1240 pum_display(array, len, selected);
1241 curwin->w_wcol = wcol;
1242 }
1243}
1244
1245/*
Bram Moolenaare3226be2005-12-18 22:10:00 +00001246 * Return the height of the popup menu, the number of entries visible.
1247 * Only valid when pum_visible() returns TRUE!
1248 */
1249 int
Bram Moolenaar05540972016-01-30 20:31:25 +01001250pum_get_height(void)
Bram Moolenaare3226be2005-12-18 22:10:00 +00001251{
1252 return pum_height;
1253}
1254
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001255#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001256/*
1257 * Add size information about the pum to "dict".
1258 */
1259 void
1260pum_set_event_info(dict_T *dict)
1261{
1262 if (!pum_visible())
1263 return;
Bram Moolenaarc0300632020-03-16 20:07:16 +01001264 (void)dict_add_number(dict, "height", pum_height);
1265 (void)dict_add_number(dict, "width", pum_width);
1266 (void)dict_add_number(dict, "row", pum_row);
1267 (void)dict_add_number(dict, "col", pum_col);
1268 (void)dict_add_number(dict, "size", pum_size);
1269 (void)dict_add_bool(dict, "scrollbar",
1270 pum_scrollbar ? VVAL_TRUE : VVAL_FALSE);
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001271}
Bram Moolenaar9cb698d2019-08-21 15:30:45 +02001272#endif
Bram Moolenaard7f246c2019-04-08 18:15:41 +02001273
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001274#if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001275 static void
1276pum_position_at_mouse(int min_width)
1277{
1278 if (Rows - mouse_row > pum_size)
1279 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001280 // Enough space below the mouse row.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001281 pum_row = mouse_row + 1;
1282 if (pum_height > Rows - pum_row)
1283 pum_height = Rows - pum_row;
Luuk van Baaldcd40cf2023-04-23 16:24:08 +01001284 if (pum_row + pum_height > cmdline_row)
1285 pum_in_cmdline = TRUE;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001286 }
1287 else
1288 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001289 // Show above the mouse row, reduce height if it does not fit.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001290 pum_row = mouse_row - pum_size;
1291 if (pum_row < 0)
1292 {
1293 pum_height += pum_row;
1294 pum_row = 0;
1295 }
1296 }
1297 if (Columns - mouse_col >= pum_base_width
1298 || Columns - mouse_col > min_width)
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001299 // Enough space to show at mouse column.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001300 pum_col = mouse_col;
1301 else
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001302 // Not enough space, right align with window.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001303 pum_col = Columns - (pum_base_width > min_width
1304 ? min_width : pum_base_width);
1305
1306 pum_width = Columns - pum_col;
1307 if (pum_width > pum_base_width + 1)
1308 pum_width = pum_base_width + 1;
Bram Moolenaar0e6e1792018-06-17 17:10:59 +02001309
1310 // Do not redraw at cursor position.
1311 pum_window = NULL;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001312}
1313
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001314#endif
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001315
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001316#if defined(FEAT_BEVAL_TERM) || defined(PROTO)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001317static pumitem_T *balloon_array = NULL;
1318static int balloon_arraysize;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001319
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001320# define BALLOON_MIN_WIDTH 50
1321# define BALLOON_MIN_HEIGHT 10
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001322
Bram Moolenaar246fe032017-11-19 19:56:27 +01001323typedef struct {
1324 char_u *start;
1325 int bytelen;
1326 int cells;
1327 int indent;
1328} balpart_T;
1329
1330/*
1331 * Split a string into parts to display in the balloon.
1332 * Aimed at output from gdb. Attempts to split at white space, preserve quoted
1333 * strings and make a struct look good.
1334 * Resulting array is stored in "array" and returns the size of the array.
1335 */
1336 int
1337split_message(char_u *mesg, pumitem_T **array)
1338{
1339 garray_T ga;
1340 char_u *p;
1341 balpart_T *item;
1342 int quoted = FALSE;
1343 int height;
1344 int line;
1345 int item_idx;
1346 int indent = 0;
1347 int max_cells = 0;
Bram Moolenaard1c1c822019-11-09 16:59:14 +01001348 int max_height = Rows / 2 - 1;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001349 int long_item_count = 0;
1350 int split_long_items = FALSE;
1351
1352 ga_init2(&ga, sizeof(balpart_T), 20);
1353 p = mesg;
1354
1355 while (*p != NUL)
1356 {
1357 if (ga_grow(&ga, 1) == FAIL)
1358 goto failed;
1359 item = ((balpart_T *)ga.ga_data) + ga.ga_len;
1360 item->start = p;
1361 item->indent = indent;
1362 item->cells = indent * 2;
1363 ++ga.ga_len;
1364 while (*p != NUL)
1365 {
1366 if (*p == '"')
1367 quoted = !quoted;
Bram Moolenaard1c1c822019-11-09 16:59:14 +01001368 else if (*p == '\n')
1369 break;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001370 else if (*p == '\\' && p[1] != NUL)
1371 ++p;
1372 else if (!quoted)
1373 {
1374 if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}')
1375 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001376 // Looks like a good point to break.
Bram Moolenaar246fe032017-11-19 19:56:27 +01001377 if (*p == '{')
1378 ++indent;
1379 else if (*p == '}' && indent > 0)
1380 --indent;
1381 ++item->cells;
1382 p = skipwhite(p + 1);
1383 break;
1384 }
1385 }
1386 item->cells += ptr2cells(p);
Bram Moolenaar1614a142019-10-06 22:00:13 +02001387 p += mb_ptr2len(p);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001388 }
1389 item->bytelen = p - item->start;
Bram Moolenaard1c1c822019-11-09 16:59:14 +01001390 if (*p == '\n')
1391 ++p;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001392 if (item->cells > max_cells)
1393 max_cells = item->cells;
Bram Moolenaara3571eb2017-11-26 16:53:16 +01001394 long_item_count += (item->cells - 1) / BALLOON_MIN_WIDTH;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001395 }
1396
1397 height = 2 + ga.ga_len;
1398
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001399 // If there are long items and the height is below the limit: split lines
Bram Moolenaar246fe032017-11-19 19:56:27 +01001400 if (long_item_count > 0 && height + long_item_count <= max_height)
1401 {
1402 split_long_items = TRUE;
1403 height += long_item_count;
1404 }
1405
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001406 // Limit to half the window height, it has to fit above or below the mouse
1407 // position.
Bram Moolenaar246fe032017-11-19 19:56:27 +01001408 if (height > max_height)
1409 height = max_height;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001410 *array = ALLOC_CLEAR_MULT(pumitem_T, height);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001411 if (*array == NULL)
1412 goto failed;
1413
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001414 // Add an empty line above and below, looks better.
Bram Moolenaar246fe032017-11-19 19:56:27 +01001415 (*array)->pum_text = vim_strsave((char_u *)"");
1416 (*array + height - 1)->pum_text = vim_strsave((char_u *)"");
1417
1418 for (line = 1, item_idx = 0; line < height - 1; ++item_idx)
1419 {
1420 int skip;
1421 int thislen;
1422 int copylen;
1423 int ind;
1424 int cells;
1425
1426 item = ((balpart_T *)ga.ga_data) + item_idx;
Bram Moolenaar9ae862e2019-11-21 13:27:06 +01001427 if (item->bytelen == 0)
1428 (*array)[line++].pum_text = vim_strsave((char_u *)"");
1429 else
1430 for (skip = 0; skip < item->bytelen; skip += thislen)
Bram Moolenaar246fe032017-11-19 19:56:27 +01001431 {
Bram Moolenaar9ae862e2019-11-21 13:27:06 +01001432 if (split_long_items && item->cells >= BALLOON_MIN_WIDTH)
1433 {
1434 cells = item->indent * 2;
1435 for (p = item->start + skip;
1436 p < item->start + item->bytelen;
Bram Moolenaar1614a142019-10-06 22:00:13 +02001437 p += mb_ptr2len(p))
Bram Moolenaar9ae862e2019-11-21 13:27:06 +01001438 if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH)
1439 break;
1440 thislen = p - (item->start + skip);
1441 }
1442 else
1443 thislen = item->bytelen;
1444
1445 // put indent at the start
1446 p = alloc(thislen + item->indent * 2 + 1);
1447 if (p == NULL)
1448 {
1449 for (line = 0; line <= height - 1; ++line)
1450 vim_free((*array)[line].pum_text);
1451 vim_free(*array);
1452 goto failed;
1453 }
1454 for (ind = 0; ind < item->indent * 2; ++ind)
1455 p[ind] = ' ';
1456
1457 // exclude spaces at the end of the string
1458 for (copylen = thislen; copylen > 0; --copylen)
1459 if (item->start[skip + copylen - 1] != ' ')
Bram Moolenaar246fe032017-11-19 19:56:27 +01001460 break;
Bram Moolenaar9ae862e2019-11-21 13:27:06 +01001461
1462 vim_strncpy(p + ind, item->start + skip, copylen);
1463 (*array)[line].pum_text = p;
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001464 item->indent = 0; // wrapped line has no indent
Bram Moolenaar9ae862e2019-11-21 13:27:06 +01001465 ++line;
Bram Moolenaar246fe032017-11-19 19:56:27 +01001466 }
Bram Moolenaar246fe032017-11-19 19:56:27 +01001467 }
1468 ga_clear(&ga);
1469 return height;
1470
1471failed:
1472 ga_clear(&ga);
1473 return 0;
1474}
1475
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001476 void
1477ui_remove_balloon(void)
1478{
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00001479 if (balloon_array == NULL)
1480 return;
1481
1482 pum_undisplay();
1483 while (balloon_arraysize > 0)
1484 vim_free(balloon_array[--balloon_arraysize].pum_text);
1485 VIM_CLEAR(balloon_array);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001486}
1487
1488/*
1489 * Terminal version of a balloon, uses the popup menu code.
1490 */
1491 void
Bram Moolenaar246fe032017-11-19 19:56:27 +01001492ui_post_balloon(char_u *mesg, list_T *list)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001493{
1494 ui_remove_balloon();
1495
Bram Moolenaar246fe032017-11-19 19:56:27 +01001496 if (mesg == NULL && list == NULL)
Bram Moolenaarbe0a2592019-05-09 13:50:16 +02001497 {
1498 pum_undisplay();
Bram Moolenaar246fe032017-11-19 19:56:27 +01001499 return;
Bram Moolenaarbe0a2592019-05-09 13:50:16 +02001500 }
Bram Moolenaar246fe032017-11-19 19:56:27 +01001501 if (list != NULL)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001502 {
Bram Moolenaar246fe032017-11-19 19:56:27 +01001503 listitem_T *li;
1504 int idx;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001505
Bram Moolenaar246fe032017-11-19 19:56:27 +01001506 balloon_arraysize = list->lv_len;
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001507 balloon_array = ALLOC_CLEAR_MULT(pumitem_T, list->lv_len);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001508 if (balloon_array == NULL)
1509 return;
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02001510 CHECK_LIST_MATERIALIZE(list);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001511 for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx)
1512 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +01001513 char_u *text = tv_get_string_chk(&li->li_tv);
Bram Moolenaar246fe032017-11-19 19:56:27 +01001514
1515 balloon_array[idx].pum_text = vim_strsave(
1516 text == NULL ? (char_u *)"" : text);
1517 }
1518 }
1519 else
1520 balloon_arraysize = split_message(mesg, &balloon_array);
1521
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00001522 if (balloon_arraysize <= 0)
1523 return;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001524
Yegappan Lakshmananf97a2952023-01-18 18:17:48 +00001525 pum_array = balloon_array;
1526 pum_size = balloon_arraysize;
1527 pum_compute_size();
1528 pum_scrollbar = 0;
1529 pum_height = balloon_arraysize;
1530
1531 pum_position_at_mouse(BALLOON_MIN_WIDTH);
1532 pum_selected = -1;
1533 pum_first = 0;
1534 pum_redraw();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001535}
1536
1537/*
1538 * Called when the mouse moved, may remove any displayed balloon.
1539 */
1540 void
1541ui_may_remove_balloon(void)
1542{
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001543 // For now: remove the balloon whenever the mouse moves to another screen
1544 // cell.
1545 ui_remove_balloon();
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001546}
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001547#endif
Bram Moolenaara8f04aa2018-02-10 15:36:55 +01001548
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001549#if defined(FEAT_TERM_POPUP_MENU) || defined(PROTO)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001550/*
1551 * Select the pum entry at the mouse position.
1552 */
1553 static void
1554pum_select_mouse_pos(void)
1555{
1556 int idx = mouse_row - pum_row;
1557
1558 if (idx < 0 || idx >= pum_size)
1559 pum_selected = -1;
1560 else if (*pum_array[idx].pum_text != NUL)
1561 pum_selected = idx;
1562}
1563
1564/*
1565 * Execute the currently selected popup menu item.
1566 */
1567 static void
Bram Moolenaar987723e2018-03-06 11:43:04 +01001568pum_execute_menu(vimmenu_T *menu, int mode)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001569{
1570 vimmenu_T *mp;
1571 int idx = 0;
1572 exarg_T ea;
1573
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001574 FOR_ALL_CHILD_MENUS(menu, mp)
Bram Moolenaar987723e2018-03-06 11:43:04 +01001575 if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001576 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02001577 CLEAR_FIELD(ea);
Bram Moolenaar4c5d8152018-10-19 22:36:53 +02001578 execute_menu(&ea, mp, -1);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001579 break;
1580 }
1581}
1582
1583/*
1584 * Open the terminal version of the popup menu and don't return until it is
1585 * closed.
1586 */
1587 void
1588pum_show_popupmenu(vimmenu_T *menu)
1589{
1590 vimmenu_T *mp;
1591 int idx = 0;
1592 pumitem_T *array;
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001593# ifdef FEAT_BEVAL_TERM
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001594 int save_bevalterm = p_bevalterm;
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001595# endif
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001596 int mode;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001597
1598 pum_undisplay();
1599 pum_size = 0;
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001600 mode = get_menu_mode_flag();
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001601
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001602 FOR_ALL_CHILD_MENUS(menu, mp)
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001603 if (menu_is_separator(mp->dname)
1604 || (mp->modes & mp->enabled & mode))
1605 ++pum_size;
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001606
Bram Moolenaarf42b45d2019-01-06 13:11:05 +01001607 // When there are only Terminal mode menus, using "popup Edit" results in
1608 // pum_size being zero.
1609 if (pum_size <= 0)
1610 {
zeertzjq276410e2023-05-07 21:59:33 +01001611 emsg(_(e_menu_only_exists_in_another_mode));
Bram Moolenaarf42b45d2019-01-06 13:11:05 +01001612 return;
1613 }
1614
Bram Moolenaarc799fe22019-05-28 23:08:19 +02001615 array = ALLOC_CLEAR_MULT(pumitem_T, pum_size);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001616 if (array == NULL)
1617 return;
1618
Bram Moolenaar00d253e2020-04-06 22:13:01 +02001619 FOR_ALL_CHILD_MENUS(menu, mp)
Bram Moolenaar38455a92020-12-24 18:39:02 +01001620 {
1621 char_u *s = NULL;
1622
1623 // Make a copy of the text, the menu may be redefined in a callback.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001624 if (menu_is_separator(mp->dname))
Bram Moolenaar38455a92020-12-24 18:39:02 +01001625 s = (char_u *)"";
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001626 else if (mp->modes & mp->enabled & mode)
Bram Moolenaar38455a92020-12-24 18:39:02 +01001627 s = mp->dname;
1628 if (s != NULL)
1629 {
1630 s = vim_strsave(s);
1631 if (s != NULL)
1632 array[idx++].pum_text = s;
1633 }
1634 }
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001635
1636 pum_array = array;
1637 pum_compute_size();
1638 pum_scrollbar = 0;
1639 pum_height = pum_size;
1640 pum_position_at_mouse(20);
1641
1642 pum_selected = -1;
1643 pum_first = 0;
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001644# ifdef FEAT_BEVAL_TERM
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001645 p_bevalterm = TRUE; // track mouse movement
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001646 mch_setmouse(TRUE);
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001647# endif
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001648
1649 for (;;)
1650 {
1651 int c;
1652
1653 pum_redraw();
Bram Moolenaar987723e2018-03-06 11:43:04 +01001654 setcursor_mayforce(TRUE);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001655 out_flush();
1656
1657 c = vgetc();
Bram Moolenaar5c3fb042019-05-30 15:53:29 +02001658
zeertzjq92a16782022-07-26 12:24:41 +01001659 // Bail out when typing Esc, CTRL-C or some callback or <expr> mapping
1660 // closed the popup menu.
Bram Moolenaar5c3fb042019-05-30 15:53:29 +02001661 if (c == ESC || c == Ctrl_C || pum_array == NULL)
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001662 break;
1663 else if (c == CAR || c == NL)
1664 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001665 // enter: select current item, if any, and close
Bram Moolenaar987723e2018-03-06 11:43:04 +01001666 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001667 break;
1668 }
1669 else if (c == 'k' || c == K_UP || c == K_MOUSEUP)
1670 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001671 // cursor up: select previous item
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001672 while (pum_selected > 0)
1673 {
1674 --pum_selected;
1675 if (*array[pum_selected].pum_text != NUL)
1676 break;
1677 }
1678 }
1679 else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN)
1680 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001681 // cursor down: select next item
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001682 while (pum_selected < pum_size - 1)
1683 {
1684 ++pum_selected;
1685 if (*array[pum_selected].pum_text != NUL)
1686 break;
1687 }
1688 }
1689 else if (c == K_RIGHTMOUSE)
1690 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001691 // Right mouse down: reposition the menu.
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001692 vungetc(c);
1693 break;
1694 }
1695 else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE)
1696 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001697 // mouse moved: select item in the mouse row
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001698 pum_select_mouse_pos();
1699 }
1700 else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE)
1701 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001702 // left mouse click: select clicked item, if any, and close;
1703 // right mouse release: select clicked item, close if any
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001704 pum_select_mouse_pos();
1705 if (pum_selected >= 0)
1706 {
Bram Moolenaar987723e2018-03-06 11:43:04 +01001707 pum_execute_menu(menu, mode);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001708 break;
1709 }
1710 if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM)
1711 break;
1712 }
1713 }
1714
Bram Moolenaar38455a92020-12-24 18:39:02 +01001715 for (idx = 0; idx < pum_size; ++idx)
1716 vim_free(array[idx].pum_text);
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001717 vim_free(array);
1718 pum_undisplay();
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001719# ifdef FEAT_BEVAL_TERM
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001720 p_bevalterm = save_bevalterm;
1721 mch_setmouse(TRUE);
Bram Moolenaare2c453d2019-08-21 14:37:09 +02001722# endif
Bram Moolenaaraef8c3d2018-03-03 18:59:16 +01001723}
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001724
1725 void
1726pum_make_popup(char_u *path_name, int use_mouse_pos)
1727{
1728 vimmenu_T *menu;
1729
1730 if (!use_mouse_pos)
1731 {
Bram Moolenaar63d9e732019-12-05 21:10:38 +01001732 // Hack: set mouse position at the cursor so that the menu pops up
1733 // around there.
zeertzjq4e1ca0d2023-04-27 19:36:55 +01001734 mouse_row = W_WINROW(curwin) + curwin->w_wrow;
Bram Moolenaar29a2c082018-03-05 21:06:23 +01001735 mouse_col = curwin->w_wincol + curwin->w_wcol;
1736 }
1737
1738 menu = gui_find_menu(path_name);
1739 if (menu != NULL)
1740 pum_show_popupmenu(menu);
1741}
Bram Moolenaar4b779472005-10-04 09:12:31 +00001742#endif