Patch 8.0.1318
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.
Files: src/popupmnu.c, src/proto/popupmnu.pro, src/evalfunc.c,
src/beval.c, src/proto/beval.pro, src/testdir/test_popup.vim,
runtime/doc/eval.txt,
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
*** ../vim-8.0.1317/src/popupmnu.c 2017-11-18 22:13:04.753908641 +0100
--- src/popupmnu.c 2017-11-19 19:50:08.635685174 +0100
***************
*** 766,774 ****
static int balloon_mouse_row = 0;
static int balloon_mouse_col = 0;
! #define BALLOON_MIN_WIDTH 40
#define BALLOON_MIN_HEIGHT 10
void
ui_remove_balloon(void)
{
--- 766,912 ----
static int balloon_mouse_row = 0;
static int balloon_mouse_col = 0;
! #define BALLOON_MIN_WIDTH 50
#define BALLOON_MIN_HEIGHT 10
+ typedef struct {
+ char_u *start;
+ int bytelen;
+ int cells;
+ int indent;
+ } balpart_T;
+
+ /*
+ * Split a string into parts to display in the balloon.
+ * Aimed at output from gdb. Attempts to split at white space, preserve
quoted
+ * strings and make a struct look good.
+ * Resulting array is stored in "array" and returns the size of the array.
+ */
+ int
+ split_message(char_u *mesg, pumitem_T **array)
+ {
+ garray_T ga;
+ char_u *p;
+ balpart_T *item;
+ int quoted = FALSE;
+ int height;
+ int line;
+ int item_idx;
+ int indent = 0;
+ int max_cells = 0;
+ int max_height = Rows / 2 - 2;
+ int long_item_count = 0;
+ int split_long_items = FALSE;
+
+ ga_init2(&ga, sizeof(balpart_T), 20);
+ p = mesg;
+
+ while (*p != NUL)
+ {
+ if (ga_grow(&ga, 1) == FAIL)
+ goto failed;
+ item = ((balpart_T *)ga.ga_data) + ga.ga_len;
+ item->start = p;
+ item->indent = indent;
+ item->cells = indent * 2;
+ ++ga.ga_len;
+ while (*p != NUL)
+ {
+ if (*p == '"')
+ quoted = !quoted;
+ else if (*p == '\\' && p[1] != NUL)
+ ++p;
+ else if (!quoted)
+ {
+ if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}')
+ {
+ /* Looks like a good point to break. */
+ if (*p == '{')
+ ++indent;
+ else if (*p == '}' && indent > 0)
+ --indent;
+ ++item->cells;
+ p = skipwhite(p + 1);
+ break;
+ }
+ }
+ item->cells += ptr2cells(p);
+ p += MB_PTR2LEN(p);
+ }
+ item->bytelen = p - item->start;
+ if (item->cells > max_cells)
+ max_cells = item->cells;
+ long_item_count += item->cells / BALLOON_MIN_WIDTH;
+ }
+
+ height = 2 + ga.ga_len;
+
+ /* If there are long items and the height is below the limit: split lines
*/
+ if (long_item_count > 0 && height + long_item_count <= max_height)
+ {
+ split_long_items = TRUE;
+ height += long_item_count;
+ }
+
+ /* Limit to half the window height, it has to fit above or below the mouse
+ * position. */
+ if (height > max_height)
+ height = max_height;
+ *array = (pumitem_T *)alloc_clear((unsigned)sizeof(pumitem_T) * height);
+ if (*array == NULL)
+ goto failed;
+
+ /* Add an empty line above and below, looks better. */
+ (*array)->pum_text = vim_strsave((char_u *)"");
+ (*array + height - 1)->pum_text = vim_strsave((char_u *)"");
+
+ for (line = 1, item_idx = 0; line < height - 1; ++item_idx)
+ {
+ int skip;
+ int thislen;
+ int copylen;
+ int ind;
+ int cells;
+
+ item = ((balpart_T *)ga.ga_data) + item_idx;
+ for (skip = 0; skip < item->bytelen; skip += thislen)
+ {
+ if (split_long_items && item->cells >= BALLOON_MIN_WIDTH)
+ {
+ cells = item->indent * 2;
+ for (p = item->start + skip; p < item->start + item->bytelen;
+ p += MB_PTR2LEN(p))
+ if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH)
+ break;
+ thislen = p - (item->start + skip);
+ }
+ else
+ thislen = item->bytelen;
+
+ /* put indent at the start */
+ p = alloc(thislen + item->indent * 2 + 1);
+ for (ind = 0; ind < item->indent * 2; ++ind)
+ p[ind] = ' ';
+
+ /* exclude spaces at the end of the string */
+ for (copylen = thislen; copylen > 0; --copylen)
+ if (item->start[skip + copylen - 1] != ' ')
+ break;
+
+ vim_strncpy(p + ind, item->start + skip, copylen);
+ (*array)[line].pum_text = p;
+ item->indent = 0; /* wrapped line has no indent */
+ ++line;
+ }
+ }
+ ga_clear(&ga);
+ return height;
+
+ failed:
+ ga_clear(&ga);
+ return 0;
+ }
+
void
ui_remove_balloon(void)
{
***************
*** 786,813 ****
* Terminal version of a balloon, uses the popup menu code.
*/
void
! ui_post_balloon(char_u *mesg)
{
ui_remove_balloon();
! /* TODO: split the text in multiple lines. */
! balloon_arraysize = 3;
! balloon_array = (pumitem_T *)alloc_clear(
! (unsigned)sizeof(pumitem_T) * balloon_arraysize);
! if (balloon_array != NULL)
{
! /* Add an empty line above and below, looks better. */
! balloon_array[0].pum_text = vim_strsave((char_u *)"");
! balloon_array[1].pum_text = vim_strsave(mesg);
! balloon_array[2].pum_text = vim_strsave((char_u *)"");
pum_array = balloon_array;
pum_size = balloon_arraysize;
pum_compute_size();
pum_scrollbar = 0;
pum_height = balloon_arraysize;
! if (Rows - mouse_row > BALLOON_MIN_HEIGHT)
{
/* Enough space below the mouse row. */
pum_row = mouse_row + 1;
--- 924,965 ----
* Terminal version of a balloon, uses the popup menu code.
*/
void
! ui_post_balloon(char_u *mesg, list_T *list)
{
ui_remove_balloon();
! if (mesg == NULL && list == NULL)
! return;
! if (list != NULL)
{
! listitem_T *li;
! int idx;
!
! balloon_arraysize = list->lv_len;
! balloon_array = (pumitem_T *)alloc_clear(
! (unsigned)sizeof(pumitem_T) * list->lv_len);
! if (balloon_array == NULL)
! return;
! for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx)
! {
! char_u *text = get_tv_string_chk(&li->li_tv);
+ balloon_array[idx].pum_text = vim_strsave(
+ text == NULL ? (char_u *)"" : text);
+ }
+ }
+ else
+ balloon_arraysize = split_message(mesg, &balloon_array);
+
+ if (balloon_arraysize > 0)
+ {
pum_array = balloon_array;
pum_size = balloon_arraysize;
pum_compute_size();
pum_scrollbar = 0;
pum_height = balloon_arraysize;
! if (Rows - mouse_row > pum_size)
{
/* Enough space below the mouse row. */
pum_row = mouse_row + 1;
***************
*** 817,823 ****
else
{
/* Show above the mouse row, reduce height if it does not fit. */
! pum_row = mouse_row - 1 - pum_size;
if (pum_row < 0)
{
pum_height += pum_row;
--- 969,975 ----
else
{
/* Show above the mouse row, reduce height if it does not fit. */
! pum_row = mouse_row - pum_size;
if (pum_row < 0)
{
pum_height += pum_row;
*** ../vim-8.0.1317/src/proto/popupmnu.pro 2017-11-18 18:51:08.129770641
+0100
--- src/proto/popupmnu.pro 2017-11-19 19:14:04.474443454 +0100
***************
*** 5,11 ****
void pum_clear(void);
int pum_visible(void);
int pum_get_height(void);
void ui_remove_balloon(void);
! void ui_post_balloon(char_u *mesg);
void ui_may_remove_balloon(void);
/* vim: set ft=c : */
--- 5,12 ----
void pum_clear(void);
int pum_visible(void);
int pum_get_height(void);
+ int split_message(char_u *mesg, pumitem_T **array);
void ui_remove_balloon(void);
! void ui_post_balloon(char_u *mesg, list_T *list);
void ui_may_remove_balloon(void);
/* vim: set ft=c : */
*** ../vim-8.0.1317/src/evalfunc.c 2017-11-18 22:13:04.737908886 +0100
--- src/evalfunc.c 2017-11-19 19:21:26.387973807 +0100
***************
*** 61,66 ****
--- 61,67 ----
#endif
#ifdef FEAT_BEVAL
static void f_balloon_show(typval_T *argvars, typval_T *rettv);
+ static void f_balloon_split(typval_T *argvars, typval_T *rettv);
#endif
static void f_browse(typval_T *argvars, typval_T *rettv);
static void f_browsedir(typval_T *argvars, typval_T *rettv);
***************
*** 494,499 ****
--- 495,501 ----
#endif
#ifdef FEAT_BEVAL
{"balloon_show", 1, 1, f_balloon_show},
+ {"balloon_split", 1, 1, f_balloon_split},
#endif
{"browse", 4, 4, f_browse},
{"browsedir", 2, 2, f_browsedir},
***************
*** 1410,1416 ****
f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
{
if (balloonEval != NULL)
! post_balloon(balloonEval, get_tv_string_chk(&argvars[0]));
}
#endif
--- 1412,1448 ----
f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
{
if (balloonEval != NULL)
! {
! if (argvars[0].v_type == VAR_LIST
! # ifdef FEAT_GUI
! && !gui.in_use
! # endif
! )
! post_balloon(balloonEval, NULL, argvars[0].vval.v_list);
! else
! post_balloon(balloonEval, get_tv_string_chk(&argvars[0]), NULL);
! }
! }
!
! static void
! f_balloon_split(typval_T *argvars, typval_T *rettv UNUSED)
! {
! if (rettv_list_alloc(rettv) == OK)
! {
! char_u *msg = get_tv_string_chk(&argvars[0]);
!
! if (msg != NULL)
! {
! pumitem_T *array;
! int size = split_message(msg, &array);
! int i;
!
! /* Skip the first and last item, they are always empty. */
! for (i = 1; i < size - 1; ++i)
! list_append_string(rettv->vval.v_list, array[i].pum_text, -1);
! vim_free(array);
! }
! }
}
#endif
*** ../vim-8.0.1317/src/beval.c 2017-11-18 22:13:04.741908825 +0100
--- src/beval.c 2017-11-19 19:04:38.238731418 +0100
***************
*** 134,152 ****
}
/*
! * Show a balloon with "mesg".
*/
void
! post_balloon(BalloonEval *beval UNUSED, char_u *mesg)
{
# ifdef FEAT_BEVAL_TERM
# ifdef FEAT_GUI
if (!gui.in_use)
# endif
! ui_post_balloon(mesg);
# endif
# ifdef FEAT_BEVAL_GUI
if (gui.in_use)
gui_mch_post_balloon(beval, mesg);
# endif
}
--- 134,153 ----
}
/*
! * Show a balloon with "mesg" or "list".
*/
void
! post_balloon(BalloonEval *beval UNUSED, char_u *mesg, list_T *list)
{
# ifdef FEAT_BEVAL_TERM
# ifdef FEAT_GUI
if (!gui.in_use)
# endif
! ui_post_balloon(mesg, list);
# endif
# ifdef FEAT_BEVAL_GUI
if (gui.in_use)
+ /* GUI can't handle a list */
gui_mch_post_balloon(beval, mesg);
# endif
}
***************
*** 257,263 ****
set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
if (result != NULL && result[0] != NUL)
{
! post_balloon(beval, result);
recursive = FALSE;
return;
}
--- 258,264 ----
set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
if (result != NULL && result[0] != NUL)
{
! post_balloon(beval, result, NULL);
recursive = FALSE;
return;
}
*** ../vim-8.0.1317/src/proto/beval.pro 2017-11-18 22:13:04.741908825 +0100
--- src/proto/beval.pro 2017-11-19 19:04:53.690505299 +0100
***************
*** 1,6 ****
/* beval.c */
int get_beval_info(BalloonEval *beval, int getword, win_T **winp, linenr_T
*lnump, char_u **textp, int *colp);
! void post_balloon(BalloonEval *beval, char_u *mesg);
int can_use_beval(void);
void general_beval_cb(BalloonEval *beval, int state);
/* vim: set ft=c : */
--- 1,6 ----
/* beval.c */
int get_beval_info(BalloonEval *beval, int getword, win_T **winp, linenr_T
*lnump, char_u **textp, int *colp);
! void post_balloon(BalloonEval *beval, char_u *mesg, list_T *list);
int can_use_beval(void);
void general_beval_cb(BalloonEval *beval, int state);
/* vim: set ft=c : */
*** ../vim-8.0.1317/src/testdir/test_popup.vim 2017-11-04 19:24:24.750197152
+0100
--- src/testdir/test_popup.vim 2017-11-19 18:29:33.957291218 +0100
***************
*** 703,706 ****
--- 703,739 ----
bw!
endfunc
+ func Test_balloon_split()
+ call assert_equal([
+ \ 'one two three four one two three four one two thre',
+ \ 'e four',
+ \ ], balloon_split(
+ \ 'one two three four one two three four one two three four'))
+
+ call assert_equal([
+ \ 'struct = {',
+ \ ' one = 1,',
+ \ ' two = 2,',
+ \ ' three = 3}',
+ \ ], balloon_split(
+ \ 'struct = {one = 1, two = 2, three = 3}'))
+
+ call assert_equal([
+ \ 'struct = {',
+ \ ' one = 1,',
+ \ ' nested = {',
+ \ ' n1 = "yes",',
+ \ ' n2 = "no"}',
+ \ ' two = 2}',
+ \ ], balloon_split(
+ \ 'struct = {one = 1, nested = {n1 = "yes", n2 = "no"} two = 2}'))
+ call assert_equal([
+ \ 'struct = 0x234 {',
+ \ ' long = 2343 "\\"some long string that will be wr',
+ \ 'apped in two\\"",',
+ \ ' next = 123}',
+ \ ], balloon_split(
+ \ 'struct = 0x234 {long = 2343 "\\"some long string that will be
wrapped in two\\"", next = 123}'))
+ endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.0.1317/runtime/doc/eval.txt 2017-11-16 23:03:43.244816117
+0100
--- runtime/doc/eval.txt 2017-11-19 19:20:18.060974160 +0100
***************
*** 2032,2037 ****
--- 2032,2038 ----
atan({expr}) Float arc tangent of {expr}
atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2}
balloon_show({msg}) none show {msg} inside the balloon
+ balloon_split({msg}) List split {msg} as used for a balloon
browse({save}, {title}, {initdir}, {default})
String put up a file requester
browsedir({title}, {initdir}) String put up a directory requester
***************
*** 2682,2689 ****
< 2.356194
{only available when compiled with the |+float| feature}
! balloon_show({msg}) *balloon_show()*
! Show {msg} inside the balloon.
Example: >
func GetBalloonContent()
" initiate getting the content
--- 2683,2694 ----
< 2.356194
{only available when compiled with the |+float| feature}
! balloon_show({expr}) *balloon_show()*
! Show {expr} inside the balloon. For the GUI {expr} is used as
! a string. For a terminal {expr} can be a list, which contains
! the lines of the balloon. If {expr} is not a list it will be
! split with |balloon_split()|.
!
Example: >
func GetBalloonContent()
" initiate getting the content
***************
*** 2705,2710 ****
--- 2710,2721 ----
error message.
{only available when compiled with the +balloon_eval feature}
+ balloon_split({msg}) *balloon_split()*
+ Split {msg} into lines to be displayed in a balloon. The
+ splits are made for the current window size and optimize to
+ show debugger output.
+ Returns a |List| with the split lines.
+
*browse()*
browse({save}, {title}, {initdir}, {default})
Put up a file requester. This only works when "has("browse")"
*** ../vim-8.0.1317/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
2017-11-18 18:51:08.133770582 +0100
--- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim 2017-11-19
19:50:36.415251892 +0100
***************
*** 127,135 ****
call win_gotoid(s:gdbwin)
" Enable showing a balloon with eval info
! if has("balloon_eval")
! set ballooneval
set balloonexpr=TermDebugBalloonExpr()
if has("balloon_eval_term")
set balloonevalterm
endif
--- 127,137 ----
call win_gotoid(s:gdbwin)
" Enable showing a balloon with eval info
! if has("balloon_eval") || has("balloon_eval_term")
set balloonexpr=TermDebugBalloonExpr()
+ if has("balloon_eval")
+ set ballooneval
+ endif
if has("balloon_eval_term")
set balloonevalterm
endif
***************
*** 158,166 ****
let &columns = s:save_columns
endif
! if has("balloon_eval")
! set noballooneval
set balloonexpr=
if has("balloon_eval_term")
set noballoonevalterm
endif
--- 160,170 ----
let &columns = s:save_columns
endif
! if has("balloon_eval") || has("balloon_eval_term")
set balloonexpr=
+ if has("balloon_eval")
+ set noballooneval
+ endif
if has("balloon_eval_term")
set noballoonevalterm
endif
***************
*** 366,371 ****
--- 370,376 ----
if a:msg =~ 'No symbol .* in current context'
\ || a:msg =~ 'Cannot access memory at address '
\ || a:msg =~ 'Attempt to use a type name as an expression'
+ \ || a:msg =~ 'A syntax error in expression,'
" Result of s:SendEval() failed, ignore.
return
endif
*** ../vim-8.0.1317/src/version.c 2017-11-19 15:05:40.146159574 +0100
--- src/version.c 2017-11-19 19:51:01.434861998 +0100
***************
*** 773,774 ****
--- 773,776 ----
{ /* Add new patch number below this line */
+ /**/
+ 1318,
/**/
--
"How is your new girlfriend?"
"90-60-90 man!"
"What, pale purple?"
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.