1 /* vi:set ts=8 sts=4 sw=4:
3 * VIM - Vi IMproved by Bram Moolenaar
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.
11 * popupmnu.c: Popup menu (PUM)
15 #if defined(FEAT_INS_EXPAND) || defined(PROTO)
17 static pumitem_T
*pum_array
= NULL
; /* items of displayed pum */
18 static int pum_size
; /* nr of items in "pum_array" */
19 static int pum_selected
; /* index of selected item or -1 */
20 static int pum_first
= 0; /* index of top item */
22 static int pum_height
; /* nr of displayed pum items */
23 static int pum_width
; /* width of displayed pum items */
24 static int pum_base_width
; /* width of pum items base */
25 static int pum_kind_width
; /* width of pum items kind column */
26 static int pum_scrollbar
; /* TRUE when scrollbar present */
28 static int pum_row
; /* top row of pum */
29 static int pum_col
; /* left column of pum */
31 static int pum_do_redraw
= FALSE
; /* do redraw anyway */
33 static int pum_set_selected
__ARGS((int n
, int repeat
));
35 #define PUM_DEF_HEIGHT 10
36 #define PUM_DEF_WIDTH 15
39 * Show the popup menu with items "array[size]".
40 * "array" must remain valid until pum_undisplay() is called!
41 * When possible the leftmost character is aligned with screen column "col".
42 * The menu appears above the screen line "row" or at "row" + "height" - 1.
45 pum_display(array
, size
, selected
)
48 int selected
; /* index of initially selected item, none if
61 int above_row
= cmdline_row
;
65 def_width
= PUM_DEF_WIDTH
;
70 /* Pretend the pum is already there to avoid that must_redraw is set when
72 pum_array
= (pumitem_T
*)1;
73 validate_cursor_col();
76 row
= curwin
->w_cline_row
+ W_WINROW(curwin
);
77 height
= curwin
->w_cline_height
;
78 col
= curwin
->w_wcol
+ W_WINCOL(curwin
) - curwin
->w_leftcol
;
80 if (firstwin
->w_p_pvw
)
81 top_clear
= firstwin
->w_height
;
85 /* When the preview window is at the bottom stop just above it. Also
86 * avoid drawing over the status line so that it's clear there is a window
89 above_row
-= lastwin
->w_height
+ lastwin
->w_status_height
+ 1;
92 * Figure out the size and position of the pum.
94 if (size
< PUM_DEF_HEIGHT
)
97 pum_height
= PUM_DEF_HEIGHT
;
98 if (p_ph
> 0 && pum_height
> p_ph
)
101 /* Put the pum below "row" if possible. If there are few lines decide on
102 * where there is more room. */
103 if (row
>= above_row
- pum_height
104 && row
> (above_row
- top_clear
- height
) / 2)
106 /* pum above "row" */
109 pum_row
= row
- size
;
117 if (p_ph
> 0 && pum_height
> p_ph
)
119 pum_row
+= pum_height
- p_ph
;
125 /* pum below "row" */
126 pum_row
= row
+ height
;
127 if (size
> above_row
- pum_row
)
128 pum_height
= above_row
- pum_row
;
131 if (p_ph
> 0 && pum_height
> p_ph
)
135 /* don't display when we only have room for one line */
136 if (pum_height
< 1 || (pum_height
== 1 && size
> 1))
139 /* If there is a preview window at the top avoid drawing over it. */
140 if (firstwin
->w_p_pvw
141 && pum_row
< firstwin
->w_height
142 && pum_height
> firstwin
->w_height
+ 4)
144 pum_row
+= firstwin
->w_height
;
145 pum_height
-= firstwin
->w_height
;
148 /* Compute the width of the widest match and the widest extra. */
149 for (i
= 0; i
< size
; ++i
)
151 w
= vim_strsize(array
[i
].pum_text
);
154 if (array
[i
].pum_kind
!= NULL
)
156 w
= vim_strsize(array
[i
].pum_kind
) + 1;
160 if (array
[i
].pum_extra
!= NULL
)
162 w
= vim_strsize(array
[i
].pum_extra
) + 1;
167 pum_base_width
= max_width
;
168 pum_kind_width
= kind_width
;
170 /* if there are more items than room we need a scrollbar */
171 if (pum_height
< size
)
179 if (def_width
< max_width
)
180 def_width
= max_width
;
182 if (col
< Columns
- PUM_DEF_WIDTH
|| col
< Columns
- max_width
)
184 /* align pum column with "col" */
186 pum_width
= Columns
- pum_col
- pum_scrollbar
;
187 if (pum_width
> max_width
+ kind_width
+ extra_width
+ 1
188 && pum_width
> PUM_DEF_WIDTH
)
190 pum_width
= max_width
+ kind_width
+ extra_width
+ 1;
191 if (pum_width
< PUM_DEF_WIDTH
)
192 pum_width
= PUM_DEF_WIDTH
;
195 else if (Columns
< def_width
)
197 /* not enough room, will use what we have */
199 pum_width
= Columns
- 1;
203 if (max_width
> PUM_DEF_WIDTH
)
204 max_width
= PUM_DEF_WIDTH
; /* truncate */
205 pum_col
= Columns
- max_width
;
206 pum_width
= max_width
- pum_scrollbar
;
212 /* Set selected item and redraw. If the window size changed need to redo
213 * the positioning. Limit this to two times, when there is not much
214 * room the window size will keep changing. */
215 if (pum_set_selected(selected
, redo_count
) && ++redo_count
<= 2)
220 * Redraw the popup menu, using "pum_first" and "pum_selected".
227 int attr_norm
= highlight_attr
[HLF_PNI
];
228 int attr_select
= highlight_attr
[HLF_PSI
];
229 int attr_scroll
= highlight_attr
[HLF_PSB
];
230 int attr_thumb
= highlight_attr
[HLF_PST
];
236 int totwidth
, width
, w
;
238 int thumb_heigth
= 1;
244 thumb_heigth
= pum_height
* pum_height
/ pum_size
;
245 if (thumb_heigth
== 0)
247 thumb_pos
= (pum_first
* (pum_height
- thumb_heigth
)
248 + (pum_size
- pum_height
) / 2)
249 / (pum_size
- pum_height
);
252 for (i
= 0; i
< pum_height
; ++i
)
255 attr
= (idx
== pum_selected
) ? attr_select
: attr_norm
;
257 /* prepend a space if there is room */
259 screen_putchar(' ', row
, pum_col
- 1, attr
);
261 /* Display each entry, use two spaces for a Tab.
262 * Do this 3 times: For the main text, kind and extra info */
265 for (round
= 1; round
<= 3; ++round
)
271 case 1: p
= pum_array
[idx
].pum_text
; break;
272 case 2: p
= pum_array
[idx
].pum_kind
; break;
273 case 3: p
= pum_array
[idx
].pum_extra
; break;
276 for ( ; ; mb_ptr_adv(p
))
281 if (*p
== NUL
|| *p
== TAB
|| totwidth
+ w
> pum_width
)
283 /* Display the text that fits or comes before a Tab. */
284 screen_puts_len(s
, (int)(p
- s
), row
, col
, attr
);
290 /* Display two spaces for a Tab. */
291 screen_puts_len((char_u
*)" ", 2, row
, col
, attr
);
294 s
= NULL
; /* start text at next char */
302 n
= pum_kind_width
+ 1;
306 /* Stop when there is nothing more to display. */
308 || (round
== 2 && pum_array
[idx
].pum_extra
== NULL
)
309 || (round
== 1 && pum_array
[idx
].pum_kind
== NULL
310 && pum_array
[idx
].pum_extra
== NULL
)
311 || pum_base_width
+ n
>= pum_width
)
313 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_base_width
+ n
,
315 col
= pum_col
+ pum_base_width
+ n
;
316 totwidth
= pum_base_width
+ n
;
319 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_width
, ' ', ' ', attr
);
320 if (pum_scrollbar
> 0)
321 screen_putchar(' ', row
, pum_col
+ pum_width
,
322 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
323 ? attr_thumb
: attr_scroll
);
329 #if 0 /* not used yet */
331 * Return the index of the currently selected item.
341 * Set the index of the currently selected item. The menu will scroll when
342 * necessary. When "n" is out of range don't scroll.
343 * This may be repeated when the preview window is used:
344 * "repeat" == 0: open preview window normally
345 * "repeat" == 1: open preview window but don't set the size
346 * "repeat" == 2: don't open preview window
347 * Returns TRUE when the window was resized and the location of the popup menu
348 * must be recomputed.
351 pum_set_selected(n
, repeat
)
356 int context
= pum_height
/ 2;
360 if (pum_selected
>= 0 && pum_selected
< pum_size
)
362 if (pum_first
> pum_selected
- 4)
364 /* scroll down; when we did a jump it's probably a PageUp then
365 * scroll a whole page */
366 if (pum_first
> pum_selected
- 2)
368 pum_first
-= pum_height
- 2;
371 else if (pum_first
> pum_selected
)
372 pum_first
= pum_selected
;
375 pum_first
= pum_selected
;
377 else if (pum_first
< pum_selected
- pum_height
+ 5)
379 /* scroll up; when we did a jump it's probably a PageDown then
380 * scroll a whole page */
381 if (pum_first
< pum_selected
- pum_height
+ 1 + 2)
383 pum_first
+= pum_height
- 2;
384 if (pum_first
< pum_selected
- pum_height
+ 1)
385 pum_first
= pum_selected
- pum_height
+ 1;
388 pum_first
= pum_selected
- pum_height
+ 1;
391 /* Give a few lines of context when possible. */
396 if (pum_first
> pum_selected
- context
)
399 pum_first
= pum_selected
- context
;
403 else if (pum_first
< pum_selected
+ context
- pum_height
+ 1)
406 pum_first
= pum_selected
+ context
- pum_height
+ 1;
410 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
412 * Show extra info in the preview window if there is something and
413 * 'completeopt' contains "preview".
414 * Skip this when tried twice already.
415 * Skip this also when there is not much room.
416 * NOTE: Be very careful not to sync undo!
418 if (pum_array
[pum_selected
].pum_info
!= NULL
421 && vim_strchr(p_cot
, 'p') != NULL
)
423 win_T
*curwin_save
= curwin
;
426 /* Open a preview window. 3 lines by default. */
428 resized
= prepare_tagpreview(FALSE
);
433 if (curbuf
->b_fname
== NULL
434 && curbuf
->b_p_bt
[0] == 'n' && curbuf
->b_p_bt
[2] == 'f'
435 && curbuf
->b_p_bh
[0] == 'w')
437 /* Already a "wipeout" buffer, make it empty. */
439 ml_delete((linenr_T
)1, FALSE
);
443 /* Don't want to sync undo in the current buffer. */
445 res
= do_ecmd(0, NULL
, NULL
, NULL
, ECMD_ONE
, 0);
449 /* Edit a new, empty buffer. Set options for a "wipeout"
451 set_option_value((char_u
*)"swf", 0L, NULL
, OPT_LOCAL
);
452 set_option_value((char_u
*)"bt", 0L,
453 (char_u
*)"nofile", OPT_LOCAL
);
454 set_option_value((char_u
*)"bh", 0L,
455 (char_u
*)"wipe", OPT_LOCAL
);
456 set_option_value((char_u
*)"diff", 0L,
457 (char_u
*)"", OPT_LOCAL
);
465 for (p
= pum_array
[pum_selected
].pum_info
; *p
!= NUL
; )
467 e
= vim_strchr(p
, '\n');
470 ml_append(lnum
++, p
, 0, FALSE
);
476 ml_append(lnum
++, p
, (int)(e
- p
+ 1), FALSE
);
482 /* Increase the height of the preview window to show the
483 * text, but no more than 'previewheight' lines. */
488 if (curwin
->w_height
< lnum
)
490 win_setheight((int)lnum
);
495 curbuf
->b_changed
= 0;
496 curbuf
->b_p_ma
= FALSE
;
497 curwin
->w_cursor
.lnum
= 0;
498 curwin
->w_cursor
.col
= 0;
500 if (curwin
!= curwin_save
&& win_valid(curwin_save
))
502 /* Return cursor to where we were */
504 redraw_later(SOME_VALID
);
506 /* When the preview window was resized we need to
507 * update the view on the buffer. Only go back to
508 * the window when needed, otherwise it will always be
512 win_enter(curwin_save
, TRUE
);
516 /* Update the screen before drawing the popup menu.
517 * Enable updating the status lines. */
518 pum_do_redraw
= TRUE
;
520 pum_do_redraw
= FALSE
;
522 if (!resized
&& win_valid(curwin_save
))
523 win_enter(curwin_save
, TRUE
);
525 /* May need to update the screen again when there are
526 * autocommands involved. */
527 pum_do_redraw
= TRUE
;
529 pum_do_redraw
= FALSE
;
537 /* Never display more than we have */
538 if (pum_first
> pum_size
- pum_height
)
539 pum_first
= pum_size
- pum_height
;
548 * Undisplay the popup menu (later).
554 redraw_all_later(SOME_VALID
);
556 redraw_tabline
= TRUE
;
562 * Clear the popup menu. Currently only resets the offset to the first
572 * Return TRUE if the popup menu is displayed.
573 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
578 return !pum_do_redraw
&& pum_array
!= NULL
;
582 * Return the height of the popup menu, the number of entries visible.
583 * Only valid when pum_visible() returns TRUE!