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.

Raspunde prin e-mail lui