summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-11-19 19:56:27 +0100
committerBram Moolenaar <Bram@vim.org>2017-11-19 19:56:27 +0100
commit246fe03d154c09070d5b7365b7f61716c4e0ddd4 (patch)
tree1d0ad5929d17f1af24ac22817bd9fe6c0d5d84f2
parente518226713784e628ae7ee077f1b66cb12b9ffd9 (diff)
downloadvim-git-246fe03d154c09070d5b7365b7f61716c4e0ddd4.tar.gz
patch 8.0.1318: terminal balloon only shows one linev8.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.
-rw-r--r--runtime/doc/eval.txt15
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim13
-rw-r--r--src/beval.c9
-rw-r--r--src/evalfunc.c34
-rw-r--r--src/popupmnu.c178
-rw-r--r--src/proto/beval.pro2
-rw-r--r--src/proto/popupmnu.pro3
-rw-r--r--src/testdir/test_popup.vim33
-rw-r--r--src/version.c2
9 files changed, 263 insertions, 26 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index c001f6f2b..2ce6c48ef 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2032,6 +2032,7 @@ asin({expr}) Float arc sine of {expr}
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,8 +2683,12 @@ atan2({expr1}, {expr2}) *atan2()*
< 2.356194
{only available when compiled with the |+float| feature}
-balloon_show({msg}) *balloon_show()*
- Show {msg} inside the balloon.
+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,6 +2710,12 @@ balloon_show({msg}) *balloon_show()*
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")"
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 1c3c9df9a..aca56d2a7 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -127,9 +127,11 @@ func s:StartDebug(cmd)
call win_gotoid(s:gdbwin)
" Enable showing a balloon with eval info
- if has("balloon_eval")
- set ballooneval
+ 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,9 +160,11 @@ func s:EndDebug(job, status)
let &columns = s:save_columns
endif
- if has("balloon_eval")
- set noballooneval
+ 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,6 +370,7 @@ func s:HandleError(msg)
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
diff --git a/src/beval.c b/src/beval.c
index d4705b8b9..f8bb6ba26 100644
--- a/src/beval.c
+++ b/src/beval.c
@@ -134,19 +134,20 @@ get_beval_info(
}
/*
- * Show a balloon with "mesg".
+ * Show a balloon with "mesg" or "list".
*/
void
-post_balloon(BalloonEval *beval UNUSED, char_u *mesg)
+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);
+ 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,7 +258,7 @@ general_beval_cb(BalloonEval *beval, int state UNUSED)
set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
if (result != NULL && result[0] != NUL)
{
- post_balloon(beval, result);
+ post_balloon(beval, result, NULL);
recursive = FALSE;
return;
}
diff --git a/src/evalfunc.c b/src/evalfunc.c
index c03e21416..77a4fc2c0 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -61,6 +61,7 @@ static void f_atan2(typval_T *argvars, typval_T *rettv);
#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,6 +495,7 @@ static struct fst
#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,7 +1412,37 @@ f_atan2(typval_T *argvars, typval_T *rettv)
f_balloon_show(typval_T *argvars, typval_T *rettv UNUSED)
{
if (balloonEval != NULL)
- post_balloon(balloonEval, get_tv_string_chk(&argvars[0]));
+ {
+ 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
diff --git a/src/popupmnu.c b/src/popupmnu.c
index 82e3ef705..77460a1a6 100644
--- a/src/popupmnu.c
+++ b/src/popupmnu.c
@@ -766,9 +766,147 @@ static int balloon_arraysize;
static int balloon_mouse_row = 0;
static int balloon_mouse_col = 0;
-#define BALLOON_MIN_WIDTH 40
+#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,28 +924,42 @@ ui_remove_balloon(void)
* Terminal version of a balloon, uses the popup menu code.
*/
void
-ui_post_balloon(char_u *mesg)
+ui_post_balloon(char_u *mesg, list_T *list)
{
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)
+ if (mesg == NULL && list == NULL)
+ return;
+ if (list != 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 *)"");
+ 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 > BALLOON_MIN_HEIGHT)
+ if (Rows - mouse_row > pum_size)
{
/* Enough space below the mouse row. */
pum_row = mouse_row + 1;
@@ -817,7 +969,7 @@ ui_post_balloon(char_u *mesg)
else
{
/* Show above the mouse row, reduce height if it does not fit. */
- pum_row = mouse_row - 1 - pum_size;
+ pum_row = mouse_row - pum_size;
if (pum_row < 0)
{
pum_height += pum_row;
diff --git a/src/proto/beval.pro b/src/proto/beval.pro
index 716fbbe30..2be64a0da 100644
--- a/src/proto/beval.pro
+++ b/src/proto/beval.pro
@@ -1,6 +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);
+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 : */
diff --git a/src/proto/popupmnu.pro b/src/proto/popupmnu.pro
index 57795338b..272730433 100644
--- a/src/proto/popupmnu.pro
+++ b/src/proto/popupmnu.pro
@@ -5,7 +5,8 @@ void pum_undisplay(void);
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);
+void ui_post_balloon(char_u *mesg, list_T *list);
void ui_may_remove_balloon(void);
/* vim: set ft=c : */
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index 54d641fde..bfccb7410 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -703,4 +703,37 @@ func Test_popup_and_preview_autocommand()
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
diff --git a/src/version.c b/src/version.c
index 7e03c4b50..9896a7715 100644
--- a/src/version.c
+++ b/src/version.c
@@ -772,6 +772,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1318,
+/**/
1317,
/**/
1316,