From d6e91385f0f7256aec8f70373c9e3399770d22e5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 12 Nov 2022 17:44:13 +0000 Subject: patch 9.0.0867: wildmenu redrawing code is spread out Problem: Wildmenu redrawing code is spread out. Solution: Refactor to move code together. (closes #11528) --- src/cmdexpand.c | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 273 insertions(+), 6 deletions(-) (limited to 'src/cmdexpand.c') diff --git a/src/cmdexpand.c b/src/cmdexpand.c index e0ad1a77f2..2dbde4ef77 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -19,6 +19,7 @@ static int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped); static int ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int); +static char_u *showmatches_gettail(char_u *s); static int expand_showtail(expand_T *xp); static int expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg); #if defined(FEAT_EVAL) @@ -34,7 +35,7 @@ static int compl_match_arraysize; static int compl_startcol; static int compl_selected; -#define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(matches[m]) : matches[m]) +#define SHOW_FILE_TEXT(m) (showtail ? showmatches_gettail(matches[m]) : matches[m]) /* * Returns TRUE if fuzzy completion is supported for a given cmdline completion @@ -334,7 +335,7 @@ cmdline_pum_create( columns = vim_strsize(xp->xp_pattern); if (showtail) { - columns += vim_strsize(sm_gettail(matches[0])); + columns += vim_strsize(showmatches_gettail(matches[0])); columns -= vim_strsize(matches[0]); } if (columns >= compl_startcol) @@ -402,6 +403,272 @@ int cmdline_compl_startcol(void) return compl_startcol; } +/* + * Return the number of characters that should be skipped in a status match. + * These are backslashes used for escaping. Do show backslashes in help tags. + */ + static int +skip_status_match_char(expand_T *xp, char_u *s) +{ + if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) +#ifdef FEAT_MENU + || ((xp->xp_context == EXPAND_MENUS + || xp->xp_context == EXPAND_MENUNAMES) + && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL))) +#endif + ) + { +#ifndef BACKSLASH_IN_FILENAME + if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') + return 2; +#endif + return 1; + } + return 0; +} + +/* + * Get the length of an item as it will be shown in the status line. + */ + static int +status_match_len(expand_T *xp, char_u *s) +{ + int len = 0; + +#ifdef FEAT_MENU + int emenu = xp->xp_context == EXPAND_MENUS + || xp->xp_context == EXPAND_MENUNAMES; + + // Check for menu separators - replace with '|'. + if (emenu && menu_is_separator(s)) + return 1; +#endif + + while (*s != NUL) + { + s += skip_status_match_char(xp, s); + len += ptr2cells(s); + MB_PTR_ADV(s); + } + + return len; +} + +/* + * Show wildchar matches in the status line. + * Show at least the "match" item. + * We start at item 'first_match' in the list and show all matches that fit. + * + * If inversion is possible we use it. Else '=' characters are used. + */ + static void +win_redr_status_matches( + expand_T *xp, + int num_matches, + char_u **matches, // list of matches + int match, + int showtail) +{ +#define L_MATCH(m) (showtail ? showmatches_gettail(matches[m]) : matches[m]) + int row; + char_u *buf; + int len; + int clen; // length in screen cells + int fillchar; + int attr; + int i; + int highlight = TRUE; + char_u *selstart = NULL; + int selstart_col = 0; + char_u *selend = NULL; + static int first_match = 0; + int add_left = FALSE; + char_u *s; +#ifdef FEAT_MENU + int emenu; +#endif + int l; + + if (matches == NULL) // interrupted completion? + return; + + if (has_mbyte) + buf = alloc(Columns * MB_MAXBYTES + 1); + else + buf = alloc(Columns + 1); + if (buf == NULL) + return; + + if (match == -1) // don't show match but original text + { + match = 0; + highlight = FALSE; + } + // count 1 for the ending ">" + clen = status_match_len(xp, L_MATCH(match)) + 3; + if (match == 0) + first_match = 0; + else if (match < first_match) + { + // jumping left, as far as we can go + first_match = match; + add_left = TRUE; + } + else + { + // check if match fits on the screen + for (i = first_match; i < match; ++i) + clen += status_match_len(xp, L_MATCH(i)) + 2; + if (first_match > 0) + clen += 2; + // jumping right, put match at the left + if ((long)clen > Columns) + { + first_match = match; + // if showing the last match, we can add some on the left + clen = 2; + for (i = match; i < num_matches; ++i) + { + clen += status_match_len(xp, L_MATCH(i)) + 2; + if ((long)clen >= Columns) + break; + } + if (i == num_matches) + add_left = TRUE; + } + } + if (add_left) + while (first_match > 0) + { + clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; + if ((long)clen >= Columns) + break; + --first_match; + } + + fillchar = fillchar_status(&attr, curwin); + + if (first_match == 0) + { + *buf = NUL; + len = 0; + } + else + { + STRCPY(buf, "< "); + len = 2; + } + clen = len; + + i = first_match; + while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) + { + if (i == match) + { + selstart = buf + len; + selstart_col = clen; + } + + s = L_MATCH(i); + // Check for menu separators - replace with '|' +#ifdef FEAT_MENU + emenu = (xp->xp_context == EXPAND_MENUS + || xp->xp_context == EXPAND_MENUNAMES); + if (emenu && menu_is_separator(s)) + { + STRCPY(buf + len, transchar('|')); + l = (int)STRLEN(buf + len); + len += l; + clen += l; + } + else +#endif + for ( ; *s != NUL; ++s) + { + s += skip_status_match_char(xp, s); + clen += ptr2cells(s); + if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) + { + STRNCPY(buf + len, s, l); + s += l - 1; + len += l; + } + else + { + STRCPY(buf + len, transchar_byte(*s)); + len += (int)STRLEN(buf + len); + } + } + if (i == match) + selend = buf + len; + + *(buf + len++) = ' '; + *(buf + len++) = ' '; + clen += 2; + if (++i == num_matches) + break; + } + + if (i != num_matches) + { + *(buf + len++) = '>'; + ++clen; + } + + buf[len] = NUL; + + row = cmdline_row - 1; + if (row >= 0) + { + if (wild_menu_showing == 0) + { + if (msg_scrolled > 0) + { + // Put the wildmenu just above the command line. If there is + // no room, scroll the screen one line up. + if (cmdline_row == Rows - 1) + { + screen_del_lines(0, 0, 1, (int)Rows, TRUE, 0, NULL); + ++msg_scrolled; + } + else + { + ++cmdline_row; + ++row; + } + wild_menu_showing = WM_SCROLLED; + } + else + { + // Create status line if needed by setting 'laststatus' to 2. + // Set 'winminheight' to zero to avoid that the window is + // resized. + if (lastwin->w_status_height == 0) + { + save_p_ls = p_ls; + save_p_wmh = p_wmh; + p_ls = 2; + p_wmh = 0; + last_status(FALSE); + } + wild_menu_showing = WM_SHOWN; + } + } + + screen_puts(buf, row, 0, attr); + if (selstart != NULL && highlight) + { + *selend = NUL; + screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM)); + } + + screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr); + } + + win_redraw_last_status(topframe); + vim_free(buf); +} + /* * Get the next or prev cmdline completion match. The index of the match is set * in "p_findex" @@ -979,11 +1246,11 @@ showmatches(expand_T *xp, int wildmenu UNUSED) } /* - * Private gettail for showmatches() (and win_redr_status_matches()): - * Find tail of file name path, but ignore trailing "/". + * gettail() version for showmatches() and win_redr_status_matches(): + * Return the tail of file name path "s", ignoring a trailing "/". */ - char_u * -sm_gettail(char_u *s) + static char_u * +showmatches_gettail(char_u *s) { char_u *p; char_u *t = s; -- cgit v1.2.3