diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/extmark.c | 8 | ||||
-rw-r--r-- | src/nvim/arglist.c | 13 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 243 | ||||
-rw-r--r-- | src/nvim/decoration.c | 12 | ||||
-rw-r--r-- | src/nvim/decoration.h | 6 | ||||
-rw-r--r-- | src/nvim/drawline.c | 5 | ||||
-rw-r--r-- | src/nvim/eval.lua | 4 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 29 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 13 | ||||
-rw-r--r-- | src/nvim/ex_eval.c | 9 | ||||
-rw-r--r-- | src/nvim/extmark.c | 2 | ||||
-rw-r--r-- | src/nvim/message.c | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 2 | ||||
-rw-r--r-- | src/nvim/screen.c | 235 | ||||
-rw-r--r-- | src/nvim/spell.c | 21 | ||||
-rw-r--r-- | src/nvim/testdir/test_arglist.vim | 10 | ||||
-rw-r--r-- | src/nvim/testdir/test_cursor_func.vim | 24 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_options.vim | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_popup.vim | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_vimscript.vim | 20 | ||||
-rw-r--r-- | src/nvim/types.h | 3 |
22 files changed, 414 insertions, 278 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index fee6876469..54eb7d9b6b 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -721,8 +721,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); - OPTION_TO_BOOL(decor.spell, spell, false); - if (decor.spell) { + if (opts->spell.type == kObjectTypeNil) { + decor.spell = kNone; + } else { + bool spell = false; + OPTION_TO_BOOL(spell, spell, false); + decor.spell = spell ? kTrue : kFalse; has_decor = true; } diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index c7af1a71be..bd49bc8a36 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -693,8 +693,17 @@ void ex_next(exarg_T *eap) void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED) { for (int i = 0; i < ARGCOUNT; i++) { + // Expand each argument to a full path to catch different paths leading + // to the same file. + char *firstFullname = FullName_save(ARGLIST[i].ae_fname, false); + for (int j = i + 1; j < ARGCOUNT; j++) { - if (path_fnamecmp(ARGLIST[i].ae_fname, ARGLIST[j].ae_fname) == 0) { + char *secondFullname = FullName_save(ARGLIST[j].ae_fname, false); + bool areNamesDuplicate = path_fnamecmp(firstFullname, secondFullname) == 0; + xfree(secondFullname); + + if (areNamesDuplicate) { + // remove one duplicate argument xfree(ARGLIST[j].ae_fname); memmove(ARGLIST + j, ARGLIST + j + 1, (size_t)(ARGCOUNT - j - 1) * sizeof(aentry_T)); @@ -709,6 +718,8 @@ void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED) j--; } } + + xfree(firstFullname); } } 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/decoration.c b/src/nvim/decoration.c index 19e99fa7a6..230d96a15f 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -69,7 +69,11 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { if (row2 >= row1) { - if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal || decor->spell) { + if (!decor + || decor->hl_id + || decor_has_sign(decor) + || decor->conceal + || decor->spell != kNone) { redraw_buf_range_later(buf, row1 + 1, row2 + 1); } } @@ -309,7 +313,7 @@ next_mark: bool conceal = 0; int conceal_char = 0; int conceal_attr = 0; - bool spell = false; + TriState spell = kNone; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); @@ -343,8 +347,8 @@ next_mark: conceal_attr = item.attr_id; } } - if (active && item.decor.spell) { - spell = true; + if (active && item.decor.spell != kNone) { + spell = item.decor.spell; } if ((item.start_row == state->row && item.start_col <= col) && decor_virt_pos(item.decor) diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 9ba621d7a4..8f016c103b 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -45,7 +45,7 @@ struct Decoration { bool hl_eol; bool virt_lines_above; bool conceal; - bool spell; + TriState spell; // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col @@ -61,7 +61,7 @@ struct Decoration { bool ui_watched; // watched for win_extmark }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \ - kHlModeUnknown, false, false, false, false, false, \ + kHlModeUnknown, false, false, false, false, kNone, \ DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0, 0, false } typedef struct { @@ -91,7 +91,7 @@ typedef struct { int conceal_char; int conceal_attr; - bool spell; + TriState spell; } DecorState; EXTERN DecorState decor_state INIT(= { 0 }); diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index cb7d85a467..6d42b91507 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -33,6 +33,7 @@ #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/syntax.h" +#include "nvim/types.h" #include "nvim/undo.h" #include "nvim/window.h" @@ -1722,9 +1723,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, decor_conceal = 2; // really?? } - if (decor_state.spell) { - can_spell = true; - } + can_spell = TRISTATE_TO_BOOL(decor_state.spell, can_spell); } // Check spelling (unless at the end of the line). diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 8bfa6797d0..dd30f51eb4 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -73,12 +73,12 @@ return { chansend={args=2}, char2nr={args={1, 2}, base=1}, charclass={args=1, base=1}, - charcol={args=1, base=1}, + charcol={args={1, 2}, base=1}, charidx={args={2, 3}, base=1}, chdir={args=1, base=1}, cindent={args=1, base=1}, clearmatches={args={0, 1}, base=1}, - col={args=1, base=1}, + col={args={1, 2}, base=1}, complete={args=2, base=2}, complete_add={args=1, base=1}, complete_check={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index be48dc7732..8acdc3256c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -846,9 +846,32 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// otherwise the byte index of the column. static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) { + if (tv_check_for_string_or_list_arg(argvars, 0) == FAIL + || tv_check_for_opt_number_arg(argvars, 1) == FAIL) { + return; + } + + switchwin_T switchwin; + bool winchanged = false; + + if (argvars[1].v_type != VAR_UNKNOWN) { + // use the window specified in the second argument + tabpage_T *tp; + win_T *wp = win_id2wp_tp((int)tv_get_number(&argvars[1]), &tp); + if (wp == NULL || tp == NULL) { + return; + } + + if (switch_win_noblock(&switchwin, wp, tp, true) != OK) { + return; + } + + check_cursor(); + winchanged = true; + } + colnr_T col = 0; int fnum = curbuf->b_fnum; - pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol); if (fp != NULL && fnum == curbuf->b_fnum) { if (fp->col == MAXCOL) { @@ -876,6 +899,10 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) } } rettv->vval.v_number = col; + + if (winchanged) { + restore_win_noblock(&switchwin, true); + } } /// "charcol()" function diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e09fda173b..7e4066adb7 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -46,6 +46,8 @@ static char e_non_empty_string_required_for_argument_nr[] = N_("E1175: Non-empty string required for argument %d"); static char e_number_required_for_argument_nr[] = N_("E1210: Number required for argument %d"); +static char e_string_or_list_required_for_argument_nr[] + = N_("E1222: String or List required for argument %d"); bool tv_in_free_unref_items = false; @@ -3880,6 +3882,17 @@ int tv_check_for_opt_number_arg(const typval_T *const args, const int idx) || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL; } +/// Give an error and return FAIL unless "args[idx]" is a string or a list. +int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST) { + semsg(_(e_string_or_list_required_for_argument_nr), idx + 1); + return FAIL; + } + return OK; +} + /// Get the string value of a "stringish" VimL object. /// /// @param[in] tv Object to get value of. diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 8c2ac895cb..bde2f3c801 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -899,7 +899,14 @@ void ex_else(exarg_T *eap) if (eap->cmdidx == CMD_elseif) { bool error; - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + // When skipping we ignore most errors, but a missing expression is + // wrong, perhaps it should have been "else". + // A double quote here is the start of a string, not a comment. + if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) { + semsg(_(e_invexpr2), eap->arg); + } else { + result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + } // When throwing error exceptions, we want to throw always the first // of several errors in a row. This is what actually happens when diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index df87cc8ab6..015799be06 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -71,7 +71,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col || decor->conceal || decor_has_sign(decor) || decor->ui_watched - || decor->spell) { + || decor->spell != kNone) { decor_full = true; decor = xmemdup(decor, sizeof *decor); } diff --git a/src/nvim/message.c b/src/nvim/message.c index 9fdd7a16a2..538bcde6a5 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3153,6 +3153,7 @@ int msg_end(void) void msg_ext_ui_flush(void) { if (!ui_has(kUIMessages)) { + msg_ext_kind = NULL; return; } diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e938760e67..dc0561d560 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -649,7 +649,7 @@ return { no_mkrc=true, redraw={'statuslines'}, varname='p_eof', - defaults={if_true=true} + defaults={if_true=false} }, { full_name='endofline', abbreviation='eol', 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 diff --git a/src/nvim/spell.c b/src/nvim/spell.c index f24af5c3bf..b1daf4ed23 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1216,7 +1216,14 @@ static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, *decor_lnum = lnum; } decor_redraw_col(wp->w_buffer, col, col, false, &decor_state); - return decor_state.spell; + return decor_state.spell == kTrue; +} + +static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) +{ + bool can_spell; + (void)syn_get_id(wp, lnum, col, false, &can_spell, false); + return can_spell; } /// Moves to the next spell error. @@ -1346,15 +1353,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att : p - buf) > wp->w_cursor.col)) { col = (colnr_T)(p - buf); - bool can_spell = decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error); - - if (!can_spell) { - if (has_syntax) { - (void)syn_get_id(wp, lnum, col, false, &can_spell, false); - } else { - can_spell = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0; - } - } + bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0) + || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error) + || (has_syntax && can_syn_spell(wp, lnum, col)); if (!can_spell) { attr = HLF_COUNT; diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index cae71e10f3..fb8b17cd16 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -418,15 +418,19 @@ func Test_argdedupe() call Reset_arglist() argdedupe call assert_equal([], argv()) + args a a a aa b b a b aa argdedupe call assert_equal(['a', 'aa', 'b'], argv()) + args a b c argdedupe call assert_equal(['a', 'b', 'c'], argv()) + args a argdedupe call assert_equal(['a'], argv()) + args a A b B argdedupe if has('fname_case') @@ -434,11 +438,17 @@ func Test_argdedupe() else call assert_equal(['a', 'b'], argv()) endif + args a b a c a b last argdedupe next call assert_equal('c', expand('%:t')) + + args a ./a + argdedupe + call assert_equal(['a'], argv()) + %argd endfunc diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 9801a45915..7f9e74e94b 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -241,8 +241,9 @@ endfunc " Test for the charcol() function func Test_charcol() - call assert_fails('call charcol({})', 'E731:') - call assert_equal(0, charcol(0)) + call assert_fails('call charcol({})', 'E1222:') + call assert_fails('call charcol(".", [])', 'E1210:') + call assert_fails('call charcol(0)', 'E1222:') new call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678']) @@ -298,6 +299,25 @@ func Test_charcol() call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) iunmap <F3> + " Test for getting the column number in another window. + let winid = win_getid() + new + call win_execute(winid, 'normal 1G') + call assert_equal(1, charcol('.', winid)) + call assert_equal(1, charcol('$', winid)) + call win_execute(winid, 'normal 2G6l') + call assert_equal(7, charcol('.', winid)) + call assert_equal(10, charcol('$', winid)) + + " calling from another tab page also works + tabnew + call assert_equal(7, charcol('.', winid)) + call assert_equal(10, charcol('$', winid)) + tabclose + + " unknown window ID + call assert_equal(0, charcol('.', 10001)) + %bw! endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index aaef01bdf5..5ff544ab55 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1288,6 +1288,9 @@ func Test_col() call assert_equal(0, col([2, '$'])) call assert_equal(0, col([1, 100])) call assert_equal(0, col([1])) + call assert_equal(0, col(v:_null_list)) + call assert_fails('let c = col({})', 'E1222:') + call assert_fails('let c = col(".", [])', 'E1210:') " test for getting the visual start column func T() @@ -1317,6 +1320,15 @@ func Test_col() call assert_equal(4, col('.')) set virtualedit& + " Test for getting the column number in another window + let winid = win_getid() + new + call win_execute(winid, 'normal 1G$') + call assert_equal(3, col('.', winid)) + call win_execute(winid, 'normal 2G') + call assert_equal(8, col('$', winid)) + call assert_equal(0, col('.', 5001)) + bw! endfunc diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index db544e47b8..11c2977a4e 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -1264,5 +1264,17 @@ func Test_keywordprg_empty() let &keywordprg = k endfunc +" check that the very first buffer created does not have 'endoffile' set +func Test_endoffile_default() + let after =<< trim [CODE] + call writefile([execute('set eof?')], 'Xtestout') + qall! + [CODE] + if RunVim([], after, '') + call assert_equal(["\nnoendoffile"], readfile('Xtestout')) + endif + call delete('Xtestout') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index a9b258c5f5..791cce4431 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -862,7 +862,6 @@ func Test_popup_position() endfunc func Test_popup_command() - CheckScreendump CheckFeature menu menu Test.Foo Foo @@ -870,13 +869,18 @@ func Test_popup_command() call assert_fails('popup Test.Foo.X', 'E327:') call assert_fails('popup Foo', 'E337:') unmenu Test.Foo +endfunc + +func Test_popup_command_dump() + CheckFeature menu + CheckScreendump let script =<< trim END func StartTimer() call timer_start(100, {-> ChangeMenu()}) endfunc func ChangeMenu() - nunmenu PopUp.&Paste + aunmenu PopUp.&Paste nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"<CR> echomsg 'changed' endfunc diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 44904af160..c93ba9dcfc 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -193,6 +193,16 @@ func Test_if_while() call assert_equal('ab3j3b2c2b1f1h1km', g:Xpath) endfunc +" Check double quote after skipped "elseif" does not give error E15 +func Test_skipped_elseif() + if "foo" ==? "foo" + let result = "first" + elseif "foo" ==? "foo" + let result = "second" + endif + call assert_equal('first', result) +endfunc + "------------------------------------------------------------------------------- " Test 4: :return {{{1 "------------------------------------------------------------------------------- @@ -3024,7 +3034,7 @@ func Test_nested_if_else_errors() " :elseif without :if let code =<< trim END - elseif + elseif 1 END call writefile(code, 'Xtest') call AssertException(['source Xtest'], 'Vim(elseif):E582: :elseif without :if') @@ -3032,7 +3042,7 @@ func Test_nested_if_else_errors() " :elseif without :if let code =<< trim END while 1 - elseif + elseif 1 endwhile END call writefile(code, 'Xtest') @@ -3042,7 +3052,7 @@ func Test_nested_if_else_errors() let code =<< trim END try finally - elseif + elseif 1 endtry END call writefile(code, 'Xtest') @@ -3051,7 +3061,7 @@ func Test_nested_if_else_errors() " :elseif without :if let code =<< trim END try - elseif + elseif 1 endtry END call writefile(code, 'Xtest') @@ -3062,7 +3072,7 @@ func Test_nested_if_else_errors() try throw "a" catch /a/ - elseif + elseif 1 endtry END call writefile(code, 'Xtest') diff --git a/src/nvim/types.h b/src/nvim/types.h index fb10bf21d9..d90c623955 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -45,6 +45,9 @@ typedef enum { kTrue = 1, } TriState; +#define TRISTATE_TO_BOOL(val, \ + default) ((val) == kTrue ? true : ((val) == kFalse ? false : (default))) + typedef struct Decoration Decoration; #endif // NVIM_TYPES_H |