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
);
351 char_u
*rt_start
= rt
;
354 size
= vim_strsize(rt
);
355 if (size
> pum_width
)
360 ? (*mb_ptr2cells
)(rt
) : 1;
362 } while (size
> pum_width
);
364 if (size
< pum_width
)
366 /* Most left character requires
367 * 2-cells but only 1 cell is
368 * available on screen. Put a
369 * '<' on the left of the pum
375 screen_puts_len(rt
, (int)STRLEN(rt
),
376 row
, col
- size
+ 1, attr
);
388 screen_puts_len(st
, (int)STRLEN(st
), row
, col
,
398 /* Display two spaces for a Tab. */
399 #ifdef FEAT_RIGHTLEFT
402 screen_puts_len((char_u
*)" ", 2, row
, col
- 1,
409 screen_puts_len((char_u
*)" ", 2, row
, col
, attr
);
413 s
= NULL
; /* start text at next char */
421 n
= pum_kind_width
+ 1;
425 /* Stop when there is nothing more to display. */
427 || (round
== 2 && pum_array
[idx
].pum_extra
== NULL
)
428 || (round
== 1 && pum_array
[idx
].pum_kind
== NULL
429 && pum_array
[idx
].pum_extra
== NULL
)
430 || pum_base_width
+ n
>= pum_width
)
432 #ifdef FEAT_RIGHTLEFT
435 screen_fill(row
, row
+ 1, pum_col
- pum_base_width
- n
+ 1,
436 col
+ 1, ' ', ' ', attr
);
437 col
= pum_col
- pum_base_width
- n
+ 1;
442 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_base_width
+ n
,
444 col
= pum_col
+ pum_base_width
+ n
;
446 totwidth
= pum_base_width
+ n
;
449 #ifdef FEAT_RIGHTLEFT
451 screen_fill(row
, row
+ 1, pum_col
- pum_width
+ 1, col
+ 1, ' ',
455 screen_fill(row
, row
+ 1, col
, pum_col
+ pum_width
, ' ', ' ',
457 if (pum_scrollbar
> 0)
459 #ifdef FEAT_RIGHTLEFT
461 screen_putchar(' ', row
, pum_col
- pum_width
,
462 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
463 ? attr_thumb
: attr_scroll
);
466 screen_putchar(' ', row
, pum_col
+ pum_width
,
467 i
>= thumb_pos
&& i
< thumb_pos
+ thumb_heigth
468 ? attr_thumb
: attr_scroll
);
475 #if 0 /* not used yet */
477 * Return the index of the currently selected item.
487 * Set the index of the currently selected item. The menu will scroll when
488 * necessary. When "n" is out of range don't scroll.
489 * This may be repeated when the preview window is used:
490 * "repeat" == 0: open preview window normally
491 * "repeat" == 1: open preview window but don't set the size
492 * "repeat" == 2: don't open preview window
493 * Returns TRUE when the window was resized and the location of the popup menu
494 * must be recomputed.
497 pum_set_selected(n
, repeat
)
502 int context
= pum_height
/ 2;
506 if (pum_selected
>= 0 && pum_selected
< pum_size
)
508 if (pum_first
> pum_selected
- 4)
510 /* scroll down; when we did a jump it's probably a PageUp then
511 * scroll a whole page */
512 if (pum_first
> pum_selected
- 2)
514 pum_first
-= pum_height
- 2;
517 else if (pum_first
> pum_selected
)
518 pum_first
= pum_selected
;
521 pum_first
= pum_selected
;
523 else if (pum_first
< pum_selected
- pum_height
+ 5)
525 /* scroll up; when we did a jump it's probably a PageDown then
526 * scroll a whole page */
527 if (pum_first
< pum_selected
- pum_height
+ 1 + 2)
529 pum_first
+= pum_height
- 2;
530 if (pum_first
< pum_selected
- pum_height
+ 1)
531 pum_first
= pum_selected
- pum_height
+ 1;
534 pum_first
= pum_selected
- pum_height
+ 1;
537 /* Give a few lines of context when possible. */
542 if (pum_first
> pum_selected
- context
)
545 pum_first
= pum_selected
- context
;
549 else if (pum_first
< pum_selected
+ context
- pum_height
+ 1)
552 pum_first
= pum_selected
+ context
- pum_height
+ 1;
556 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
558 * Show extra info in the preview window if there is something and
559 * 'completeopt' contains "preview".
560 * Skip this when tried twice already.
561 * Skip this also when there is not much room.
562 * NOTE: Be very careful not to sync undo!
564 if (pum_array
[pum_selected
].pum_info
!= NULL
567 && vim_strchr(p_cot
, 'p') != NULL
)
569 win_T
*curwin_save
= curwin
;
572 /* Open a preview window. 3 lines by default. */
574 resized
= prepare_tagpreview(FALSE
);
579 if (curbuf
->b_fname
== NULL
580 && curbuf
->b_p_bt
[0] == 'n' && curbuf
->b_p_bt
[2] == 'f'
581 && curbuf
->b_p_bh
[0] == 'w')
583 /* Already a "wipeout" buffer, make it empty. */
585 ml_delete((linenr_T
)1, FALSE
);
589 /* Don't want to sync undo in the current buffer. */
591 res
= do_ecmd(0, NULL
, NULL
, NULL
, ECMD_ONE
, 0, NULL
);
595 /* Edit a new, empty buffer. Set options for a "wipeout"
597 set_option_value((char_u
*)"swf", 0L, NULL
, OPT_LOCAL
);
598 set_option_value((char_u
*)"bt", 0L,
599 (char_u
*)"nofile", OPT_LOCAL
);
600 set_option_value((char_u
*)"bh", 0L,
601 (char_u
*)"wipe", OPT_LOCAL
);
602 set_option_value((char_u
*)"diff", 0L,
611 for (p
= pum_array
[pum_selected
].pum_info
; *p
!= NUL
; )
613 e
= vim_strchr(p
, '\n');
616 ml_append(lnum
++, p
, 0, FALSE
);
622 ml_append(lnum
++, p
, (int)(e
- p
+ 1), FALSE
);
628 /* Increase the height of the preview window to show the
629 * text, but no more than 'previewheight' lines. */
634 if (curwin
->w_height
< lnum
)
636 win_setheight((int)lnum
);
641 curbuf
->b_changed
= 0;
642 curbuf
->b_p_ma
= FALSE
;
643 curwin
->w_cursor
.lnum
= 1;
644 curwin
->w_cursor
.col
= 0;
646 if (curwin
!= curwin_save
&& win_valid(curwin_save
))
648 /* Return cursor to where we were */
650 redraw_later(SOME_VALID
);
652 /* When the preview window was resized we need to
653 * update the view on the buffer. Only go back to
654 * the window when needed, otherwise it will always be
658 win_enter(curwin_save
, TRUE
);
662 /* Update the screen before drawing the popup menu.
663 * Enable updating the status lines. */
664 pum_do_redraw
= TRUE
;
666 pum_do_redraw
= FALSE
;
668 if (!resized
&& win_valid(curwin_save
))
669 win_enter(curwin_save
, TRUE
);
671 /* May need to update the screen again when there are
672 * autocommands involved. */
673 pum_do_redraw
= TRUE
;
675 pum_do_redraw
= FALSE
;
683 /* Never display more than we have */
684 if (pum_first
> pum_size
- pum_height
)
685 pum_first
= pum_size
- pum_height
;
694 * Undisplay the popup menu (later).
700 redraw_all_later(SOME_VALID
);
702 redraw_tabline
= TRUE
;
708 * Clear the popup menu. Currently only resets the offset to the first
718 * Return TRUE if the popup menu is displayed.
719 * Overruled when "pum_do_redraw" is set, used to redraw the status lines.
724 return !pum_do_redraw
&& pum_array
!= NULL
;
728 * Return the height of the popup menu, the number of entries visible.
729 * Only valid when pum_visible() returns TRUE!