aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/cmdexpand.c243
-rw-r--r--src/nvim/screen.c235
2 files changed, 238 insertions, 240 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 480f3aa18c..ef742525da 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -20,6 +20,7 @@
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/locale.h"
@@ -34,6 +35,7 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sign.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
@@ -275,6 +277,236 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
wildmenu_cleanup(cclp);
}
+/// Return the number of characters that should be skipped in the wildmenu
+/// These are backslashes used for escaping. Do show backslashes in help tags.
+static int skip_wildmenu_char(expand_T *xp, char_u *s)
+{
+ if ((rem_backslash((char *)s) && xp->xp_context != EXPAND_HELP)
+ || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES)
+ && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL)))) {
+#ifndef BACKSLASH_IN_FILENAME
+ // TODO(bfredl): Why in the actual fuck are we special casing the
+ // shell variety deep in the redraw logic? Shell special snowflakiness
+ // should already be eliminated multiple layers before reaching the
+ // screen infracstructure.
+ 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 wildmenu_match_len(expand_T *xp, char_u *s)
+{
+ int len = 0;
+
+ int emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+
+ // Check for menu separators - replace with '|'.
+ if (emenu && menu_is_separator((char *)s)) {
+ return 1;
+ }
+
+ while (*s != NUL) {
+ s += skip_wildmenu_char(xp, s);
+ len += ptr2cells((char *)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.
+///
+/// @param matches list of matches
+static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+{
+#define L_MATCH(m) (showtail ? showmatches_gettail(matches[m], false) : matches[m])
+ int row;
+ char_u *buf;
+ int len;
+ int clen; // length in screen cells
+ int fillchar;
+ int attr;
+ int i;
+ bool highlight = true;
+ char_u *selstart = NULL;
+ int selstart_col = 0;
+ char_u *selend = NULL;
+ static int first_match = 0;
+ bool add_left = false;
+ char_u *s;
+ int emenu;
+ int l;
+
+ if (matches == NULL) { // interrupted completion?
+ return;
+ }
+
+ buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
+
+ if (match == -1) { // don't show match but original text
+ match = 0;
+ highlight = false;
+ }
+ // count 1 for the ending ">"
+ clen = wildmenu_match_len(xp, (char_u *)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 += wildmenu_match_len(xp, (char_u *)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 += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+ if ((long)clen >= Columns) {
+ break;
+ }
+ }
+ if (i == num_matches) {
+ add_left = true;
+ }
+ }
+ }
+ if (add_left) {
+ while (first_match > 0) {
+ clen += wildmenu_match_len(xp, (char_u *)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 (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
+ if (i == match) {
+ selstart = buf + len;
+ selstart_col = clen;
+ }
+
+ s = (char_u *)L_MATCH(i);
+ // Check for menu separators - replace with '|'
+ emenu = (xp->xp_context == EXPAND_MENUS
+ || xp->xp_context == EXPAND_MENUNAMES);
+ if (emenu && menu_is_separator((char *)s)) {
+ STRCPY(buf + len, transchar('|'));
+ l = (int)STRLEN(buf + len);
+ len += l;
+ clen += l;
+ } else {
+ for (; *s != NUL; s++) {
+ s += skip_wildmenu_char(xp, s);
+ clen += ptr2cells((char *)s);
+ if ((l = utfc_ptr2len((char *)s)) > 1) {
+ STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
+ 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 || wild_menu_showing == WM_LIST) {
+ 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) {
+ msg_scroll_up(false, false);
+ 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 && global_stl_height() == 0) {
+ save_p_ls = (int)p_ls;
+ save_p_wmh = (int)p_wmh;
+ p_ls = 2;
+ p_wmh = 0;
+ last_status(false);
+ }
+ wild_menu_showing = WM_SHOWN;
+ }
+ }
+
+ // Tricky: wildmenu can be drawn either over a status line, or at empty
+ // scrolled space in the message output
+ ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
+ ? &msg_grid_adj : &default_grid;
+
+ grid_puts(grid, (char *)buf, row, 0, attr);
+ if (selstart != NULL && highlight) {
+ *selend = NUL;
+ grid_puts(grid, (char *)selstart, row, selstart_col, HL_ATTR(HLF_WM));
+ }
+
+ grid_fill(grid, row, row + 1, clen, Columns,
+ fillchar, fillchar, attr);
+ }
+
+ win_redraw_last_status(topframe);
+ xfree(buf);
+}
+
/// Get the next or prev cmdline completion match. The index of the match is set
/// in "p_findex"
static char *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char *orig_save)
@@ -563,7 +795,8 @@ int showmatches(expand_T *xp, int wildmenu)
{
CmdlineInfo *const ccline = get_cmdline_info();
#define L_SHOWFILE(m) (showtail \
- ? sm_gettail(files_found[m], false) : files_found[m])
+ ? showmatches_gettail(files_found[m], false) \
+ : files_found[m])
int num_files;
char **files_found;
int i, j, k;
@@ -606,7 +839,7 @@ int showmatches(expand_T *xp, int wildmenu)
.pum_kind = NULL,
};
}
- char *endpos = (showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
+ char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, true) : xp->xp_pattern;
if (ui_has(kUICmdline)) {
compl_startcol = (int)(endpos - ccline->cmdbuff);
} else {
@@ -739,9 +972,9 @@ int showmatches(expand_T *xp, int wildmenu)
return EXPAND_OK;
}
-/// Private path_tail for showmatches() (and redraw_wildmenu()):
-/// Find tail of file name path, but ignore trailing "/".
-char *sm_gettail(char *s, bool eager)
+/// path_tail() version for showmatches() and redraw_wildmenu():
+/// Return the tail of file name path "s", ignoring a trailing "/".
+static char *showmatches_gettail(char *s, bool eager)
{
char_u *p;
char_u *t = (char_u *)s;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 3f27302617..cbd5b96bef 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -15,9 +15,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
-#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
@@ -27,7 +25,6 @@
#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/menu.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
@@ -249,238 +246,6 @@ void rl_mirror(char *str)
}
}
-/// Get the length of an item as it will be shown in the status line.
-static int wildmenu_match_len(expand_T *xp, char_u *s)
-{
- int len = 0;
-
- int emenu = (xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES);
-
- // Check for menu separators - replace with '|'.
- if (emenu && menu_is_separator((char *)s)) {
- return 1;
- }
-
- while (*s != NUL) {
- s += skip_wildmenu_char(xp, s);
- len += ptr2cells((char *)s);
- MB_PTR_ADV(s);
- }
-
- return len;
-}
-
-/// Return the number of characters that should be skipped in the wildmenu
-/// These are backslashes used for escaping. Do show backslashes in help tags.
-static int skip_wildmenu_char(expand_T *xp, char_u *s)
-{
- if ((rem_backslash((char *)s) && xp->xp_context != EXPAND_HELP)
- || ((xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES)
- && (s[0] == '\t'
- || (s[0] == '\\' && s[1] != NUL)))) {
-#ifndef BACKSLASH_IN_FILENAME
- // TODO(bfredl): Why in the actual fuck are we special casing the
- // shell variety deep in the redraw logic? Shell special snowflakiness
- // should already be eliminated multiple layers before reaching the
- // screen infracstructure.
- if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
- return 2;
- }
-#endif
- return 1;
- }
- return 0;
-}
-
-/// 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.
-///
-/// @param matches list of matches
-void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
-{
-#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m])
- int row;
- char_u *buf;
- int len;
- int clen; // length in screen cells
- int fillchar;
- int attr;
- int i;
- bool highlight = true;
- char_u *selstart = NULL;
- int selstart_col = 0;
- char_u *selend = NULL;
- static int first_match = 0;
- bool add_left = false;
- char_u *s;
- int emenu;
- int l;
-
- if (matches == NULL) { // interrupted completion?
- return;
- }
-
- buf = xmalloc((size_t)Columns * MB_MAXBYTES + 1);
-
- if (match == -1) { // don't show match but original text
- match = 0;
- highlight = false;
- }
- // count 1 for the ending ">"
- clen = wildmenu_match_len(xp, (char_u *)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 += wildmenu_match_len(xp, (char_u *)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 += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
- if ((long)clen >= Columns) {
- break;
- }
- }
- if (i == num_matches) {
- add_left = true;
- }
- }
- }
- if (add_left) {
- while (first_match > 0) {
- clen += wildmenu_match_len(xp, (char_u *)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 (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
- if (i == match) {
- selstart = buf + len;
- selstart_col = clen;
- }
-
- s = (char_u *)L_MATCH(i);
- // Check for menu separators - replace with '|'
- emenu = (xp->xp_context == EXPAND_MENUS
- || xp->xp_context == EXPAND_MENUNAMES);
- if (emenu && menu_is_separator((char *)s)) {
- STRCPY(buf + len, transchar('|'));
- l = (int)STRLEN(buf + len);
- len += l;
- clen += l;
- } else {
- for (; *s != NUL; s++) {
- s += skip_wildmenu_char(xp, s);
- clen += ptr2cells((char *)s);
- if ((l = utfc_ptr2len((char *)s)) > 1) {
- STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
- 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 || wild_menu_showing == WM_LIST) {
- 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) {
- msg_scroll_up(false, false);
- 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 && global_stl_height() == 0) {
- save_p_ls = (int)p_ls;
- save_p_wmh = (int)p_wmh;
- p_ls = 2;
- p_wmh = 0;
- last_status(false);
- }
- wild_menu_showing = WM_SHOWN;
- }
- }
-
- // Tricky: wildmenu can be drawn either over a status line, or at empty
- // scrolled space in the message output
- ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
- ? &msg_grid_adj : &default_grid;
-
- grid_puts(grid, (char *)buf, row, 0, attr);
- if (selstart != NULL && highlight) {
- *selend = NUL;
- grid_puts(grid, (char *)selstart, row, selstart_col, HL_ATTR(HLF_WM));
- }
-
- grid_fill(grid, row, row + 1, clen, Columns,
- fillchar, fillchar, attr);
- }
-
- win_redraw_last_status(topframe);
- xfree(buf);
-}
-
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status