diff options
author | Bram Moolenaar <Bram@vim.org> | 2017-11-19 19:56:27 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2017-11-19 19:56:27 +0100 |
commit | 246fe03d154c09070d5b7365b7f61716c4e0ddd4 (patch) | |
tree | 1d0ad5929d17f1af24ac22817bd9fe6c0d5d84f2 /src/popupmnu.c | |
parent | e518226713784e628ae7ee077f1b66cb12b9ffd9 (diff) | |
download | vim-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.
Diffstat (limited to 'src/popupmnu.c')
-rw-r--r-- | src/popupmnu.c | 178 |
1 files changed, 165 insertions, 13 deletions
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; |