static void pum_compute_size(void) { int i; int w; /* Compute the width of the widest match and the widest extra. */ pum_base_width = 0; pum_kind_width = 0; pum_extra_width = 0; for (i = 0; i < pum_size; ++i) { w = vim_strsize(pum_array[i].pum_text); if (pum_base_width < w) pum_base_width = w; if (pum_array[i].pum_kind != NULL) { w = vim_strsize(pum_array[i].pum_kind) + 1; if (pum_kind_width < w) pum_kind_width = w; } if (pum_array[i].pum_extra != NULL) { w = vim_strsize(pum_array[i].pum_extra) + 1; if (pum_extra_width < w) pum_extra_width = w; } } }
static void do_intro_line(long row, char_u *mesg, int attr) { long col; char_u *p; int l; int clen; // Center the message horizontally. col = vim_strsize(mesg); col = (Columns - col) / 2; if (col < 0) { col = 0; } // Split up in parts to highlight <> items differently. for (p = mesg; *p != NUL; p += l) { clen = 0; for (l = 0; p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>')); ++l) { if (has_mbyte) { clen += ptr2cells(p + l); l += (*mb_ptr2len)(p + l) - 1; } else { clen += byte2cells(p[l]); } } assert(row <= INT_MAX && col <= INT_MAX); screen_puts_len(p, l, (int)row, (int)col, *p == '<' ? hl_attr(HLF_8) : attr); col += clen; } }
/* * Translate a string into allocated memory, replacing special chars with * printable chars. Returns NULL when out of memory. */ char_u *transstr(char_u *s) { char_u *res; char_u *p; int l, len, c; char_u hexbuf[11]; if (has_mbyte) { /* Compute the length of the result, taking account of unprintable * multi-byte characters. */ len = 0; p = s; while (*p != NUL) { if ((l = (*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); p += l; if (vim_isprintc(c)) len += l; else { transchar_hex(hexbuf, c); len += (int)STRLEN(hexbuf); } } else { l = byte2cells(*p++); if (l > 0) len += l; else len += 4; /* illegal byte sequence */ } } res = alloc((unsigned)(len + 1)); } else res = alloc((unsigned)(vim_strsize(s) + 1)); if (res != NULL) { *res = NUL; p = s; while (*p != NUL) { if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); if (vim_isprintc(c)) STRNCAT(res, p, l); /* append printable multi-byte char */ else transchar_hex(res + STRLEN(res), c); p += l; } else STRCAT(res, transchar_byte(*p++)); } } return res; }
/* * Output a string for the version message. If it's going to wrap, output a * newline, unless the message is too long to fit on the screen anyway. * When "wrap" is TRUE wrap the string in []. */ static void version_msg_wrap(char_u *s, int wrap) { int len = (int)vim_strsize(s) + (wrap ? 2 : 0); if (!got_int && len < (int)Columns && msg_col + len >= (int)Columns && *s != '\n') msg_putchar('\n'); if (!got_int) { if (wrap) MSG_PUTS("["); MSG_PUTS(s); if (wrap) MSG_PUTS("]"); } }
/* * Return appropriate space number for breakindent, taking influencing * parameters into account. Window must be specified, since it is not * necessarily always the current one. */ int get_breakindent_win(win_T *wp, char_u *line) { static int prev_indent = 0; /* cached indent value */ static int prev_ts = 0L; /* cached tabstop value */ static char_u *prev_line = NULL; /* cached pointer to line */ int bri = 0; /* window width minus window margin space, i.e. what rests for text */ const int eff_wwidth = wp->w_width - ((wp->w_p_nu || wp->w_p_rnu) && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); /* used cached indent, unless pointer or 'tabstop' changed */ if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts) { prev_line = line; prev_ts = wp->w_buffer->b_p_ts; prev_indent = get_indent_str(line, (int)wp->w_buffer->b_p_ts, wp->w_p_list); } bri = prev_indent + wp->w_p_brishift; /* indent minus the length of the showbreak string */ if (wp->w_p_brisbr) bri -= vim_strsize(p_sbr); /* Add offset for number column, if 'n' is in 'cpoptions' */ bri += win_col_off2(wp); /* never indent past left window margin */ if (bri < 0) bri = 0; /* always leave at least bri_min characters on the left, * if text width is sufficient */ else if (bri > eff_wwidth - wp->w_p_brimin) bri = (eff_wwidth - wp->w_p_brimin < 0) ? 0 : eff_wwidth - wp->w_p_brimin; return bri; }
/* * Redraw the popup menu, using "pum_first" and "pum_selected". */ void pum_redraw(void) { int row = pum_row; int col; int attr_norm = highlight_attr[HLF_PNI]; int attr_select = highlight_attr[HLF_PSI]; int attr_scroll = highlight_attr[HLF_PSB]; int attr_thumb = highlight_attr[HLF_PST]; int attr; int i; int idx; char_u *s; char_u *p = NULL; int totwidth, width, w; int thumb_pos = 0; int thumb_heigth = 1; int round; int n; /* Never display more than we have */ if (pum_first > pum_size - pum_height) pum_first = pum_size - pum_height; if (pum_scrollbar) { thumb_heigth = pum_height * pum_height / pum_size; if (thumb_heigth == 0) thumb_heigth = 1; thumb_pos = (pum_first * (pum_height - thumb_heigth) + (pum_size - pum_height) / 2) / (pum_size - pum_height); } for (i = 0; i < pum_height; ++i) { idx = i + pum_first; attr = (idx == pum_selected) ? attr_select : attr_norm; /* prepend a space if there is room */ #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) { if (pum_col < curwin->w_wincol + curwin->w_width - 1) screen_putchar(' ', row, pum_col + 1, attr); } else #endif if (pum_col > 0) screen_putchar(' ', row, pum_col - 1, attr); /* Display each entry, use two spaces for a Tab. * Do this 3 times: For the main text, kind and extra info */ col = pum_col; totwidth = 0; for (round = 1; round <= 3; ++round) { width = 0; s = NULL; switch (round) { case 1: p = pum_array[idx].pum_text; break; case 2: p = pum_array[idx].pum_kind; break; case 3: p = pum_array[idx].pum_extra; break; } if (p != NULL) for ( ; ; MB_PTR_ADV(p)) { if (s == NULL) s = p; w = ptr2cells(p); if (*p == NUL || *p == TAB || totwidth + w > pum_width) { /* Display the text that fits or comes before a Tab. * First convert it to printable characters. */ char_u *st; int saved = *p; if (saved != NUL) *p = NUL; st = transstr(s); if (saved != NUL) *p = saved; #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) { if (st != NULL) { char_u *rt = reverse_text(st); if (rt != NULL) { char_u *rt_start = rt; int size; size = vim_strsize(rt); if (size > pum_width) { do { size -= has_mbyte ? (*mb_ptr2cells)(rt) : 1; MB_PTR_ADV(rt); } while (size > pum_width); if (size < pum_width) { /* Most left character requires * 2-cells but only 1 cell is * available on screen. Put a * '<' on the left of the pum * item */ *(--rt) = '<'; size++; } } screen_puts_len(rt, (int)STRLEN(rt), row, col - size + 1, attr); vim_free(rt_start); } vim_free(st); } col -= width; } else #endif { if (st != NULL) { screen_puts_len(st, (int)STRLEN(st), row, col, attr); vim_free(st); } col += width; } if (*p != TAB) break; /* Display two spaces for a Tab. */ #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) { screen_puts_len((char_u *)" ", 2, row, col - 1, attr); col -= 2; } else #endif { screen_puts_len((char_u *)" ", 2, row, col, attr); col += 2; } totwidth += 2; s = NULL; /* start text at next char */ width = 0; } else width += w; } if (round > 1) n = pum_kind_width + 1; else n = 1; /* Stop when there is nothing more to display. */ if (round == 3 || (round == 2 && pum_array[idx].pum_extra == NULL) || (round == 1 && pum_array[idx].pum_kind == NULL && pum_array[idx].pum_extra == NULL) || pum_base_width + n >= pum_width) break; #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) { screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, col + 1, ' ', ' ', attr); col = pum_col - pum_base_width - n + 1; } else #endif { screen_fill(row, row + 1, col, pum_col + pum_base_width + n, ' ', ' ', attr); col = pum_col + pum_base_width + n; } totwidth = pum_base_width + n; } #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', ' ', attr); else #endif screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', attr); if (pum_scrollbar > 0) { #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) screen_putchar(' ', row, pum_col - pum_width, i >= thumb_pos && i < thumb_pos + thumb_heigth ? attr_thumb : attr_scroll); else #endif screen_putchar(' ', row, pum_col + pum_width, i >= thumb_pos && i < thumb_pos + thumb_heigth ? attr_thumb : attr_scroll); } ++row; } }
static void do_intro_line(int row, char_u *mesg, int add_version, int attr) { char_u vers[20]; int col; char_u *p; int l; int clen; #ifdef MODIFIED_BY # define MODBY_LEN 150 char_u modby[MODBY_LEN]; if (*mesg == ' ') { vim_strncpy(modby, (char_u *)_("Modified by "), MODBY_LEN - 1); l = STRLEN(modby); vim_strncpy(modby + l, (char_u *)MODIFIED_BY, MODBY_LEN - l - 1); mesg = modby; } #endif // ifdef MODIFIED_BY // Center the message horizontally. col = vim_strsize(mesg); if (add_version) { STRCPY(vers, mediumVersion); if (highest_patch()) { // Check for 9.9x or 9.9xx, alpha/beta version if (isalpha((int)vers[3])) { int len = (isalpha((int)vers[4])) ? 5 : 4; sprintf((char *)vers + len, ".%d%s", highest_patch(), mediumVersion + len); } else { sprintf((char *)vers + 3, ".%d", highest_patch()); } } col += (int)STRLEN(vers); } col = (Columns - col) / 2; if (col < 0) { col = 0; } // Split up in parts to highlight <> items differently. for (p = mesg; *p != NUL; p += l) { clen = 0; for (l = 0; p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>')); ++l) { if (has_mbyte) { clen += ptr2cells(p + l); l += (*mb_ptr2len)(p + l) - 1; } else { clen += byte2cells(p[l]); } } screen_puts_len(p, l, row, col, *p == '<' ? hl_attr(HLF_8) : attr); col += clen; } // Add the version number to the version line. if (add_version) { screen_puts(vers, row, col, 0); } }
/// Show the popup menu with items "array[size]". /// "array" must remain valid until pum_undisplay() is called! /// When possible the leftmost character is aligned with screen column "col". /// The menu appears above the screen line "row" or at "row" + "height" - 1. /// /// @param array /// @param size /// @param selected index of initially selected item, none if out of range void pum_display(pumitem_T *array, int size, int selected) { int w; int def_width; int max_width; int kind_width; int extra_width; int i; int top_clear; int row; int context_lines; int col; int above_row = cmdline_row; int redo_count = 0; redo: def_width = PUM_DEF_WIDTH; max_width = 0; kind_width = 0; extra_width = 0; // Pretend the pum is already there to avoid that must_redraw is set when // 'cuc' is on. pum_array = (pumitem_T *)1; validate_cursor_col(); pum_array = NULL; row = curwin->w_wrow + curwin->w_winrow; if (firstwin->w_p_pvw) { top_clear = firstwin->w_height; } else { top_clear = 0; } // When the preview window is at the bottom stop just above it. Also // avoid drawing over the status line so that it's clear there is a window // boundary. if (lastwin->w_p_pvw) { above_row -= lastwin->w_height + lastwin->w_status_height + 1; } // Figure out the size and position of the pum. if (size < PUM_DEF_HEIGHT) { pum_height = size; } else { pum_height = PUM_DEF_HEIGHT; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_height = p_ph; } // Put the pum below "row" if possible. If there are few lines decide on // where there is more room. if ((row + 2 >= above_row - pum_height) && (row > (above_row - top_clear) / 2)) { // pum above "row" // Leave two lines of context if possible if (curwin->w_wrow - curwin->w_cline_row >= 2) { context_lines = 2; } else { context_lines = curwin->w_wrow - curwin->w_cline_row; } if (row >= size + context_lines) { pum_row = row - size - context_lines; pum_height = size; } else { pum_row = 0; pum_height = row - context_lines; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_row += pum_height - p_ph; pum_height = p_ph; } } else { // pum below "row" // Leave two lines of context if possible if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { context_lines = 3; } else { context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow; } pum_row = row + context_lines; if (size > above_row - pum_row) { pum_height = above_row - pum_row; } else { pum_height = size; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_height = p_ph; } } // don't display when we only have room for one line if ((pum_height < 1) || ((pum_height == 1) && (size > 1))) { return; } // If there is a preview window at the top avoid drawing over it. if (firstwin->w_p_pvw && (pum_row < firstwin->w_height) && (pum_height > firstwin->w_height + 4)) { pum_row += firstwin->w_height; pum_height -= firstwin->w_height; } // Compute the width of the widest match and the widest extra. for (i = 0; i < size; ++i) { w = vim_strsize(array[i].pum_text); if (max_width < w) { max_width = w; } if (array[i].pum_kind != NULL) { w = vim_strsize(array[i].pum_kind) + 1; if (kind_width < w) { kind_width = w; } } if (array[i].pum_extra != NULL) { w = vim_strsize(array[i].pum_extra) + 1; if (extra_width < w) { extra_width = w; } } } pum_base_width = max_width; pum_kind_width = kind_width; // Calculate column if (curwin->w_p_rl) { col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1; } else { col = curwin->w_wincol + curwin->w_wcol; } // if there are more items than room we need a scrollbar if (pum_height < size) { pum_scrollbar = 1; max_width++; } else { pum_scrollbar = 0; } if (def_width < max_width) { def_width = max_width; } if ((((col < Columns - PUM_DEF_WIDTH) || (col < Columns - max_width)) && !curwin->w_p_rl) || (curwin->w_p_rl && ((col > PUM_DEF_WIDTH) || (col > max_width)))) { // align pum column with "col" pum_col = col; if (curwin->w_p_rl) { pum_width = pum_col - pum_scrollbar + 1; } else { pum_width = Columns - pum_col - pum_scrollbar; } if ((pum_width > max_width + kind_width + extra_width + 1) && (pum_width > PUM_DEF_WIDTH)) { pum_width = max_width + kind_width + extra_width + 1; if (pum_width < PUM_DEF_WIDTH) { pum_width = PUM_DEF_WIDTH; } } } else if (Columns < def_width) { // not enough room, will use what we have if (curwin->w_p_rl) { pum_col = Columns - 1; } else { pum_col = 0; } pum_width = Columns - 1; } else { if (max_width > PUM_DEF_WIDTH) { // truncate max_width = PUM_DEF_WIDTH; } if (curwin->w_p_rl) { pum_col = max_width - 1; } else { pum_col = Columns - max_width; } pum_width = max_width - pum_scrollbar; } pum_array = array; pum_size = size; // Set selected item and redraw. If the window size changed need to redo // the positioning. Limit this to two times, when there is not much // room the window size will keep changing. if (pum_set_selected(selected, redo_count) && (++redo_count <= 2)) { goto redo; } }
/// Redraw the popup menu, using "pum_first" and "pum_selected". void pum_redraw(void) { int row = pum_row; int col; int attr_norm = highlight_attr[HLF_PNI]; int attr_select = highlight_attr[HLF_PSI]; int attr_scroll = highlight_attr[HLF_PSB]; int attr_thumb = highlight_attr[HLF_PST]; int attr; int i; int idx; char_u *s; char_u *p = NULL; int totwidth, width, w; int thumb_pos = 0; int thumb_heigth = 1; int round; int n; // Never display more than we have if (pum_first > pum_size - pum_height) { pum_first = pum_size - pum_height; } if (pum_scrollbar) { thumb_heigth = pum_height * pum_height / pum_size; if (thumb_heigth == 0) { thumb_heigth = 1; } thumb_pos = (pum_first * (pum_height - thumb_heigth) + (pum_size - pum_height) / 2) / (pum_size - pum_height); } for (i = 0; i < pum_height; ++i) { idx = i + pum_first; attr = (idx == pum_selected) ? attr_select : attr_norm; // prepend a space if there is room if (curwin->w_p_rl) { if (pum_col < curwin->w_wincol + curwin->w_width - 1) { screen_putchar(' ', row, pum_col + 1, attr); } } else if (pum_col > 0) { screen_putchar(' ', row, pum_col - 1, attr); } // Display each entry, use two spaces for a Tab. // Do this 3 times: For the main text, kind and extra info col = pum_col; totwidth = 0; for (round = 1; round <= 3; ++round) { width = 0; s = NULL; switch (round) { case 1: p = pum_array[idx].pum_text; break; case 2: p = pum_array[idx].pum_kind; break; case 3: p = pum_array[idx].pum_extra; break; } if (p != NULL) { for (;; mb_ptr_adv(p)) { if (s == NULL) { s = p; } w = ptr2cells(p); if ((*p == NUL) || (*p == TAB) || (totwidth + w > pum_width)) { // Display the text that fits or comes before a Tab. // First convert it to printable characters. char_u *st; int saved = *p; *p = NUL; st = transstr(s); *p = saved; if (curwin->w_p_rl) { char_u *rt = reverse_text(st); char_u *rt_start = rt; int size = vim_strsize(rt); if (size > pum_width) { do { size -= has_mbyte ? (*mb_ptr2cells)(rt) : 1; mb_ptr_adv(rt); } while (size > pum_width); if (size < pum_width) { // Most left character requires 2-cells but only 1 cell // is available on screen. Put a '<' on the left of the // pum item *(--rt) = '<'; size++; } } screen_puts_len(rt, (int)STRLEN(rt), row, col - size + 1, attr); free(rt_start); free(st); col -= width; } else { if (st != NULL) { screen_puts_len(st, (int)STRLEN(st), row, col, attr); free(st); } col += width; } if (*p != TAB) { break; } // Display two spaces for a Tab. if (curwin->w_p_rl) { screen_puts_len((char_u *)" ", 2, row, col - 1, attr); col -= 2; } else { screen_puts_len((char_u *)" ", 2, row, col, attr); col += 2; } totwidth += 2; // start text at next char s = NULL; width = 0; } else { width += w; } } } if (round > 1) { n = pum_kind_width + 1; } else { n = 1; } // Stop when there is nothing more to display. if ((round == 3) || ((round == 2) && (pum_array[idx].pum_extra == NULL)) || ((round == 1) && (pum_array[idx].pum_kind == NULL) && (pum_array[idx].pum_extra == NULL)) || (pum_base_width + n >= pum_width)) { break; } if (curwin->w_p_rl) { screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, col + 1, ' ', ' ', attr); col = pum_col - pum_base_width - n + 1; } else { screen_fill(row, row + 1, col, pum_col + pum_base_width + n, ' ', ' ', attr); col = pum_col + pum_base_width + n; } totwidth = pum_base_width + n; } if (curwin->w_p_rl) { screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', ' ', attr); } else { screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', attr); } if (pum_scrollbar > 0) { if (curwin->w_p_rl) { screen_putchar(' ', row, pum_col - pum_width, i >= thumb_pos && i < thumb_pos + thumb_heigth ? attr_thumb : attr_scroll); } else { screen_putchar(' ', row, pum_col + pum_width, i >= thumb_pos && i < thumb_pos + thumb_heigth ? attr_thumb : attr_scroll); } } row++; } }
/// Show the popup menu with items "array[size]". /// "array" must remain valid until pum_undisplay() is called! /// When possible the leftmost character is aligned with screen column "col". /// The menu appears above the screen line "row" or at "row" + "height" - 1. /// /// @param array /// @param size /// @param selected index of initially selected item, none if out of range /// @param array_changed if true, array contains different items since last call /// if false, a new item is selected, but the array /// is the same void pum_display(pumitem_T *array, int size, int selected, bool array_changed) { int w; int def_width; int max_width; int kind_width; int extra_width; int i; int top_clear; int row; int context_lines; int col; int above_row = cmdline_row; int redo_count = 0; if (!pum_is_visible) { // To keep the code simple, we only allow changing the // draw mode when the popup menu is not being displayed pum_external = pum_wants_external; } redo: // Mark the pum as visible already here, // to avoid that must_redraw is set when 'cursorcolumn' is on. pum_is_visible = true; validate_cursor_col(); // anchor position: the start of the completed word row = curwin->w_wrow + curwin->w_winrow; if (curwin->w_p_rl) { col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1; } else { col = curwin->w_wincol + curwin->w_wcol; } if (pum_external) { Array args = ARRAY_DICT_INIT; if (array_changed) { Array arr = ARRAY_DICT_INIT; for (i = 0; i < size; i++) { Array item = ARRAY_DICT_INIT; ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text))); ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind))); ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra))); ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); ADD(arr, ARRAY_OBJ(item)); } ADD(args, ARRAY_OBJ(arr)); ADD(args, INTEGER_OBJ(selected)); ADD(args, INTEGER_OBJ(row)); ADD(args, INTEGER_OBJ(col)); ui_event("popupmenu_show", args); } else { ADD(args, INTEGER_OBJ(selected)); ui_event("popupmenu_select", args); } return; } def_width = PUM_DEF_WIDTH; max_width = 0; kind_width = 0; extra_width = 0; if (firstwin->w_p_pvw) { top_clear = firstwin->w_height; } else { top_clear = 0; } // When the preview window is at the bottom stop just above it. Also // avoid drawing over the status line so that it's clear there is a window // boundary. if (lastwin->w_p_pvw) { above_row -= lastwin->w_height + lastwin->w_status_height + 1; } // Figure out the size and position of the pum. if (size < PUM_DEF_HEIGHT) { pum_height = size; } else { pum_height = PUM_DEF_HEIGHT; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_height = (int)p_ph; } // Put the pum below "row" if possible. If there are few lines decide on // where there is more room. if ((row + 2 >= above_row - pum_height) && (row > (above_row - top_clear) / 2)) { // pum above "row" // Leave two lines of context if possible if (curwin->w_wrow - curwin->w_cline_row >= 2) { context_lines = 2; } else { context_lines = curwin->w_wrow - curwin->w_cline_row; } if (row >= size + context_lines) { pum_row = row - size - context_lines; pum_height = size; } else { pum_row = 0; pum_height = row - context_lines; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_row += pum_height - (int)p_ph; pum_height = (int)p_ph; } } else { // pum below "row" // Leave two lines of context if possible if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { context_lines = 3; } else { context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow; } pum_row = row + context_lines; if (size > above_row - pum_row) { pum_height = above_row - pum_row; } else { pum_height = size; } if ((p_ph > 0) && (pum_height > p_ph)) { pum_height = (int)p_ph; } } // don't display when we only have room for one line if ((pum_height < 1) || ((pum_height == 1) && (size > 1))) { return; } // If there is a preview window at the top avoid drawing over it. if (firstwin->w_p_pvw && (pum_row < firstwin->w_height) && (pum_height > firstwin->w_height + 4)) { pum_row += firstwin->w_height; pum_height -= firstwin->w_height; } // Compute the width of the widest match and the widest extra. for (i = 0; i < size; ++i) { w = vim_strsize(array[i].pum_text); if (max_width < w) { max_width = w; } if (array[i].pum_kind != NULL) { w = vim_strsize(array[i].pum_kind) + 1; if (kind_width < w) { kind_width = w; } } if (array[i].pum_extra != NULL) { w = vim_strsize(array[i].pum_extra) + 1; if (extra_width < w) { extra_width = w; } } } pum_base_width = max_width; pum_kind_width = kind_width; // if there are more items than room we need a scrollbar if (pum_height < size) { pum_scrollbar = 1; max_width++; } else { pum_scrollbar = 0; } if (def_width < max_width) { def_width = max_width; } if ((((col < Columns - PUM_DEF_WIDTH) || (col < Columns - max_width)) && !curwin->w_p_rl) || (curwin->w_p_rl && ((col > PUM_DEF_WIDTH) || (col > max_width)))) { // align pum column with "col" pum_col = col; if (curwin->w_p_rl) { pum_width = pum_col - pum_scrollbar + 1; } else { assert(Columns - pum_col - pum_scrollbar >= INT_MIN && Columns - pum_col - pum_scrollbar <= INT_MAX); pum_width = (int)(Columns - pum_col - pum_scrollbar); } if ((pum_width > max_width + kind_width + extra_width + 1) && (pum_width > PUM_DEF_WIDTH)) { pum_width = max_width + kind_width + extra_width + 1; if (pum_width < PUM_DEF_WIDTH) { pum_width = PUM_DEF_WIDTH; } } } else if (Columns < def_width) { // not enough room, will use what we have if (curwin->w_p_rl) { assert(Columns - 1 >= INT_MIN); pum_col = (int)(Columns - 1); } else { pum_col = 0; } assert(Columns - 1 >= INT_MIN); pum_width = (int)(Columns - 1); } else { if (max_width > PUM_DEF_WIDTH) { // truncate max_width = PUM_DEF_WIDTH; } if (curwin->w_p_rl) { pum_col = max_width - 1; } else { assert(Columns - max_width >= INT_MIN && Columns - max_width <= INT_MAX); pum_col = (int)(Columns - max_width); } pum_width = max_width - pum_scrollbar; } pum_array = array; pum_size = size; // Set selected item and redraw. If the window size changed need to redo // the positioning. Limit this to two times, when there is not much // room the window size will keep changing. if (pum_set_selected(selected, redo_count) && (++redo_count <= 2)) { goto redo; } }
/// Translate a string into allocated memory, replacing special chars with /// printable chars. Returns NULL when out of memory. /// /// @param s /// /// @return translated string char_u *transstr(char_u *s) { char_u *res; char_u *p; int l, c; char_u hexbuf[11]; if (has_mbyte) { // Compute the length of the result, taking account of unprintable // multi-byte characters. size_t len = 0; p = s; while (*p != NUL) { if ((l = (*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); p += l; if (vim_isprintc(c)) { len += l; } else { transchar_hex(hexbuf, c); len += STRLEN(hexbuf); } } else { l = byte2cells(*p++); if (l > 0) { len += l; } else { // illegal byte sequence len += 4; } } } res = xmallocz(len); } else { res = xmallocz(vim_strsize(s)); } *res = NUL; p = s; while (*p != NUL) { if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) { c = (*mb_ptr2char)(p); if (vim_isprintc(c)) { // append printable multi-byte char STRNCAT(res, p, l); } else { transchar_hex(res + STRLEN(res), c); } p += l; } else { STRCAT(res, transchar_byte(*p++)); } } return res; }
/* * Show the popup menu with items "array[size]". * "array" must remain valid until pum_undisplay() is called! * When possible the leftmost character is aligned with screen column "col". * The menu appears above the screen line "row" or at "row" + "height" - 1. */ void pum_display ( pumitem_T *array, int size, int selected /* index of initially selected item, none if out of range */ ) { int w; int def_width; int max_width; int kind_width; int extra_width; int i; int top_clear; int row; int context_lines; int col; int above_row = cmdline_row; int redo_count = 0; redo: def_width = PUM_DEF_WIDTH; max_width = 0; kind_width = 0; extra_width = 0; /* Pretend the pum is already there to avoid that must_redraw is set when * 'cuc' is on. */ pum_array = (pumitem_T *)1; validate_cursor_col(); pum_array = NULL; row = curwin->w_wrow + W_WINROW(curwin); if (firstwin->w_p_pvw) top_clear = firstwin->w_height; else top_clear = 0; /* When the preview window is at the bottom stop just above it. Also * avoid drawing over the status line so that it's clear there is a window * boundary. */ if (lastwin->w_p_pvw) above_row -= lastwin->w_height + lastwin->w_status_height + 1; /* * Figure out the size and position of the pum. */ if (size < PUM_DEF_HEIGHT) pum_height = size; else pum_height = PUM_DEF_HEIGHT; if (p_ph > 0 && pum_height > p_ph) pum_height = p_ph; /* Put the pum below "row" if possible. If there are few lines decide on * where there is more room. */ if (row + 2 >= above_row - pum_height && row > (above_row - top_clear) / 2) { /* pum above "row" */ /* Leave two lines of context if possible */ if (curwin->w_wrow - curwin->w_cline_row >= 2) context_lines = 2; else context_lines = curwin->w_wrow - curwin->w_cline_row; if (row >= size + context_lines) { pum_row = row - size - context_lines; pum_height = size; } else { pum_row = 0; pum_height = row - context_lines; } if (p_ph > 0 && pum_height > p_ph) { pum_row += pum_height - p_ph; pum_height = p_ph; } } else { /* pum below "row" */ /* Leave two lines of context if possible */ if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) context_lines = 3; else context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow; pum_row = row + context_lines; if (size > above_row - pum_row) pum_height = above_row - pum_row; else pum_height = size; if (p_ph > 0 && pum_height > p_ph) pum_height = p_ph; } /* don't display when we only have room for one line */ if (pum_height < 1 || (pum_height == 1 && size > 1)) return; /* If there is a preview window at the top avoid drawing over it. */ if (firstwin->w_p_pvw && pum_row < firstwin->w_height && pum_height > firstwin->w_height + 4) { pum_row += firstwin->w_height; pum_height -= firstwin->w_height; } /* Compute the width of the widest match and the widest extra. */ for (i = 0; i < size; ++i) { w = vim_strsize(array[i].pum_text); if (max_width < w) max_width = w; if (array[i].pum_kind != NULL) { w = vim_strsize(array[i].pum_kind) + 1; if (kind_width < w) kind_width = w; } if (array[i].pum_extra != NULL) { w = vim_strsize(array[i].pum_extra) + 1; if (extra_width < w) extra_width = w; } } pum_base_width = max_width; pum_kind_width = kind_width; /* Calculate column */ if (curwin->w_p_rl) col = W_WINCOL(curwin) + W_WIDTH(curwin) - curwin->w_wcol - 1; else col = W_WINCOL(curwin) + curwin->w_wcol; /* if there are more items than room we need a scrollbar */ if (pum_height < size) { pum_scrollbar = 1; ++max_width; } else pum_scrollbar = 0; if (def_width < max_width) def_width = max_width; if (((col < Columns - PUM_DEF_WIDTH || col < Columns - max_width) && !curwin->w_p_rl) || (curwin->w_p_rl && (col > PUM_DEF_WIDTH || col > max_width) )) { /* align pum column with "col" */ pum_col = col; if (curwin->w_p_rl) pum_width = pum_col - pum_scrollbar + 1; else pum_width = Columns - pum_col - pum_scrollbar; if (pum_width > max_width + kind_width + extra_width + 1 && pum_width > PUM_DEF_WIDTH) { pum_width = max_width + kind_width + extra_width + 1; if (pum_width < PUM_DEF_WIDTH) pum_width = PUM_DEF_WIDTH; } } else if (Columns < def_width) { /* not enough room, will use what we have */ if (curwin->w_p_rl) pum_col = Columns - 1; else pum_col = 0; pum_width = Columns - 1; } else { if (max_width > PUM_DEF_WIDTH) max_width = PUM_DEF_WIDTH; /* truncate */ if (curwin->w_p_rl) pum_col = max_width - 1; else pum_col = Columns - max_width; pum_width = max_width - pum_scrollbar; } pum_array = array; pum_size = size; /* Set selected item and redraw. If the window size changed need to redo * the positioning. Limit this to two times, when there is not much * room the window size will keep changing. */ if (pum_set_selected(selected, redo_count) && ++redo_count <= 2) goto redo; }
/* * List string items nicely aligned in columns. * When "size" is < 0 then the last entry is marked with NULL. * The entry with index "current" is inclosed in []. */ void list_in_columns(char_u **items, int size, int current) { int i; int ncol; int nrow; int item_count = 0; int width = 0; /* Find the length of the longest item, use that + 1 as the column * width. */ for (i = 0; size < 0 ? items[i] != NULL : i < size; ++i) { int l = (int)vim_strsize(items[i]) + (i == current ? 2 : 0); if (l > width) width = l; ++item_count; } width += 1; if (Columns < width) { /* Not enough screen columns - show one per line */ for (i = 0; i < item_count; ++i) { version_msg_wrap(items[i], i == current); if (msg_col > 0) msg_putchar('\n'); } return; } /* The rightmost column doesn't need a separator. * Sacrifice it to fit in one more column if possible. */ ncol = (int) (Columns + 1) / width; nrow = item_count / ncol + (item_count % ncol ? 1 : 0); /* i counts columns then rows. idx counts rows then columns. */ for (i = 0; !got_int && i < nrow * ncol; ++i) { int idx = (i / ncol) + (i % ncol) * nrow; if (idx < item_count) { int last_col = (i + 1) % ncol == 0; if (idx == current) msg_putchar('['); msg_puts(items[idx]); if (idx == current) msg_putchar(']'); if (last_col) { if (msg_col > 0) msg_putchar('\n'); } else { while (msg_col % width) msg_putchar(' '); } } else { if (msg_col > 0) msg_putchar('\n'); } } }