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_wrow
+ W_WINROW(curwin
);
78 if (firstwin
->w_p_pvw
)
79 top_clear
= firstwin
->w_height
;
83 /* When the preview window is at the bottom stop just above it. Also
84 * avoid drawing over the status line so that it's clear there is a window
87 above_row
-= lastwin
->w_height
+ lastwin
->w_status_height
+ 1;
90 * Figure out the size and position of the pum.
92 if (size
< PUM_DEF_HEIGHT
)
95 pum_height
= PUM_DEF_HEIGHT
;
96 if (p_ph
> 0 && pum_height
> p_ph
)
99 /* Put the pum below "row" if possible. If there are few lines decide on
100 * where there is more room. */
101 if (row
+ 2 >= above_row
- pum_height
102 && row
> (above_row
- top_clear
) / 2)
104 /* pum above "row" */
106 /* Leave two lines of context if possible */
107 if (curwin
->w_wrow
- curwin
->w_cline_row
>= 2)
110 context_lines
= curwin
->w_wrow
- curwin
->w_cline_row
;
112 if (row
>= size
+ context_lines
)
114 pum_row
= row
- size
- context_lines
;
120 pum_height
= row
- context_lines
;
122 if (p_ph
> 0 && pum_height
> p_ph
)
124 pum_row
+= pum_height
- p_ph
;
130 /* pum below "row" */
132 /* Leave two lines of context if possible */
133 if (curwin
->w_cline_row
+ curwin
->w_cline_height
- curwin
->w_wrow
>= 3)
136 context_lines
= curwin
->w_cline_row
137 + curwin
->w_cline_height
- curwin
->w_wrow
;
139 pum_row
= row
+ context_lines
;
140 if (size
> above_row
- pum_row
)
141 pum_height
= above_row
- pum_row
;
144 if (p_ph
> 0 && pum_height
> p_ph
)
148 /* don't display when we only have room for one line */
149 if (pum_height
< 1 || (pum_height
== 1 && size
> 1))
152 /* If there is a preview window at the top avoid drawing over it. */
153 if (firstwin
->w_p_pvw
154 && pum_row
< firstwin
->w_height
155 && pum_height
> firstwin
->w_height
+ 4)
157 pum_row
+= firstwin
->w_height
;
158 pum_height
-= firstwin
->w_height
;
161 /* Compute the width of the widest match and the widest extra. */
162 for (i
= 0; i
< size
; ++i
)
164 w
= vim_strsize(array
[i
].pum_text
);
167 if (array
[i
].pum_kind
!= NULL
)
169 w
= vim_strsize(array
[i
].pum_kind
) + 1;
173 if (array
[i
].pum_extra
!= NULL
)
175 w
= vim_strsize(array
[i
].pum_extra
) + 1;
180 pum_base_width
= max_width
;
181 pum_kind_width
= kind_width
;
183 /* Calculate column */
184 #ifdef FEAT_RIGHTLEFT
186 col
= W_WINCOL(curwin
) + W_WIDTH(curwin
) - curwin
->w_wcol
- 1;
189 col
= W_WINCOL(curwin
) + curwin
->w_wcol
;
191 /* if there are more items than room we need a scrollbar */
192 if (pum_height
< size
)
200 if (def_width
< max_width
)
201 def_width
= max_width
;
203 if (((col
< Columns
- PUM_DEF_WIDTH
|| col
< Columns
- max_width
)
204 #ifdef FEAT_RIGHTLEFT
206 || (curwin
->w_p_rl
&& (col
> PUM_DEF_WIDTH
|| col
> max_width
)
210 /* align pum column with "col" */
213 #ifdef FEAT_RIGHTLEFT
215 pum_width
= pum_col
- pum_scrollbar
+ 1;
218 pum_width
= Columns
- pum_col
- pum_scrollbar
;
220 if (pum_width
> max_width
+ kind_width
+ extra_width
+ 1
221 && pum_width
> PUM_DEF_WIDTH
)
223 pum_width
= max_width
+ kind_width
+ extra_width
+ 1;
224 if (pum_width
< PUM_DEF_WIDTH
)
225 pum_width
= PUM_DEF_WIDTH
;
228 else if (Columns
< def_width
)
230 /* not enough room, will use what we have */
231 #ifdef FEAT_RIGHTLEFT
233 pum_col
= Columns
- 1;
237 pum_width
= Columns
- 1;
241 if (max_width
> PUM_DEF_WIDTH
)
242 max_width
= PUM_DEF_WIDTH
; /* truncate */
243 #ifdef FEAT_RIGHTLEFT
245 pum_col
= max_width
- 1;
248 pum_col
= Columns
- max_width
;
249 pum_width
= max_width
- pum_scrollbar
;
255 /* Set selected item and redraw. If the window size changed need to redo
256 * the positioning. Limit this to two times, when there is not much
257 * room the window size will keep changing. */
258 if (pum_set_selected(selected
, redo_count
) && ++redo_count
<= 2)
263 * Redraw the popup menu, using "pum_first" and "pum_selected".
270 int attr_norm
= highlight_attr
[HLF_PNI
];
271 int attr_select
= highlight_attr
[HLF_PSI
];
272 int attr_scroll
= highlight_attr
[HLF_PSB
];
273 int attr_thumb
= highlight_attr
[HLF_PST
];
279 int totwidth
, width
, w
;
281 int thumb_heigth
= 1;
287 thumb_heigth
= pum_height
* pum_height
/ pum_size
;
288 if (thumb_heigth
== 0)
290 thumb_pos
= (pum_first
* (pum_height
- thumb_heigth
)
291 + (pum_size
- pum_height
) / 2)
292 / (pum_size
- pum_height
);
295 for (i
= 0; i
< pum_height
; ++i
)
298 attr
= (idx
== pum_selected
) ? attr_select
: attr_norm
;
300 /* prepend a space if there is room */
301 #ifdef FEAT_RIGHTLEFT
304 if (pum_col
< W_WINCOL(curwin
) + W_WIDTH(curwin
) - 1)
305 screen_putchar(' ', row
, pum_col
+ 1, attr
);
310 screen_putchar(' ', row
, pum_col
- 1, attr
);
312 /* Display each entry, use two spaces for a Tab.
313 * Do this 3 times: For the main text, kind and extra info */
316 for (round
= 1; round
<= 3; ++round
)
322 case 1: p
= pum_array
[idx
].pum_text
; break;
323 case 2: p
= pum_array
[idx
].pum_kind
; break;
324 case 3: p
= pum_array
[idx
].pum_extra
; break;
327 for ( ; ; mb_ptr_adv(p
))
332 if (*p
== NUL
|| *p
== TAB
|| totwidth
+ w
> pum_width
)
334 /* Display the text that fits or comes before a Tab.
335 * First convert it to printable characters. */
342 #ifdef FEAT_RIGHTLEFT
347 char_u
*rt
= reverse_text(st
);
348 char_u
*rt_saved
= rt
;
353 len
= (int)STRLEN(rt
);
356 for (j
= pum_width
; j
< len
; ++j
)
360 screen_puts_len(rt
, len
, row
,
361 col
- len
+ 1, attr
);
373 screen_puts_len(st
, (int)STRLEN(st
), row
, col
,
383 /* Display two spaces for a Tab. */
384 #ifdef FEAT_RIGHTLEFT
387 screen_puts_len((char_u
*)" ", 2, row
, col
- 1,
394 screen_puts_len((char_u
*)" ", 2, row
, col
, attr
);
398 s
= NULL
; /* start text at next char */
406 n
= pum_kind_width
+ 1;
410 /* Stop when there is nothing more to display. */
412 || (round
== 2 && pum_array
[idx
].pum_extra
== NULL
)
413 || (round
== 1 && pum_array
[idx
].pum_kind
== NULL
414 && pum_array
[idx
].pum_extra
== NULL
)
415 || pum_base_width
+ n
>= pum_width
)
417 #ifdef FEAT_RIGHTLEFT
420 screen_fill(row
, row
+ 1, pum_col
- pum_base_width
- n
+ 1,
421 col
+ 1, ' ', ' ', attr
);
422 col
= pum_col
- pum_base_width
- n
+ 1;
427 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_base_width
+ n
,
429 col
= pum_col
+ pum_base_width
+ n
;
431 totwidth
= pum_base_width
+ n
;
434 #ifdef FEAT_RIGHTLEFT
436 screen_fill(row
, row
+ 1, pum_col
- pum_width
+ 1, col
+ 1, ' ',
440 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_width
, ' ', ' ',
442 if (pum_scrollbar
> 0)
444 #ifdef FEAT_RIGHTLEFT
446 screen_putchar(' ', row
, pum_col
- pum_width
,
447 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
448 ? attr_thumb
: attr_scroll
);
451 screen_putchar(' ', row
, pum_col
+ pum_width
,
452 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
453 ? attr_thumb
: attr_scroll
);
460 #if 0 /* not used yet */
462 * Return the index of the currently selected item.
472 * Set the index of the currently selected item. The menu will scroll when
473 * necessary. When "n" is out of range don't scroll.
474 * This may be repeated when the preview window is used:
475 * "repeat" == 0: open preview window normally
476 * "repeat" == 1: open preview window but don't set the size
477 * "repeat" == 2: don't open preview window
478 * Returns TRUE when the window was resized and the location of the popup menu
479 * must be recomputed.
482 pum_set_selected(n
, repeat
)
487 int context
= pum_height
/ 2;
491 if (pum_selected
>= 0 && pum_selected
< pum_size
)
493 if (pum_first
> pum_selected
- 4)
495 /* scroll down; when we did a jump it's probably a PageUp then
496 * scroll a whole page */
497 if (pum_first
> pum_selected
- 2)
499 pum_first
-= pum_height
- 2;
502 else if (pum_first
> pum_selected
)
503 pum_first
= pum_selected
;
506 pum_first
= pum_selected
;
508 else if (pum_first
< pum_selected
- pum_height
+ 5)
510 /* scroll up; when we did a jump it's probably a PageDown then
511 * scroll a whole page */
512 if (pum_first
< pum_selected
- pum_height
+ 1 + 2)
514 pum_first
+= pum_height
- 2;
515 if (pum_first
< pum_selected
- pum_height
+ 1)
516 pum_first
= pum_selected
- pum_height
+ 1;
519 pum_first
= pum_selected
- pum_height
+ 1;
522 /* Give a few lines of context when possible. */
527 if (pum_first
> pum_selected
- context
)
530 pum_first
= pum_selected
- context
;
534 else if (pum_first
< pum_selected
+ context
- pum_height
+ 1)
537 pum_first
= pum_selected
+ context
- pum_height
+ 1;
541 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
543 * Show extra info in the preview window if there is something and
544 * 'completeopt' contains "preview".
545 * Skip this when tried twice already.
546 * Skip this also when there is not much room.
547 * NOTE: Be very careful not to sync undo!
549 if (pum_array
[pum_selected
].pum_info
!= NULL
552 && vim_strchr(p_cot
, 'p') != NULL
)
554 win_T
*curwin_save
= curwin
;
557 /* Open a preview window. 3 lines by default. */
559 resized
= prepare_tagpreview(FALSE
);
564 if (curbuf
->b_fname
== NULL
565 && curbuf
->b_p_bt
[0] == 'n' && curbuf
->b_p_bt
[2] == 'f'
566 && curbuf
->b_p_bh
[0] == 'w')
568 /* Already a "wipeout" buffer, make it empty. */
570 ml_delete((linenr_T
)1, FALSE
);
574 /* Don't want to sync undo in the current buffer. */
576 res
= do_ecmd(0, NULL
, NULL
, NULL
, ECMD_ONE
, 0);
580 /* Edit a new, empty buffer. Set options for a "wipeout"
582 set_option_value((char_u
*)"swf", 0L, NULL
, OPT_LOCAL
);
583 set_option_value((char_u
*)"bt", 0L,
584 (char_u
*)"nofile", OPT_LOCAL
);
585 set_option_value((char_u
*)"bh", 0L,
586 (char_u
*)"wipe", OPT_LOCAL
);
587 set_option_value((char_u
*)"diff", 0L,
596 for (p
= pum_array
[pum_selected
].pum_info
; *p
!= NUL
; )
598 e
= vim_strchr(p
, '\n');
601 ml_append(lnum
++, p
, 0, FALSE
);
607 ml_append(lnum
++, p
, (int)(e
- p
+ 1), FALSE
);
613 /* Increase the height of the preview window to show the
614 * text, but no more than 'previewheight' lines. */
619 if (curwin
->w_height
< lnum
)
621 win_setheight((int)lnum
);
626 curbuf
->b_changed
= 0;
627 curbuf
->b_p_ma
= FALSE
;
628 curwin
->w_cursor
.lnum
= 0;
629 curwin
->w_cursor
.col
= 0;
631 if (curwin
!= curwin_save
&& win_valid(curwin_save
))
633 /* Return cursor to where we were */
635 redraw_later(SOME_VALID
);
637 /* When the preview window was resized we need to
638 * update the view on the buffer. Only go back to
639 * the window when needed, otherwise it will always be
643 win_enter(curwin_save
, TRUE
);
647 /* Update the screen before drawing the popup menu.
648 * Enable updating the status lines. */
649 pum_do_redraw
= TRUE
;
651 pum_do_redraw
= FALSE
;
653 if (!resized
&& win_valid(curwin_save
))
654 win_enter(curwin_save
, TRUE
);
656 /* May need to update the screen again when there are
657 * autocommands involved. */
658 pum_do_redraw
= TRUE
;
660 pum_do_redraw
= FALSE
;
668 /* Never display more than we have */
669 if (pum_first
> pum_size
- pum_height
)
670 pum_first
= pum_size
- pum_height
;
679 * Undisplay the popup menu (later).
685 redraw_all_later(SOME_VALID
);
687 redraw_tabline
= TRUE
;
693 * Clear the popup menu. Currently only resets the offset to the first
703 * Return TRUE if the popup menu is displayed.
704 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
709 return !pum_do_redraw
&& pum_array
!= NULL
;
713 * Return the height of the popup menu, the number of entries visible.
714 * Only valid when pum_visible() returns TRUE!