aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt17
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--runtime/doc/treesitter.txt3
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua19
-rw-r--r--runtime/lua/vim/ui.lua4
-rw-r--r--src/nvim/api/extmark.c8
-rw-r--r--src/nvim/arglist.c13
-rw-r--r--src/nvim/cmdexpand.c243
-rw-r--r--src/nvim/decoration.c12
-rw-r--r--src/nvim/decoration.h6
-rw-r--r--src/nvim/drawline.c5
-rw-r--r--src/nvim/eval.lua4
-rw-r--r--src/nvim/eval/funcs.c29
-rw-r--r--src/nvim/eval/typval.c13
-rw-r--r--src/nvim/ex_eval.c9
-rw-r--r--src/nvim/extmark.c2
-rw-r--r--src/nvim/message.c1
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/screen.c235
-rw-r--r--src/nvim/spell.c21
-rw-r--r--src/nvim/testdir/test_arglist.vim10
-rw-r--r--src/nvim/testdir/test_cursor_func.vim24
-rw-r--r--src/nvim/testdir/test_functions.vim12
-rw-r--r--src/nvim/testdir/test_options.vim12
-rw-r--r--src/nvim/testdir/test_popup.vim8
-rw-r--r--src/nvim/testdir/test_vimscript.vim20
-rw-r--r--src/nvim/types.h3
-rw-r--r--test/functional/lua/ui_event_spec.lua27
-rw-r--r--test/functional/lua/ui_spec.lua8
-rw-r--r--test/functional/ui/decorations_spec.lua38
-rw-r--r--test/functional/ui/popupmenu_spec.lua11
31 files changed, 513 insertions, 308 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 50faa612b3..fa9fb286ae 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -78,13 +78,13 @@ chanclose({id} [, {stream}]) Number Closes a channel or one of its streams
chansend({id}, {data}) Number Writes {data} to channel
char2nr({expr} [, {utf8}]) Number ASCII/UTF-8 value of first char in {expr}
charclass({string}) Number character class of {string}
-charcol({expr}) Number column number of cursor or mark
+charcol({expr} [, {winid}]) Number column number of cursor or mark
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
-col({expr}) Number column byte index of cursor or mark
+col({expr} [, {winid}]) Number column byte index of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion
@@ -1089,8 +1089,8 @@ charclass({string}) *charclass()*
Returns 0 if {string} is not a |String|.
- *charcol()*
-charcol({expr}) Same as |col()| but returns the character index of the column
+charcol({expr} [, {winid}]) *charcol()*
+ Same as |col()| but returns the character index of the column
position given with {expr} instead of the byte position.
Example:
@@ -1172,8 +1172,8 @@ clearmatches([{win}]) *clearmatches()*
Can also be used as a |method|: >
GetWin()->clearmatches()
<
- *col()*
-col({expr}) The result is a Number, which is the byte index of the column
+col({expr} [, {winid}) *col()*
+ The result is a Number, which is the byte index of the column
position given with {expr}. The accepted positions are:
. the cursor position
$ the end of the cursor line (the result is the
@@ -1188,6 +1188,8 @@ col({expr}) The result is a Number, which is the byte index of the column
and column number. Most useful when the column is "$", to get
the last column of a specific line. When "lnum" or "col" is
out of range then col() returns zero.
+ With the optional {winid} argument the values are obtained for
+ that window instead of the current window.
To get the line number use |line()|. To get both use
|getpos()|.
For the screen column position use |virtcol()|. For the
@@ -1198,7 +1200,8 @@ col({expr}) The result is a Number, which is the byte index of the column
col("$") length of cursor line plus one
col("'t") column of mark t
col("'" .. markname) column of mark markname
-< The first column is 1. Returns 0 if {expr} is invalid.
+< The first column is 1. Returns 0 if {expr} is invalid or when
+ the window with ID {winid} is not found.
For an uppercase mark the column may actually be in another
buffer.
For the cursor position, when 'virtualedit' is active, the
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 4a277c146f..fd76f11046 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2164,7 +2164,7 @@ A jump table for the options with a short description can be found at |Q_op|.
See 'fileencoding' to control file-content encoding.
*'endoffile'* *'eof'* *'noendoffile'* *'noeof'*
-'endoffile' 'eof' boolean (default on)
+'endoffile' 'eof' boolean (default off)
local to buffer
Indicates that a CTRL-Z character was found at the end of the file
when reading it. Normally only happens when 'fileformat' is "dos".
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 992e5ebe69..d6cd370ec7 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -432,6 +432,9 @@ capture marks comments as to be checked: >
(comment) @spell
<
+
+There is also `@nospell` which disables spellchecking regions with `@spell`.
+
*treesitter-highlight-conceal*
Treesitter highlighting supports |conceal| via the `conceal` metadata. By
convention, nodes to be concealed are captured as `@conceal`, but any capture
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 83a26aff13..f5e5ca1988 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -164,7 +164,7 @@ function TSHighlighter:get_query(lang)
end
---@private
-local function on_line_impl(self, buf, line, spell)
+local function on_line_impl(self, buf, line, is_spell_nav)
self.tree:for_each_tree(function(tstree, tree)
if not tstree then
return
@@ -201,17 +201,26 @@ local function on_line_impl(self, buf, line, spell)
local start_row, start_col, end_row, end_col = node:range()
local hl = highlighter_query.hl_cache[capture]
- local is_spell = highlighter_query:query().captures[capture] == 'spell'
+ local capture_name = highlighter_query:query().captures[capture]
+ local spell = nil
+ if capture_name == 'spell' then
+ spell = true
+ elseif capture_name == 'nospell' then
+ spell = false
+ end
+
+ -- Give nospell a higher priority so it always overrides spell captures.
+ local spell_pri_offset = capture_name == 'nospell' and 1 or 0
- if hl and end_row >= line and (not spell or is_spell) then
+ if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then
a.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
end_line = end_row,
end_col = end_col,
hl_group = hl,
ephemeral = true,
- priority = tonumber(metadata.priority) or 100, -- Low but leaves room below
+ priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below
conceal = metadata.conceal,
- spell = is_spell,
+ spell = spell,
})
end
if start_row > line then
diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua
index d9a3963afc..97dccd83ab 100644
--- a/runtime/lua/vim/ui.lua
+++ b/runtime/lua/vim/ui.lua
@@ -95,8 +95,8 @@ function M.input(opts, on_confirm)
local _canceled = vim.NIL
opts = vim.tbl_extend('keep', opts, { cancelreturn = _canceled })
- local input = vim.fn.input(opts)
- if input == _canceled then
+ local ok, input = pcall(vim.fn.input, opts)
+ if not ok or input == _canceled then
on_confirm(nil)
else
on_confirm(input)
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
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index 57ffcf7b4e..6481da900e 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -117,4 +117,31 @@ describe('vim.ui_attach', function()
})
eq(0, helpers.eval('v:shell_error'))
end)
+
+ it('can receive accurate message kinds even if they are history', function()
+ exec_lua([[
+ vim.cmd.echomsg("'message1'")
+ print('message2')
+ vim.ui_attach(ns, { ext_messages = true }, on_event)
+ vim.cmd.echomsg("'message3'")
+ ]])
+ feed(':messages<cr>')
+ feed('<cr>')
+
+ local actual = exec_lua([[
+ return vim.tbl_filter(function (event)
+ return event[1] == "msg_history_show"
+ end, events)
+ ]])
+ eq({
+ {
+ 'msg_history_show',
+ {
+ { 'echomsg', { { 0, 'message1' } } },
+ { '', { { 0, 'message2' } } },
+ { 'echomsg', { { 0, 'message3' } } },
+ },
+ },
+ }, actual, inspect(actual))
+ end)
end)
diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua
index 8f9d8e0f72..9ee99b4905 100644
--- a/test/functional/lua/ui_spec.lua
+++ b/test/functional/lua/ui_spec.lua
@@ -4,6 +4,7 @@ local exec_lua = helpers.exec_lua
local clear = helpers.clear
local feed = helpers.feed
local eval = helpers.eval
+local poke_eventloop = helpers.poke_eventloop
describe('vim.ui', function()
before_each(function()
@@ -111,13 +112,12 @@ describe('vim.ui', function()
eq('CANCEL', exec_lua('return result'))
end)
- it('does not call on_confirm when interrupted with Ctrl-C #18144', function()
+ it('can return nil when interrupted with Ctrl-C #18144', function()
feed(':lua result = "on_confirm not called"<cr>')
- eq('on_confirm not called', exec_lua('return result'))
feed(':lua vim.ui.input({}, function(input) result = input end)<cr>')
+ poke_eventloop() -- This is needed because Ctrl-C flushes input
feed('Inputted Text<c-c>')
- -- Ctrl-C would make vim.ui.input() throw, so `result = input` won't be executed
- eq('on_confirm not called', exec_lua('return result'))
+ eq(true, exec_lua('return (nil == result)'))
end)
it('can return the identical object when an arbitrary opts.cancelreturn object is given', function()
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 5b62f5b3e1..489c33d8b1 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -176,7 +176,13 @@ describe('decorations providers', function()
beamtrace = {}
local function on_do(kind, ...)
if kind == 'win' or kind == 'spell' then
- a.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 2, end_col = 23, spell = true, ephemeral = true })
+ a.nvim_buf_set_extmark(0, ns, 0, 0, {
+ end_row = 2,
+ end_col = 23,
+ spell = true,
+ priority = 20,
+ ephemeral = true
+ })
end
table.insert(beamtrace, {kind, ...})
end
@@ -234,6 +240,36 @@ describe('decorations providers', function()
{1:~ }|
|
]]}
+
+ -- spell=false with lower priority doesn't disable spell
+ local ns = meths.create_namespace "spell"
+ local id = helpers.curbufmeths.set_extmark(ns, 0, 0, { priority = 30, end_row = 2, end_col = 23, spell = false })
+
+ screen:expect{grid=[[
+ I am well written text. |
+ i am not capitalized. |
+ I am a ^speling mistakke. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- spell=false with higher priority does disable spell
+ helpers.curbufmeths.set_extmark(ns, 0, 0, { id = id, priority = 10, end_row = 2, end_col = 23, spell = false })
+
+ screen:expect{grid=[[
+ I am well written text. |
+ {15:i} am not capitalized. |
+ I am a {16:^speling} {16:mistakke}. |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
end)
it('can predefine highlights', function()
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index e43eef4dfd..7e81d1a577 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -3039,18 +3039,9 @@ describe('builtin popupmenu', function()
eq('bar', meths.get_var('menustr'))
end)
- -- oldtest: Test_popup_command()
+ -- oldtest: Test_popup_command_dump()
it(':popup command', function()
exec([[
- menu Test.Foo Foo
- call assert_fails('popup Test.Foo', 'E336:')
- call assert_fails('popup Test.Foo.X', 'E327:')
- call assert_fails('popup Foo', 'E337:')
- unmenu Test.Foo
- ]])
- eq({}, meths.get_vvar('errors'))
-
- exec([[
func ChangeMenu()
aunmenu PopUp.&Paste
nnoremenu 1.40 PopUp.&Paste :echomsg "pasted"<CR>