Skip to content

Commit 246fe03

Browse files
committed
patch 8.0.1318: terminal balloon only shows one line
Problem: Terminal balloon only shows one line. Solution: Split into several lines in a clever way. Add balloon_split(). Make balloon_show() accept a list in the terminal.
1 parent e518226 commit 246fe03

9 files changed

Lines changed: 263 additions & 26 deletions

File tree

runtime/doc/eval.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,7 @@ asin({expr}) Float arc sine of {expr}
20322032
atan({expr}) Float arc tangent of {expr}
20332033
atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2}
20342034
balloon_show({msg}) none show {msg} inside the balloon
2035+
balloon_split({msg}) List split {msg} as used for a balloon
20352036
browse({save}, {title}, {initdir}, {default})
20362037
String put up a file requester
20372038
browsedir({title}, {initdir}) String put up a directory requester
@@ -2682,8 +2683,12 @@ atan2({expr1}, {expr2}) *atan2()*
26822683
< 2.356194
26832684
{only available when compiled with the |+float| feature}
26842685

2685-
balloon_show({msg}) *balloon_show()*
2686-
Show {msg} inside the balloon.
2686+
balloon_show({expr}) *balloon_show()*
2687+
Show {expr} inside the balloon. For the GUI {expr} is used as
2688+
a string. For a terminal {expr} can be a list, which contains
2689+
the lines of the balloon. If {expr} is not a list it will be
2690+
split with |balloon_split()|.
2691+
26872692
Example: >
26882693
func GetBalloonContent()
26892694
" initiate getting the content
@@ -2705,6 +2710,12 @@ balloon_show({msg}) *balloon_show()*
27052710
error message.
27062711
{only available when compiled with the +balloon_eval feature}
27072712

2713+
balloon_split({msg}) *balloon_split()*
2714+
Split {msg} into lines to be displayed in a balloon. The
2715+
splits are made for the current window size and optimize to
2716+
show debugger output.
2717+
Returns a |List| with the split lines.
2718+
27082719
*browse()*
27092720
browse({save}, {title}, {initdir}, {default})
27102721
Put up a file requester. This only works when "has("browse")"

runtime/pack/dist/opt/termdebug/plugin/termdebug.vim

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,11 @@ func s:StartDebug(cmd)
127127
call win_gotoid(s:gdbwin)
128128

129129
" Enable showing a balloon with eval info
130-
if has("balloon_eval")
131-
set ballooneval
130+
if has("balloon_eval") || has("balloon_eval_term")
132131
set balloonexpr=TermDebugBalloonExpr()
132+
if has("balloon_eval")
133+
set ballooneval
134+
endif
133135
if has("balloon_eval_term")
134136
set balloonevalterm
135137
endif
@@ -158,9 +160,11 @@ func s:EndDebug(job, status)
158160
let &columns = s:save_columns
159161
endif
160162

161-
if has("balloon_eval")
162-
set noballooneval
163+
if has("balloon_eval") || has("balloon_eval_term")
163164
set balloonexpr=
165+
if has("balloon_eval")
166+
set noballooneval
167+
endif
164168
if has("balloon_eval_term")
165169
set noballoonevalterm
166170
endif
@@ -366,6 +370,7 @@ func s:HandleError(msg)
366370
if a:msg =~ 'No symbol .* in current context'
367371
\ || a:msg =~ 'Cannot access memory at address '
368372
\ || a:msg =~ 'Attempt to use a type name as an expression'
373+
\ || a:msg =~ 'A syntax error in expression,'
369374
" Result of s:SendEval() failed, ignore.
370375
return
371376
endif

src/beval.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,20 @@ get_beval_info(
134134
}
135135

