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
;
79 if (firstwin
->w_p_pvw
)
80 top_clear
= firstwin
->w_height
;
84 /* When the preview window is at the bottom stop just above it. Also
85 * avoid drawing over the status line so that it's clear there is a window
88 above_row
-= lastwin
->w_height
+ lastwin
->w_status_height
+ 1;
91 * Figure out the size and position of the pum.
93 if (size
< PUM_DEF_HEIGHT
)
96 pum_height
= PUM_DEF_HEIGHT
;
97 if (p_ph
> 0 && pum_height
> p_ph
)
100 /* Put the pum below "row" if possible. If there are few lines decide on
101 * where there is more room. */
102 if (row
>= above_row
- pum_height
103 && row
> (above_row
- top_clear
- height
) / 2)
105 /* pum above "row" */
108 pum_row
= row
- size
;
116 if (p_ph
> 0 && pum_height
> p_ph
)
118 pum_row
+= pum_height
- p_ph
;
124 /* pum below "row" */
125 pum_row
= row
+ height
;
126 if (size
> above_row
- pum_row
)
127 pum_height
= above_row
- pum_row
;
130 if (p_ph
> 0 && pum_height
> p_ph
)
134 /* don't display when we only have room for one line */
135 if (pum_height
< 1 || (pum_height
== 1 && size
> 1))
138 /* If there is a preview window at the top avoid drawing over it. */
139 if (firstwin
->w_p_pvw
140 && pum_row
< firstwin
->w_height
141 && pum_height
> firstwin
->w_height
+ 4)
143 pum_row
+= firstwin
->w_height
;
144 pum_height
-= firstwin
->w_height
;
147 /* Compute the width of the widest match and the widest extra. */
148 for (i
= 0; i
< size
; ++i
)
150 w
= vim_strsize(array
[i
].pum_text
);
153 if (array
[i
].pum_kind
!= NULL
)
155 w
= vim_strsize(array
[i
].pum_kind
) + 1;
159 if (array
[i
].pum_extra
!= NULL
)
161 w
= vim_strsize(array
[i
].pum_extra
) + 1;
166 pum_base_width
= max_width
;
167 pum_kind_width
= kind_width
;
169 /* Calculate column */
170 #ifdef FEAT_RIGHTLEFT
172 col
= W_WINCOL(curwin
) + W_WIDTH(curwin
) - curwin
->w_wcol
-
173 curwin
->w_leftcol
- 1;
176 col
= W_WINCOL(curwin
) + curwin
->w_wcol
- curwin
->w_leftcol
;
178 /* if there are more items than room we need a scrollbar */
179 if (pum_height
< size
)
187 if (def_width
< max_width
)
188 def_width
= max_width
;
190 if (((col
< Columns
- PUM_DEF_WIDTH
|| col
< Columns
- max_width
)
191 #ifdef FEAT_RIGHTLEFT
193 || (curwin
->w_p_rl
&& (col
> PUM_DEF_WIDTH
|| col
> max_width
)
197 /* align pum column with "col" */
200 #ifdef FEAT_RIGHTLEFT
202 pum_width
= pum_col
- pum_scrollbar
+ 1;
205 pum_width
= Columns
- pum_col
- pum_scrollbar
;
207 if (pum_width
> max_width
+ kind_width
+ extra_width
+ 1
208 && pum_width
> PUM_DEF_WIDTH
)
210 pum_width
= max_width
+ kind_width
+ extra_width
+ 1;
211 if (pum_width
< PUM_DEF_WIDTH
)
212 pum_width
= PUM_DEF_WIDTH
;
215 else if (Columns
< def_width
)
217 /* not enough room, will use what we have */
218 #ifdef FEAT_RIGHTLEFT
220 pum_col
= Columns
- 1;
224 pum_width
= Columns
- 1;
228 if (max_width
> PUM_DEF_WIDTH
)
229 max_width
= PUM_DEF_WIDTH
; /* truncate */
230 #ifdef FEAT_RIGHTLEFT
232 pum_col
= max_width
- 1;
235 pum_col
= Columns
- max_width
;
236 pum_width
= max_width
- pum_scrollbar
;
242 /* Set selected item and redraw. If the window size changed need to redo
243 * the positioning. Limit this to two times, when there is not much
244 * room the window size will keep changing. */
245 if (pum_set_selected(selected
, redo_count
) && ++redo_count
<= 2)
250 * Redraw the popup menu, using "pum_first" and "pum_selected".
257 int attr_norm
= highlight_attr
[HLF_PNI
];
258 int attr_select
= highlight_attr
[HLF_PSI
];
259 int attr_scroll
= highlight_attr
[HLF_PSB
];
260 int attr_thumb
= highlight_attr
[HLF_PST
];
266 int totwidth
, width
, w
;
268 int thumb_heigth
= 1;
274 thumb_heigth
= pum_height
* pum_height
/ pum_size
;
275 if (thumb_heigth
== 0)
277 thumb_pos
= (pum_first
* (pum_height
- thumb_heigth
)
278 + (pum_size
- pum_height
) / 2)
279 / (pum_size
- pum_height
);
282 for (i
= 0; i
< pum_height
; ++i
)
285 attr
= (idx
== pum_selected
) ? attr_select
: attr_norm
;
287 /* prepend a space if there is room */
288 #ifdef FEAT_RIGHTLEFT
291 if (pum_col
< W_WINCOL(curwin
) + W_WIDTH(curwin
) - 1)
292 screen_putchar(' ', row
, pum_col
+ 1, attr
);
297 screen_putchar(' ', row
, pum_col
- 1, attr
);
299 /* Display each entry, use two spaces for a Tab.
300 * Do this 3 times: For the main text, kind and extra info */
303 for (round
= 1; round
<= 3; ++round
)
309 case 1: p
= pum_array
[idx
].pum_text
; break;
310 case 2: p
= pum_array
[idx
].pum_kind
; break;
311 case 3: p
= pum_array
[idx
].pum_extra
; break;
314 for ( ; ; mb_ptr_adv(p
))
319 if (*p
== NUL
|| *p
== TAB
|| totwidth
+ w
> pum_width
)
321 /* Display the text that fits or comes before a Tab.
322 * First convert it to printable characters. */
329 #ifdef FEAT_RIGHTLEFT
334 char_u
*rt
= reverse_text(st
);
335 char_u
*rt_saved
= rt
;
343 for (j
= pum_width
; j
< len
; ++j
)
347 screen_puts_len(rt
, len
, row
,
348 col
- len
+ 1, attr
);
360 screen_puts_len(st
, (int)STRLEN(st
), row
, col
,
370 /* Display two spaces for a Tab. */
371 #ifdef FEAT_RIGHTLEFT
374 screen_puts_len((char_u
*)" ", 2, row
, col
- 1,
381 screen_puts_len((char_u
*)" ", 2, row
, col
, attr
);
385 s
= NULL
; /* start text at next char */
393 n
= pum_kind_width
+ 1;
397 /* Stop when there is nothing more to display. */
399 || (round
== 2 && pum_array
[idx
].pum_extra
== NULL
)
400 || (round
== 1 && pum_array
[idx
].pum_kind
== NULL
401 && pum_array
[idx
].pum_extra
== NULL
)
402 || pum_base_width
+ n
>= pum_width
)
404 #ifdef FEAT_RIGHTLEFT
407 screen_fill(row
, row
+ 1, pum_col
- pum_base_width
- n
+ 1,
408 col
+ 1, ' ', ' ', attr
);
409 col
= pum_col
- pum_base_width
- n
+ 1;
414 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_base_width
+ n
,
416 col
= pum_col
+ pum_base_width
+ n
;
418 totwidth
= pum_base_width
+ n
;
421 #ifdef FEAT_RIGHTLEFT
423 screen_fill(row
, row
+ 1, pum_col
- pum_width
+ 1, col
+ 1, ' ',
427 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_width
, ' ', ' ',
429 if (pum_scrollbar
> 0)
431 #ifdef FEAT_RIGHTLEFT
433 screen_putchar(' ', row
, pum_col
- pum_width
,
434 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
435 ? attr_thumb
: attr_scroll
);
438 screen_putchar(' ', row
, pum_col
+ pum_width
,
439 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
440 ? attr_thumb
: attr_scroll
);
447 #if 0 /* not used yet */
449 * Return the index of the currently selected item.
459 * Set the index of the currently selected item. The menu will scroll when
460 * necessary. When "n" is out of range don't scroll.
461 * This may be repeated when the preview window is used:
462 * "repeat" == 0: open preview window normally
463 * "repeat" == 1: open preview window but don't set the size
464 * "repeat" == 2: don't open preview window
465 * Returns TRUE when the window was resized and the location of the popup menu
466 * must be recomputed.
469 pum_set_selected(n
, repeat
)
474 int context
= pum_height
/ 2;
478 if (pum_selected
>= 0 && pum_selected
< pum_size
)
480 if (pum_first
> pum_selected
- 4)
482 /* scroll down; when we did a jump it's probably a PageUp then
483 * scroll a whole page */
484 if (pum_first
> pum_selected
- 2)
486 pum_first
-= pum_height
- 2;
489 else if (pum_first
> pum_selected
)
490 pum_first
= pum_selected
;
493 pum_first
= pum_selected
;
495 else if (pum_first
< pum_selected
- pum_height
+ 5)
497 /* scroll up; when we did a jump it's probably a PageDown then
498 * scroll a whole page */
499 if (pum_first
< pum_selected
- pum_height
+ 1 + 2)
501 pum_first
+= pum_height
- 2;
502 if (pum_first
< pum_selected
- pum_height
+ 1)
503 pum_first
= pum_selected
- pum_height
+ 1;
506 pum_first
= pum_selected
- pum_height
+ 1;
509 /* Give a few lines of context when possible. */
514 if (pum_first
> pum_selected
- context
)
517 pum_first
= pum_selected
- context
;
521 else if (pum_first
< pum_selected
+ context
- pum_height
+ 1)
524 pum_first
= pum_selected
+ context
- pum_height
+ 1;
528 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
530 * Show extra info in the preview window if there is something and
531 * 'completeopt' contains "preview".
532 * Skip this when tried twice already.
533 * Skip this also when there is not much room.
534 * NOTE: Be very careful not to sync undo!
536 if (pum_array
[pum_selected
].pum_info
!= NULL
539 && vim_strchr(p_cot
, 'p') != NULL
)
541 win_T
*curwin_save
= curwin
;
544 /* Open a preview window. 3 lines by default. */
546 resized
= prepare_tagpreview(FALSE
);
551 if (curbuf
->b_fname
== NULL
552 && curbuf
->b_p_bt
[0] == 'n' && curbuf
->b_p_bt
[2] == 'f'
553 && curbuf
->b_p_bh
[0] == 'w')
555 /* Already a "wipeout" buffer, make it empty. */
557 ml_delete((linenr_T
)1, FALSE
);
561 /* Don't want to sync undo in the current buffer. */
563 res
= do_ecmd(0, NULL
, NULL
, NULL
, ECMD_ONE
, 0);
567 /* Edit a new, empty buffer. Set options for a "wipeout"
569 set_option_value((char_u
*)"swf", 0L, NULL
, OPT_LOCAL
);
570 set_option_value((char_u
*)"bt", 0L,
571 (char_u
*)"nofile", OPT_LOCAL
);
572 set_option_value((char_u
*)"bh", 0L,
573 (char_u
*)"wipe", OPT_LOCAL
);
574 set_option_value((char_u
*)"diff", 0L,
583 for (p
= pum_array
[pum_selected
].pum_info
; *p
!= NUL
; )
585 e
= vim_strchr(p
, '\n');
588 ml_append(lnum
++, p
, 0, FALSE
);
594 ml_append(lnum
++, p
, (int)(e
- p
+ 1), FALSE
);
600 /* Increase the height of the preview window to show the
601 * text, but no more than 'previewheight' lines. */
606 if (curwin
->w_height
< lnum
)
608 win_setheight((int)lnum
);
613 curbuf
->b_changed
= 0;
614 curbuf
->b_p_ma
= FALSE
;
615 curwin
->w_cursor
.lnum
= 0;
616 curwin
->w_cursor
.col
= 0;
618 if (curwin
!= curwin_save
&& win_valid(curwin_save
))
620 /* Return cursor to where we were */
622 redraw_later(SOME_VALID
);
624 /* When the preview window was resized we need to
625 * update the view on the buffer. Only go back to
626 * the window when needed, otherwise it will always be
630 win_enter(curwin_save
, TRUE
);
634 /* Update the screen before drawing the popup menu.
635 * Enable updating the status lines. */
636 pum_do_redraw
= TRUE
;
638 pum_do_redraw
= FALSE
;
640 if (!resized
&& win_valid(curwin_save
))
641 win_enter(curwin_save
, TRUE
);
643 /* May need to update the screen again when there are
644 * autocommands involved. */
645 pum_do_redraw
= TRUE
;
647 pum_do_redraw
= FALSE
;
655 /* Never display more than we have */
656 if (pum_first
> pum_size
- pum_height
)
657 pum_first
= pum_size
- pum_height
;
666 * Undisplay the popup menu (later).
672 redraw_all_later(SOME_VALID
);
674 redraw_tabline
= TRUE
;
680 * Clear the popup menu. Currently only resets the offset to the first
690 * Return TRUE if the popup menu is displayed.
691 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
696 return !pum_do_redraw
&& pum_array
!= NULL
;
700 * Return the height of the popup menu, the number of entries visible.
701 * Only valid when pum_visible() returns TRUE!