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 * First convert it to printable characters. */
293 screen_puts_len(st
, (int)STRLEN(st
), row
, col
,
302 /* Display two spaces for a Tab. */
303 screen_puts_len((char_u
*)" ", 2, row
, col
, attr
);
306 s
= NULL
; /* start text at next char */
314 n
= pum_kind_width
+ 1;
318 /* Stop when there is nothing more to display. */
320 || (round
== 2 && pum_array
[idx
].pum_extra
== NULL
)
321 || (round
== 1 && pum_array
[idx
].pum_kind
== NULL
322 && pum_array
[idx
].pum_extra
== NULL
)
323 || pum_base_width
+ n
>= pum_width
)
325 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_base_width
+ n
,
327 col
= pum_col
+ pum_base_width
+ n
;
328 totwidth
= pum_base_width
+ n
;
331 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_width
, ' ', ' ', attr
);
332 if (pum_scrollbar
> 0)
333 screen_putchar(' ', row
, pum_col
+ pum_width
,
334 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
335 ? attr_thumb
: attr_scroll
);
341 #if 0 /* not used yet */
343 * Return the index of the currently selected item.
353 * Set the index of the currently selected item. The menu will scroll when
354 * necessary. When "n" is out of range don't scroll.
355 * This may be repeated when the preview window is used:
356 * "repeat" == 0: open preview window normally
357 * "repeat" == 1: open preview window but don't set the size
358 * "repeat" == 2: don't open preview window
359 * Returns TRUE when the window was resized and the location of the popup menu
360 * must be recomputed.
363 pum_set_selected(n
, repeat
)
368 int context
= pum_height
/ 2;
372 if (pum_selected
>= 0 && pum_selected
< pum_size
)
374 if (pum_first
> pum_selected
- 4)
376 /* scroll down; when we did a jump it's probably a PageUp then
377 * scroll a whole page */
378 if (pum_first
> pum_selected
- 2)
380 pum_first
-= pum_height
- 2;
383 else if (pum_first
> pum_selected
)
384 pum_first
= pum_selected
;
387 pum_first
= pum_selected
;
389 else if (pum_first
< pum_selected
- pum_height
+ 5)
391 /* scroll up; when we did a jump it's probably a PageDown then
392 * scroll a whole page */
393 if (pum_first
< pum_selected
- pum_height
+ 1 + 2)
395 pum_first
+= pum_height
- 2;
396 if (pum_first
< pum_selected
- pum_height
+ 1)
397 pum_first
= pum_selected
- pum_height
+ 1;
400 pum_first
= pum_selected
- pum_height
+ 1;
403 /* Give a few lines of context when possible. */
408 if (pum_first
> pum_selected
- context
)
411 pum_first
= pum_selected
- context
;
415 else if (pum_first
< pum_selected
+ context
- pum_height
+ 1)
418 pum_first
= pum_selected
+ context
- pum_height
+ 1;
422 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
424 * Show extra info in the preview window if there is something and
425 * 'completeopt' contains "preview".
426 * Skip this when tried twice already.
427 * Skip this also when there is not much room.
428 * NOTE: Be very careful not to sync undo!
430 if (pum_array
[pum_selected
].pum_info
!= NULL
433 && vim_strchr(p_cot
, 'p') != NULL
)
435 win_T
*curwin_save
= curwin
;
438 /* Open a preview window. 3 lines by default. */
440 resized
= prepare_tagpreview(FALSE
);
445 if (curbuf
->b_fname
== NULL
446 && curbuf
->b_p_bt
[0] == 'n' && curbuf
->b_p_bt
[2] == 'f'
447 && curbuf
->b_p_bh
[0] == 'w')
449 /* Already a "wipeout" buffer, make it empty. */
451 ml_delete((linenr_T
)1, FALSE
);
455 /* Don't want to sync undo in the current buffer. */
457 res
= do_ecmd(0, NULL
, NULL
, NULL
, ECMD_ONE
, 0);
461 /* Edit a new, empty buffer. Set options for a "wipeout"
463 set_option_value((char_u
*)"swf", 0L, NULL
, OPT_LOCAL
);
464 set_option_value((char_u
*)"bt", 0L,
465 (char_u
*)"nofile", OPT_LOCAL
);
466 set_option_value((char_u
*)"bh", 0L,
467 (char_u
*)"wipe", OPT_LOCAL
);
468 set_option_value((char_u
*)"diff", 0L,
477 for (p
= pum_array
[pum_selected
].pum_info
; *p
!= NUL
; )
479 e
= vim_strchr(p
, '\n');
482 ml_append(lnum
++, p
, 0, FALSE
);
488 ml_append(lnum
++, p
, (int)(e
- p
+ 1), FALSE
);
494 /* Increase the height of the preview window to show the
495 * text, but no more than 'previewheight' lines. */
500 if (curwin
->w_height
< lnum
)
502 win_setheight((int)lnum
);
507 curbuf
->b_changed
= 0;
508 curbuf
->b_p_ma
= FALSE
;
509 curwin
->w_cursor
.lnum
= 0;
510 curwin
->w_cursor
.col
= 0;
512 if (curwin
!= curwin_save
&& win_valid(curwin_save
))
514 /* Return cursor to where we were */
516 redraw_later(SOME_VALID
);
518 /* When the preview window was resized we need to
519 * update the view on the buffer. Only go back to
520 * the window when needed, otherwise it will always be
524 win_enter(curwin_save
, TRUE
);
528 /* Update the screen before drawing the popup menu.
529 * Enable updating the status lines. */
530 pum_do_redraw
= TRUE
;
532 pum_do_redraw
= FALSE
;
534 if (!resized
&& win_valid(curwin_save
))
535 win_enter(curwin_save
, TRUE
);
537 /* May need to update the screen again when there are
538 * autocommands involved. */
539 pum_do_redraw
= TRUE
;
541 pum_do_redraw
= FALSE
;
549 /* Never display more than we have */
550 if (pum_first
> pum_size
- pum_height
)
551 pum_first
= pum_size
- pum_height
;
560 * Undisplay the popup menu (later).
566 redraw_all_later(SOME_VALID
);
568 redraw_tabline
= TRUE
;
574 * Clear the popup menu. Currently only resets the offset to the first
584 * Return TRUE if the popup menu is displayed.
585 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
590 return !pum_do_redraw
&& pum_array
!= NULL
;
594 * Return the height of the popup menu, the number of entries visible.
595 * Only valid when pum_visible() returns TRUE!