136136
/*
137-
* Show a balloon with "mesg".
137+
* Show a balloon with "mesg" or "list".
138138
*/
139139
void
140-
post_balloon(BalloonEval *beval UNUSED, char_u *mesg)
140+
post_balloon(BalloonEval *beval UNUSED, char_u *mesg, list_T *list)
141141
{
142142
# ifdef FEAT_BEVAL_TERM
143143
# ifdef FEAT_GUI
144144
if (!gui.in_use)
145145
# endif
146-
ui_post_balloon(mesg);
146+
ui_post_balloon(mesg, list);
147147
# endif
148148
# ifdef FEAT_BEVAL_GUI
149149
if (gui.in_use)
150+
/* GUI can't handle a list */
150151
gui_mch_post_balloon(beval, mesg);
151152
# endif
152153
}
@@ -257,7 +258,7 @@ general_beval_cb(BalloonEval *beval, int state UNUSED)
257258
set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
258259
if (result != NULL && result[0] != NUL)
259260
{
260-
post_balloon(beval, result);
261+
post_balloon(beval, result, NULL);
261262
recursive = FALSE;
262263
return;
263264
}

src/evalfunc.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ static void f_atan2(typval_T *argvars, typval_T *rettv);
6161
#endif
6262
#ifdef FEAT_BEVAL
6363
static void f_balloon_show(typval_T *argvars, typval_T *rettv);
64+
static void f_balloon_split(typval_T *argvars, typval_T *rettv);
6465
#endif
6566
static void f_browse(typval_T *argvars, typval_T *rettv);
6667
static void f_browsedir(typval_T *argvars, typval_T *rettv);
@@ -494,6 +495,7 @@ static struct fst
494495
#endif
495496
#ifdef FEAT_BEVAL
496497
{"balloon_show", 1, 1, f_balloon_show},
498+
{"balloon_split", 1, 1, f_balloon_split},
497499
#endif
498500
{"browse", 4, 4, f_browse},
499501
{"browsedir", 2, 2, f_browsedir},
@@ -1410,7 +1412,37 @@ f_atan2(typval_T *argvars, typval_T *rettv)
14101412
f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
14111413
{
14121414
if (balloonEval != NULL)
1413-
post_balloon(balloonEval, get_tv_string_chk(&argvars[0]));
1415+
{
1416+
if (argvars[0].v_type == VAR_LIST
1417+
# ifdef FEAT_GUI
1418+
&& !gui.in_use
1419+
# endif
1420+
)
1421+
post_balloon(balloonEval, NULL, argvars[0].vval.v_list);
1422+
else
1423+
post_balloon(balloonEval, get_tv_string_chk(&argvars[0]), NULL);
1424+
}
1425+
}
1426+
1427+
static void
1428+
f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
1429+
{
1430+
if (rettv_list_alloc(rettv) == OK)
1431+
{
1432+
char_u *msg = get_tv_string_chk(&argvars[0]);
1433+
1434+
if (msg != NULL)
1435+
{
1436+
pumitem_T *array;
1437+
int size = split_message(msg, &array);
1438+
int i;
1439+
1440+
/* Skip the first and last item, they are always empty. */
1441+
for (i = 1; i < size - 1; ++i)
1442+
list_append_string(rettv->vval.v_list, array[i].pum_text, -1);
1443+
vim_free(array);
1444+
}
1445+
}
14141446
}
14151447
#endif
14161448

src/popupmnu.c

Lines changed: 165 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -766,9 +766,147 @@ static int balloon_arraysize;
766766
static int balloon_mouse_row = 0;
767767
static int balloon_mouse_col = 0;
768768

769-
#define BALLOON_MIN_WIDTH 40
769+
#define BALLOON_MIN_WIDTH 50
770770
#define BALLOON_MIN_HEIGHT 10
771771

772+
typedef struct {
773+
char_u *start;
774+
int bytelen;
775+
int cells;
776+
int indent;
777+
} balpart_T;
778+
779+
/*
780+
* Split a string into parts to display in the balloon.
781+
* Aimed at output from gdb. Attempts to split at white space, preserve quoted
782+
* strings and make a struct look good.
783+
* Resulting array is stored in "array" and returns the size of the array.
784+
*/
785+
int
786+
split_message(char_u *mesg, pumitem_T **array)
787+
{
788+
garray_T ga;
789+
char_u *p;
790+
balpart_T *item;
791+
int quoted = FALSE;
792+
int height;
793+
int line;
794+
int item_idx;
795+
int indent = 0;
796+
int max_cells = 0;
797+
int max_height = Rows / 2 - 2;
798+
int long_item_count = 0;
799+
int split_long_items = FALSE;
800+
801+
ga_init2(&ga, sizeof(balpart_T), 20);
802+
p = mesg;
803+
804+
while (*p != NUL)
805+
{
806+
if (ga_grow(&ga, 1) == FAIL)
807+
goto failed;
808+
item = ((balpart_T *)ga.ga_data) + ga.ga_len;
809+
item->start = p;
810+
item->indent = indent;
811+
item->cells = indent * 2;
812+
++ga.ga_len;
813+
while (*p != NUL)
814+
{
815+
if (*p == '"')
816+
quoted = !quoted;
817+
else if (*p == '\\' && p[1] != NUL)
818+
++p;
819+
else if (!quoted)
820+
{
821+
if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}')
822+
{
823+
/* Looks like a good point to break. */
824+
if (*p == '{')
825+
++indent;
826+
else if (*p == '}' && indent > 0)
827+
--indent;
828+
++item->cells;
829+
p = skipwhite(p + 1);
830+
break;
831+
}
832+
}
833+
item->cells += ptr2cells(p);
834+
p += MB_PTR2LEN(p);
835+
}
836+
item->bytelen = p - item->start;
837+
if (item->cells > max_cells)
838+
max_cells = item->cells;
839+
long_item_count += item->cells / BALLOON_MIN_WIDTH;
840+
}
841+
842+
height = 2 + ga.ga_len;
843+
844+
/* If there are long items and the height is below the limit: split lines */
845+
if (long_item_count > 0 && height + long_item_count <= max_height)
846+
{
847+
split_long_items = TRUE;
848+
height += long_item_count;
849+
}
850+
851+
/* Limit to half the window height, it has to fit above or below the mouse
852+
* position. */
853+
if (height > max_height)
854+
height = max_height;
855+
*array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * height);
856+
if (*array == NULL)
857+
goto failed;
858+
859+
/* Add an empty line above and below, looks better. */
860+
(*array)->pum_text = vim_strsave((char_u *)"");
861+
(*array + height - 1)->pum_text = vim_strsave((char_u *)"");
862+
863+
for (line = 1, item_idx = 0; line < height - 1; ++item_idx)
864+
{
865+
int skip;
866+
int thislen;
867+
int copylen;
868+
int ind;
869+
int cells;
870+
871+
item = ((balpart_T *)ga.ga_data) + item_idx;
872+
for (skip = 0; skip < item->bytelen; skip += thislen)
873+
{
874+
if (split_long_items && item->cells >= BALLOON_MIN_WIDTH)
875+
{
876+
cells = item->indent * 2;
877+
for (p = item->start + skip; p < item->start + item->bytelen;
878+
p += MB_PTR2LEN(p))
879+
if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH)
880+
break;
881+
thislen = p - (item->start + skip);
882+
}
883+
else
884+
thislen = item->bytelen;
885+
886+
/* put indent at the start */
887+
p = alloc(thislen + item->indent * 2 + 1);
888+
for (ind = 0; ind < item->indent * 2; ++ind)
889+
p[ind] = ' ';
890+
891+
/* exclude spaces at the end of the string */
892+
for (copylen = thislen; copylen > 0; --copylen)
893+
if (item->start[skip + copylen - 1] != ' ')
894+
break;
895+
896+
vim_strncpy(p + ind, item->start + skip, copylen);
897+
(*array)[line].pum_text = p;
898+
item->indent = 0; /* wrapped line has no indent */
899+
++line;
900+
}
901+
}
902+
ga_clear(&ga);
903+
return height;
904+
905+
failed:
906+
ga_clear(&ga);
907+
return 0;
908+
}
909+
772910
void
773911
ui_remove_balloon(void)
774912
{
@@ -786,28 +924,42 @@ ui_remove_balloon(void)
786924
* Terminal version of a balloon, uses the popup menu code.
787925
*/
788926
void
789-
ui_post_balloon(char_u *mesg)
927+
ui_post_balloon(char_u *mesg, list_T *list)
790928
{
791929
ui_remove_balloon();
792930

793-
/* TODO: split the text in multiple lines. */
794-
balloon_arraysize = 3;
795-
balloon_array = (pumitem_T *)alloc_clear(
796-
(unsigned)sizeof(pumitem_T) * balloon_arraysize);
797-
if (balloon_array != NULL)
931+
if (mesg == NULL && list == NULL)
932+
return;
933+
if (list != NULL)
798934
{
799-
/* Add an empty line above and below, looks better. */
800-
balloon_array[0].pum_text = vim_strsave((char_u *)"");
801-
balloon_array[1].pum_text = vim_strsave(mesg);
802-
balloon_array[2].pum_text = vim_strsave((char_u *)"");
935+
listitem_T *li;
936+
int idx;
937+
938+
balloon_arraysize = list->lv_len;
939+
balloon_array = (pumitem_T *)alloc_clear(
940+
(unsigned)sizeof(pumitem_T) * list->lv_len);
941+
if (balloon_array == NULL)
942+
return;
943+
for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx)
944+
{
945+
char_u *text = get_tv_string_chk(&li->li_tv);
803946

947+
balloon_array[idx].pum_text = vim_strsave(
948+
text == NULL ? (char_u *)"" : text);
949+
}
950+
}
951+
else
952+
balloon_arraysize = split_message(mesg, &balloon_array);
953+
954+
if (balloon_arraysize > 0)
955+
{
804956
pum_array = balloon_array;
805957
pum_size = balloon_arraysize;
806958
pum_compute_size();
807959
pum_scrollbar = 0;
808960
pum_height = balloon_arraysize;
809961

810-
if (Rows - mouse_row > BALLOON_MIN_HEIGHT)
962+
if (Rows - mouse_row > pum_size)
811963
{
812964
/* Enough space below the mouse row. */
813965
pum_row = mouse_row + 1;
@@ -817,7 +969,7 @@ ui_post_balloon(char_u *mesg)
817969
else
818970
{
819971
/* Show above the mouse row, reduce height if it does not fit. */
820-
pum_row = mouse_row - 1 - pum_size;
972+
pum_row = mouse_row - pum_size;
821973
if (pum_row < 0)
822974
{
823975
pum_height += pum_row;

src/proto/beval.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* beval.c */
22
int get_beval_info(BalloonEval *beval, int getword, win_T **winp, linenr_T *lnump, char_u **textp, int *colp);
3-
void post_balloon(BalloonEval *beval, char_u *mesg);
3+
void post_balloon(BalloonEval *beval, char_u *mesg, list_T *list);
44
int can_use_beval(void);
55
void general_beval_cb(BalloonEval *beval, int state);
66
/* vim: set ft=c : */

src/proto/popupmnu.pro

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ void pum_undisplay(void);
55
void pum_clear(void);
66
int pum_visible(void);
77
int pum_get_height(void);
8+
int split_message(char_u *mesg, pumitem_T **array);
89
void ui_remove_balloon(void);
9-
void ui_post_balloon(char_u *mesg);
10+
void ui_post_balloon(char_u *mesg, list_T *list);
1011
void ui_may_remove_balloon(void);
1112
/* vim: set ft=c : */

0 commit comments

Comments
 (0)