aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/ui.c39
-rw-r--r--src/nvim/api/vim.c4
-rw-r--r--src/nvim/buffer.c5
-rw-r--r--src/nvim/charset.c14
-rw-r--r--src/nvim/edit.c45
-rw-r--r--src/nvim/eval.c18
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/ex_cmds.c9
-rw-r--r--src/nvim/ex_cmds2.c10
-rw-r--r--src/nvim/ex_docmd.c138
-rw-r--r--src/nvim/ex_getln.c97
-rw-r--r--src/nvim/indent_c.c47
-rw-r--r--src/nvim/lua/vim.lua4
-rw-r--r--src/nvim/main.c7
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/memory.c4
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/misc1.c22
-rw-r--r--src/nvim/msgpack_rpc/channel.c5
-rw-r--r--src/nvim/normal.c101
-rw-r--r--src/nvim/ops.c135
-rw-r--r--src/nvim/options.lua3
-rw-r--r--src/nvim/os/env.c13
-rw-r--r--src/nvim/screen.c15
-rw-r--r--src/nvim/search.c144
-rw-r--r--src/nvim/search.h2
-rw-r--r--src/nvim/spell.c13
-rw-r--r--src/nvim/strings.c2
-rw-r--r--src/nvim/testdir/Makefile130
-rw-r--r--src/nvim/testdir/test_blockedit.vim20
-rw-r--r--src/nvim/testdir/test_bufwintabinfo.vim22
-rw-r--r--src/nvim/testdir/test_cd.vim54
-rw-r--r--src/nvim/testdir/test_cindent.vim31
-rw-r--r--src/nvim/testdir/test_cmdline.vim71
-rw-r--r--src/nvim/testdir/test_functions.vim12
-rw-r--r--src/nvim/testdir/test_gf.vim7
-rw-r--r--src/nvim/testdir/test_gn.vim46
-rw-r--r--src/nvim/testdir/test_ins_complete.vim19
-rw-r--r--src/nvim/testdir/test_normal.vim130
-rw-r--r--src/nvim/testdir/test_options.vim16
-rw-r--r--src/nvim/testdir/test_preview.vim13
-rw-r--r--src/nvim/testdir/test_startup.vim24
-rw-r--r--src/nvim/testdir/test_tabpage.vim66
-rw-r--r--src/nvim/testdir/test_textobjects.vim105
-rw-r--r--src/nvim/testdir/test_undo.vim44
-rw-r--r--src/nvim/testdir/test_winbuf_close.vim36
-rw-r--r--src/nvim/testdir/test_window_cmd.vim13
-rw-r--r--src/nvim/undo.c228
-rw-r--r--src/nvim/vim.h2
-rw-r--r--src/nvim/window.c84
50 files changed, 1453 insertions, 621 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 37d34c5843..76e3927820 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -501,10 +501,8 @@ static void remote_ui_flush(UI *ui)
}
}
-static void remote_ui_cmdline_show(UI *ui, Array args)
+static Array translate_contents(UI *ui, Array contents)
{
- Array new_args = ARRAY_DICT_INIT;
- Array contents = args.items[0].data.array;
Array new_contents = ARRAY_DICT_INIT;
for (size_t i = 0; i < contents.size; i++) {
Array item = contents.items[i].data.array;
@@ -519,23 +517,48 @@ static void remote_ui_cmdline_show(UI *ui, Array args)
ADD(new_item, copy_object(item.items[1]));
ADD(new_contents, ARRAY_OBJ(new_item));
}
- ADD(new_args, ARRAY_OBJ(new_contents));
+ return new_contents;
+}
+
+static Array translate_firstarg(UI *ui, Array args)
+{
+ Array new_args = ARRAY_DICT_INIT;
+ Array contents = args.items[0].data.array;
+
+ ADD(new_args, ARRAY_OBJ(translate_contents(ui, contents)));
for (size_t i = 1; i < args.size; i++) {
ADD(new_args, copy_object(args.items[i]));
}
- push_call(ui, "cmdline_show", new_args);
+ return new_args;
}
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
{
if (!ui->ui_ext[kUINewgrid]) {
- // the representation of cmdline_show changed, translate back
+ // the representation of highlights in cmdline changed, translate back
+ // never consumes args
if (strequal(name, "cmdline_show")) {
- remote_ui_cmdline_show(ui, args);
- // never consumes args
+ Array new_args = translate_firstarg(ui, args);
+ push_call(ui, name, new_args);
+ return;
+ } else if (strequal(name, "cmdline_block_show")) {
+ Array new_args = ARRAY_DICT_INIT;
+ Array block = args.items[0].data.array;
+ Array new_block = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < block.size; i++) {
+ ADD(new_block,
+ ARRAY_OBJ(translate_contents(ui, block.items[i].data.array)));
+ }
+ ADD(new_args, ARRAY_OBJ(new_block));
+ push_call(ui, name, new_args);
+ return;
+ } else if (strequal(name, "cmdline_block_append")) {
+ Array new_args = translate_firstarg(ui, args);
+ push_call(ui, name, new_args);
return;
}
}
+
Array my_args = ARRAY_DICT_INIT;
// Objects are currently single-reference
// make a copy, but only if necessary
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 1ffae8ef43..e78b8c776d 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1083,7 +1083,7 @@ void nvim_set_client_info(uint64_t channel_id, String name,
/// - "buffer" buffer with connected |terminal| instance (optional)
/// - "client" information about the client on the other end of the
/// RPC channel, if it has added it using
-/// |nvim_set_client_info|. (optional)
+/// |nvim_set_client_info()|. (optional)
///
Dictionary nvim_get_chan_info(Integer chan, Error *err)
FUNC_API_SINCE(4)
@@ -1097,7 +1097,7 @@ Dictionary nvim_get_chan_info(Integer chan, Error *err)
/// Get information about all open channels.
///
/// @returns Array of Dictionaries, each describing a channel with
-/// the format specified at |nvim_get_chan_info|.
+/// the format specified at |nvim_get_chan_info()|.
Array nvim_list_chans(void)
FUNC_API_SINCE(4)
{
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 00d472b4c8..64569c294b 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -2659,9 +2659,8 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
* Get alternate file name for current window.
* Return NULL if there isn't any, and give error message if requested.
*/
-char_u *
-getaltfname (
- int errmsg /* give error message */
+char_u * getaltfname(
+ bool errmsg // give error message
)
{
char_u *fname;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index f1b3be6b46..4e8bb3b0d7 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -30,7 +30,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/path.h"
-
+#include "nvim/cursor.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "charset.c.generated.h"
@@ -1465,6 +1465,18 @@ char_u *skipwhite(const char_u *q)
return (char_u *)p;
}
+// getwhitecols: return the number of whitespace
+// columns (bytes) at the start of a given line
+intptr_t getwhitecols_curline(void)
+{
+ return getwhitecols(get_cursor_line_ptr());
+}
+
+intptr_t getwhitecols(const char_u *p)
+{
+ return skipwhite(p) - p;
+}
+
/// Skip over digits
///
/// @param[in] q String to skip digits in.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index c8061d82e4..a20661bb16 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4503,7 +4503,7 @@ static int ins_complete(int c, bool enable_pum)
* first non_blank in the line, if it is not a wordchar
* include it to get a better pattern, but then we don't
* want the "\\<" prefix, check it bellow */
- compl_col = (colnr_T)(skipwhite(line) - line);
+ compl_col = (colnr_T)getwhitecols(line);
compl_startpos.col = compl_col;
compl_startpos.lnum = curwin->w_cursor.lnum;
compl_cont_status &= ~CONT_SOL; /* clear SOL if present */
@@ -4615,7 +4615,7 @@ static int ins_complete(int c, bool enable_pum)
}
}
} else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) {
- compl_col = (colnr_T)(skipwhite(line) - line);
+ compl_col = (colnr_T)getwhitecols(line);
compl_length = (int)curs_col - (int)compl_col;
if (compl_length < 0) /* cursor in indent: empty pattern */
compl_length = 0;
@@ -6909,7 +6909,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
p = look + STRLEN(look);
if ((try_match || try_match_word)
&& curwin->w_cursor.col >= (colnr_T)(p - look)) {
- int match = FALSE;
+ bool match = false;
if (keytyped == KEY_COMPLETE) {
char_u *s;
@@ -6934,29 +6934,30 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
&& (icase
? mb_strnicmp(s, look, (size_t)(p - look))
: STRNCMP(s, look, p - look)) == 0)
- match = TRUE;
- } else
- /* TODO: multi-byte */
- if (keytyped == (int)p[-1] || (icase && keytyped < 256
- && TOLOWER_LOC(keytyped) ==
- TOLOWER_LOC((int)p[-1]))) {
- line = get_cursor_pos_ptr();
- assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
- if ((curwin->w_cursor.col == (colnr_T)(p - look)
- || !vim_iswordc(line[-(p - look) - 1]))
- && (icase
- ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
- : STRNCMP(line - (p - look), look, p - look))
- == 0)
- match = TRUE;
+ match = true;
+ } else {
+ // TODO(@brammool): multi-byte
+ if (keytyped == (int)p[-1]
+ || (icase && keytyped < 256
+ && TOLOWER_LOC(keytyped) == TOLOWER_LOC((int)p[-1]))) {
+ line = get_cursor_pos_ptr();
+ assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
+ if ((curwin->w_cursor.col == (colnr_T)(p - look)
+ || !vim_iswordc(line[-(p - look) - 1]))
+ && (icase
+ ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
+ : STRNCMP(line - (p - look), look, p - look)) == 0) {
+ match = true;
+ }
+ }
}
if (match && try_match_word && !try_match) {
/* "0=word": Check if there are only blanks before the
* word. */
- line = get_cursor_line_ptr();
- if ((int)(skipwhite(line) - line) !=
- (int)(curwin->w_cursor.col - (p - look)))
- match = FALSE;
+ if (getwhitecols(line) !=
+ (int)(curwin->w_cursor.col - (p - look))) {
+ match = false;
+ }
}
if (match) {
return true;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 86f57ee5a2..e65a4d489c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1210,8 +1210,12 @@ int call_vim_function(
if (str_arg_only) {
len = 0;
} else {
- // Recognize a number argument, the others must be strings.
+ // Recognize a number argument, the others must be strings. A dash
+ // is a string too.
vim_str2nr(argv[i], NULL, &len, STR2NR_ALL, &n, NULL, 0);
+ if (len == 1 && *argv[i] == '-') {
+ len = 0;
+ }
}
if (len != 0 && len == (int)STRLEN(argv[i])) {
argvars[i].v_type = VAR_NUMBER;
@@ -10262,8 +10266,10 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
+ tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow);
tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
+ tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol);
tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
tv_dict_add_nr(dict, S_LEN("loclist"),
@@ -10310,6 +10316,15 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// "win_screenpos()" function
+static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv, 2);
+ const win_T *const wp = find_win_by_nr(&argvars[0], NULL);
+ tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
+ tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
+}
+
/*
* "getwinposx()" function
*/
@@ -10594,6 +10609,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef HAVE_ACL
"acl",
#endif
+ "autochdir",
"arabic",
"autocmd",
"browsefilter",
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 23959f348a..328f46443f 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -338,6 +338,7 @@ return {
win_gotoid={args=1},
win_id2tabwin={args=1},
win_id2win={args=1},
+ win_screenpos={args=1},
winbufnr={args=1},
wincol={},
winheight={args=1},
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4dcecae9d8..c9eccfa6b0 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1595,15 +1595,16 @@ void ex_file(exarg_T *eap)
}
if (*eap->arg != NUL || eap->addr_count == 1) {
- if (rename_buffer(eap->arg) == FAIL)
+ if (rename_buffer(eap->arg) == FAIL) {
return;
+ }
+ redraw_tabline = true;
}
- if (!shortmess(SHM_FILEINFO)) {
- // print full file name if :cd used
+ // print file name if no argument or 'F' is not in 'shortmess'
+ if (*eap->arg == NUL || !shortmess(SHM_FILEINFO)) {
fileinfo(false, false, eap->forceit);
}
- redraw_tabline = true;
}
/*
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 120278d3fb..ab24b63110 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2254,6 +2254,15 @@ static int alist_add_list(int count, char_u **files, int after)
}
}
+// Function given to ExpandGeneric() to obtain the possible arguments of the
+// argedit and argdelete commands.
+char_u *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= ARGCOUNT) {
+ return NULL;
+ }
+ return alist_name(&ARGLIST[idx]);
+}
/// ":compiler[!] {name}"
void ex_compiler(exarg_T *eap)
@@ -2719,6 +2728,7 @@ void ex_packadd(exarg_T *eap)
/// ":options"
void ex_options(exarg_T *eap)
{
+ vim_setenv("OPTWIN_CMD", cmdmod.tab ? "tab" : "");
cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index c31242f2ac..5a92a85c97 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2665,8 +2665,8 @@ const char * set_one_cmd_context(
size_t len = 0;
exarg_T ea;
int context = EXPAND_NOTHING;
- int forceit = false;
- int usefilter = false; // Filter instead of file name.
+ bool forceit = false;
+ bool usefilter = false; // Filter instead of file name.
ExpandInit(xp);
xp->xp_pattern = (char_u *)buff;
@@ -2786,9 +2786,9 @@ const char * set_one_cmd_context(
xp->xp_context = EXPAND_NOTHING; /* Default now that we're past command */
- if (*p == '!') { /* forced commands */
- forceit = TRUE;
- ++p;
+ if (*p == '!') { // forced commands
+ forceit = true;
+ p++;
}
/*
@@ -2813,10 +2813,10 @@ const char * set_one_cmd_context(
}
if (ea.cmdidx == CMD_read) {
- usefilter = forceit; /* :r! filter if forced */
- if (*arg == '!') { /* :r !filter */
- ++arg;
- usefilter = TRUE;
+ usefilter = forceit; // :r! filter if forced
+ if (*arg == '!') { // :r !filter
+ arg++;
+ usefilter = true;
}
}
@@ -2978,7 +2978,7 @@ const char * set_one_cmd_context(
// A full match ~user<Tab> will be replaced by user's home
// directory i.e. something like ~user<Tab> -> /home/user/
if (*p == NUL && p > (const char *)xp->xp_pattern + 1
- && match_user(xp->xp_pattern + 1) == 1) {
+ && match_user(xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
++xp->xp_pattern;
}
@@ -3350,6 +3350,19 @@ const char * set_one_cmd_context(
case CMD_xunmap:
return (const char *)set_context_in_map_cmd(
xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx);
+ case CMD_mapclear:
+ case CMD_nmapclear:
+ case CMD_vmapclear:
+ case CMD_omapclear:
+ case CMD_imapclear:
+ case CMD_cmapclear:
+ case CMD_lmapclear:
+ case CMD_smapclear:
+ case CMD_xmapclear:
+ xp->xp_context = EXPAND_MAPCLEAR;
+ xp->xp_pattern = (char_u *)arg;
+ break;
+
case CMD_abbreviate: case CMD_noreabbrev:
case CMD_cabbrev: case CMD_cnoreabbrev:
case CMD_iabbrev: case CMD_inoreabbrev:
@@ -3441,6 +3454,13 @@ const char * set_one_cmd_context(
xp->xp_pattern = (char_u *)arg;
break;
+ case CMD_argdelete:
+ while ((xp->xp_pattern = vim_strchr((const char_u *)arg, ' ')) != NULL) {
+ arg = (const char *)(xp->xp_pattern + 1);
+ }
+ xp->xp_context = EXPAND_ARGLIST;
+ xp->xp_pattern = (char_u *)arg;
+ break;
default:
break;
@@ -4846,6 +4866,7 @@ static struct {
*/
static const char *command_complete[] =
{
+ [EXPAND_ARGLIST] = "arglist",
[EXPAND_AUGROUP] = "augroup",
[EXPAND_BEHAVE] = "behave",
[EXPAND_BUFFERS] = "buffer",
@@ -4870,6 +4891,7 @@ static const char *command_complete[] =
#ifdef HAVE_WORKING_LIBINTL
[EXPAND_LOCALES] = "locale",
#endif
+ [EXPAND_MAPCLEAR] = "mapclear",
[EXPAND_MAPPINGS] = "mapping",
[EXPAND_MENUS] = "menu",
[EXPAND_MESSAGES] = "messages",
@@ -7555,10 +7577,11 @@ static void ex_bang(exarg_T *eap)
*/
static void ex_undo(exarg_T *eap)
{
- if (eap->addr_count == 1) /* :undo 123 */
- undo_time(eap->line2, FALSE, FALSE, TRUE);
- else
+ if (eap->addr_count == 1) { // :undo 123
+ undo_time(eap->line2, false, false, true);
+ } else {
u_undo(1);
+ }
}
static void ex_wundo(exarg_T *eap)
@@ -7591,8 +7614,8 @@ static void ex_redo(exarg_T *eap)
static void ex_later(exarg_T *eap)
{
long count = 0;
- int sec = FALSE;
- int file = FALSE;
+ bool sec = false;
+ bool file = false;
char_u *p = eap->arg;
if (*p == NUL)
@@ -7600,11 +7623,11 @@ static void ex_later(exarg_T *eap)
else if (isdigit(*p)) {
count = getdigits_long(&p);
switch (*p) {
- case 's': ++p; sec = TRUE; break;
- case 'm': ++p; sec = TRUE; count *= 60; break;
- case 'h': ++p; sec = TRUE; count *= 60 * 60; break;
- case 'd': ++p; sec = TRUE; count *= 24 * 60 * 60; break;
- case 'f': ++p; file = TRUE; break;
+ case 's': ++p; sec = true; break;
+ case 'm': ++p; sec = true; count *= 60; break;
+ case 'h': ++p; sec = true; count *= 60 * 60; break;
+ case 'd': ++p; sec = true; count *= 24 * 60 * 60; break;
+ case 'f': ++p; file = true; break;
}
}
@@ -7612,7 +7635,7 @@ static void ex_later(exarg_T *eap)
EMSG2(_(e_invarg2), eap->arg);
else
undo_time(eap->cmdidx == CMD_earlier ? -count : count,
- sec, file, FALSE);
+ sec, file, false);
}
/*
@@ -8131,6 +8154,10 @@ static void ex_normal(exarg_T *eap)
static void ex_startinsert(exarg_T *eap)
{
if (eap->forceit) {
+ // cursor line can be zero on startup
+ if (!curwin->w_cursor.lnum) {
+ curwin->w_cursor.lnum = 1;
+ }
coladvance((colnr_T)MAXCOL);
curwin->w_curswant = MAXCOL;
curwin->w_set_curswant = FALSE;
@@ -8370,23 +8397,25 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
"%",
#define SPEC_PERC 0
"#",
-#define SPEC_HASH 1
- "<cword>", /* cursor word */
-#define SPEC_CWORD 2
- "<cWORD>", /* cursor WORD */
-#define SPEC_CCWORD 3
- "<cfile>", /* cursor path name */
-#define SPEC_CFILE 4
- "<sfile>", /* ":so" file name */
-#define SPEC_SFILE 5
- "<slnum>", /* ":so" file line number */
-#define SPEC_SLNUM 6
- "<afile>", /* autocommand file name */
-# define SPEC_AFILE 7
- "<abuf>", /* autocommand buffer number */
-# define SPEC_ABUF 8
- "<amatch>", /* autocommand match name */
-# define SPEC_AMATCH 9
+#define SPEC_HASH (SPEC_PERC + 1)
+ "<cword>", // cursor word
+#define SPEC_CWORD (SPEC_HASH + 1)
+ "<cWORD>", // cursor WORD
+#define SPEC_CCWORD (SPEC_CWORD + 1)
+ "<cexpr>", // expr under cursor
+#define SPEC_CEXPR (SPEC_CCWORD + 1)
+ "<cfile>", // cursor path name
+#define SPEC_CFILE (SPEC_CEXPR + 1)
+ "<sfile>", // ":so" file name
+#define SPEC_SFILE (SPEC_CFILE + 1)
+ "<slnum>", // ":so" file line number
+#define SPEC_SLNUM (SPEC_SFILE + 1)
+ "<afile>", // autocommand file name
+#define SPEC_AFILE (SPEC_SLNUM + 1)
+ "<abuf>", // autocommand buffer number
+#define SPEC_ABUF (SPEC_AFILE + 1)
+ "<amatch>", // autocommand match name
+#define SPEC_AMATCH (SPEC_ABUF + 1)
};
for (size_t i = 0; i < ARRAY_SIZE(spec_str); ++i) {
@@ -8467,10 +8496,16 @@ eval_vars (
/*
* word or WORD under cursor
*/
- if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD) {
- resultlen = find_ident_under_cursor(&result, (spec_idx == SPEC_CWORD
- ? (FIND_IDENT|FIND_STRING)
- : FIND_STRING));
+ if (spec_idx == SPEC_CWORD
+ || spec_idx == SPEC_CCWORD
+ || spec_idx == SPEC_CEXPR) {
+ resultlen = find_ident_under_cursor(
+ &result,
+ spec_idx == SPEC_CWORD
+ ? (FIND_IDENT | FIND_STRING)
+ : (spec_idx == SPEC_CEXPR
+ ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
+ : FIND_STRING));
if (resultlen == 0) {
*errormsg = (char_u *)"";
return NULL;
@@ -8507,9 +8542,13 @@ eval_vars (
if (*s == '<') /* "#<99" uses v:oldfiles */
++s;
i = getdigits_int(&s);
- *usedlen = (size_t)(s - src); /* length of what we expand */
+ if (s == src + 2 && src[1] == '-') {
+ // just a minus sign, don't skip over it
+ s--;
+ }
+ *usedlen = (size_t)(s - src); // length of what we expand
- if (src[1] == '<') {
+ if (src[1] == '<' && i != 0) {
if (*usedlen < 2) {
/* Should we give an error message for #<text? */
*usedlen = 1;
@@ -8522,6 +8561,9 @@ eval_vars (
return NULL;
}
} else {
+ if (i == 0 && src[1] == '<' && *usedlen > 1) {
+ *usedlen = 1;
+ }
buf = buflist_findnr(i);
if (buf == NULL) {
*errormsg = (char_u *)_(
@@ -9655,6 +9697,14 @@ char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
return NULL;
}
+char_u *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return (char_u *)"<buffer>";
+ }
+ return NULL;
+}
+
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index b4509f9e92..5c02c4c4d6 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -177,10 +177,6 @@ typedef struct command_line_state {
int break_ctrl_c;
expand_T xpc;
long *b_im_ptr;
- // Everything that may work recursively should save and restore the
- // current command line in save_ccline. That includes update_screen(), a
- // custom status line may invoke ":normal".
- struct cmdline_info save_ccline;
} CommandLineState;
typedef struct cmdline_info CmdlineInfo;
@@ -225,11 +221,19 @@ static bool need_cursor_update = false;
# include "ex_getln.c.generated.h"
#endif
-static int cmd_hkmap = 0; /* Hebrew mapping during command line */
+static int cmd_hkmap = 0; // Hebrew mapping during command line
+static int cmd_fkmap = 0; // Farsi mapping during command line
-static int cmd_fkmap = 0; /* Farsi mapping during command line */
+/// Internal entry point for cmdline mode.
+///
+/// caller must use save_cmdline and restore_cmdline. Best is to use
+/// getcmdline or getcmdline_prompt, instead of calling this directly.
static uint8_t *command_line_enter(int firstc, long count, int indent)
{
+ // can be invoked recursively, identify each level
+ static int cmdline_level = 0;
+ cmdline_level++;
+
CommandLineState state, *s = &state;
memset(s, 0, sizeof(CommandLineState));
s->firstc = firstc;
@@ -257,7 +261,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
}
ccline.prompt_id = last_prompt_id++;
- ccline.level++;
+ ccline.level = cmdline_level;
ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
@@ -489,19 +493,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
sb_text_end_cmdline();
- {
- char_u *p = ccline.cmdbuff;
-
- // Make ccline empty, getcmdline() may try to use it.
- ccline.cmdbuff = NULL;
+ char_u *p = ccline.cmdbuff;
- if (ui_is_external(kUICmdline)) {
- ccline.redraw_state = kCmdRedrawNone;
- ui_call_cmdline_hide(ccline.level);
- }
- ccline.level--;
- return p;
+ if (ui_is_external(kUICmdline)) {
+ ui_call_cmdline_hide(ccline.level);
}
+
+ cmdline_level--;
+ return p;
}
static int command_line_check(VimState *state)
@@ -641,9 +640,7 @@ static int command_line_execute(VimState *state, int key)
p_ls = save_p_ls;
p_wmh = save_p_wmh;
last_status(false);
- save_cmdline(&s->save_ccline);
update_screen(VALID); // redraw the screen NOW
- restore_cmdline(&s->save_ccline);
redrawcmd();
save_p_ls = -1;
wild_menu_showing = 0;
@@ -814,18 +811,17 @@ static int command_line_execute(VimState *state, int key)
new_cmdpos = ccline.cmdpos;
}
- save_cmdline(&s->save_ccline);
s->c = get_expr_register();
- restore_cmdline(&s->save_ccline);
if (s->c == '=') {
// Need to save and restore ccline. And set "textlock"
// to avoid nasty things like going to another buffer when
// evaluating an expression.
- save_cmdline(&s->save_ccline);
- ++textlock;
+ CmdlineInfo save_ccline;
+ save_cmdline(&save_ccline);
+ textlock++;
p = get_expr_line();
- --textlock;
- restore_cmdline(&s->save_ccline);
+ textlock--;
+ restore_cmdline(&save_ccline);
if (p != NULL) {
len = (int)STRLEN(p);
@@ -1358,9 +1354,10 @@ static int command_line_handle_key(CommandLineState *s)
beep_flush();
s->c = ESC;
} else {
- save_cmdline(&s->save_ccline);
+ CmdlineInfo save_ccline;
+ save_cmdline(&save_ccline);
s->c = get_expr_register();
- restore_cmdline(&s->save_ccline);
+ restore_cmdline(&save_ccline);
}
}
@@ -1892,9 +1889,7 @@ static int command_line_changed(CommandLineState *s)
curwin->w_redr_status = true;
}
- save_cmdline(&s->save_ccline);
update_screen(SOME_VALID);
- restore_cmdline(&s->save_ccline);
restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work.
@@ -1989,7 +1984,14 @@ getcmdline (
int indent // indent for inside conditionals
)
{
- return command_line_enter(firstc, count, indent);
+ // Be prepared for situations where cmdline can be invoked recursively.
+ // That includes cmd mappings, event handlers, as well as update_screen()
+ // (custom status line eval), which all may invoke ":normal :".
+ CmdlineInfo save_ccline;
+ save_cmdline(&save_ccline);
+ char_u *retval = command_line_enter(firstc, count, indent);
+ restore_cmdline(&save_ccline);
+ return retval;
}
/// Get a command line with a prompt
@@ -2013,7 +2015,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
{
const int msg_col_save = msg_col;
- struct cmdline_info save_ccline;
+ CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
ccline.prompt_id = last_prompt_id++;
@@ -2027,7 +2029,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
int msg_silent_saved = msg_silent;
msg_silent = 0;
- char *const ret = (char *)getcmdline(firstc, 1L, 0);
+ char *const ret = (char *)command_line_enter(firstc, 1L, 0);
restore_cmdline(&save_ccline);
msg_silent = msg_silent_saved;
@@ -2169,6 +2171,7 @@ getexline (
/* When executing a register, remove ':' that's in front of each line. */
if (exec_from_reg && vpeekc() == ':')
(void)vgetc();
+
return getcmdline(c, 1L, indent);
}
@@ -2976,7 +2979,7 @@ void ui_ext_cmdline_block_append(int indent, const char *line)
memcpy(buf + indent, line, strlen(line)); // -V575
Array item = ARRAY_DICT_INIT;
- ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
+ ADD(item, INTEGER_OBJ(0));
ADD(item, STRING_OBJ(cstr_as_string(buf)));
Array content = ARRAY_DICT_INIT;
ADD(content, ARRAY_OBJ(item));
@@ -3008,16 +3011,16 @@ void cmdline_screen_cleared(void)
}
int prev_level = ccline.level-1;
- CmdlineInfo *prev_ccline = ccline.prev_ccline;
- while (prev_level > 0 && prev_ccline) {
- if (prev_ccline->level == prev_level) {
+ CmdlineInfo *line = ccline.prev_ccline;
+ while (prev_level > 0 && line) {
+ if (line->level == prev_level) {
// don't redraw a cmdline already shown in the cmdline window
if (prev_level != cmdwin_level) {
- prev_ccline->redraw_state = kCmdRedrawAll;
+ line->redraw_state = kCmdRedrawAll;
}
prev_level--;
}
- prev_ccline = prev_ccline->prev_ccline;
+ line = line->prev_ccline;
}
need_cursor_update = true;
@@ -3230,6 +3233,7 @@ static void save_cmdline(struct cmdline_info *ccp)
ccline.cmdprompt = NULL;
ccline.xpc = NULL;
ccline.special_char = NUL;
+ ccline.level = 0;
}
/*
@@ -3272,17 +3276,18 @@ void restore_cmdline_alloc(char_u *p)
/// @returns FAIL for failure, OK otherwise
static bool cmdline_paste(int regname, bool literally, bool remcr)
{
- long i;
char_u *arg;
char_u *p;
- int allocated;
+ bool allocated;
struct cmdline_info save_ccline;
/* check for valid regname; also accept special characters for CTRL-R in
* the command line */
if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W
- && regname != Ctrl_A && !valid_yank_reg(regname, false))
+ && regname != Ctrl_A && regname != Ctrl_L
+ && !valid_yank_reg(regname, false)) {
return FAIL;
+ }
/* A register containing CTRL-R can cause an endless loop. Allow using
* CTRL-C to break the loop. */
@@ -3294,9 +3299,9 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
/* Need to save and restore ccline. And set "textlock" to avoid nasty
* things like going to another buffer when evaluating an expression. */
save_cmdline(&save_ccline);
- ++textlock;
- i = get_spec_reg(regname, &arg, &allocated, TRUE);
- --textlock;
+ textlock++;
+ const bool i = get_spec_reg(regname, &arg, &allocated, true);
+ textlock--;
restore_cmdline(&save_ccline);
if (i) {
@@ -4742,6 +4747,7 @@ ExpandFromContext (
} tab[] = {
{ EXPAND_COMMANDS, get_command_name, false, true },
{ EXPAND_BEHAVE, get_behave_arg, true, true },
+ { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
{ EXPAND_MESSAGES, get_messages_arg, true, true },
{ EXPAND_HISTORY, get_history_arg, true, true },
{ EXPAND_USER_COMMANDS, get_user_commands, false, true },
@@ -4769,6 +4775,7 @@ ExpandFromContext (
#endif
{ EXPAND_ENV_VARS, get_env_name, true, true },
{ EXPAND_USER, get_users, true, false },
+ { EXPAND_ARGLIST, get_arglist_name, true, false },
};
int i;
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 2a215f854f..f8ce6200d7 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -75,11 +75,12 @@ find_start_comment ( /* XXX */
/// Find the start of a comment or raw string, not knowing if we are in a
/// comment or raw string right now.
/// Search starts at w_cursor.lnum and goes backwards.
+/// If is_raw is given and returns start of raw_string, sets it to true.
///
/// @returns NULL when not inside a comment or raw string.
///
/// @note "CORS" -> Comment Or Raw String
-static pos_T *ind_find_start_CORS(void)
+static pos_T *ind_find_start_CORS(linenr_T *is_raw)
{
// XXX
static pos_T comment_pos_copy;
@@ -96,6 +97,9 @@ static pos_T *ind_find_start_CORS(void)
// If comment_pos is before rs_pos the raw string is inside the comment.
// If rs_pos is before comment_pos the comment is inside the raw string.
if (comment_pos == NULL || (rs_pos != NULL && lt(*rs_pos, *comment_pos))) {
+ if (is_raw != NULL && rs_pos != NULL) {
+ *is_raw = rs_pos->lnum;
+ }
return rs_pos;
}
return comment_pos;
@@ -384,8 +388,9 @@ int cin_islabel(void)
* it.
*/
curwin->w_cursor.col = 0;
- if ((trypos = ind_find_start_CORS()) != NULL) /* XXX */
+ if ((trypos = ind_find_start_CORS(NULL)) != NULL) { // XXX
curwin->w_cursor = *trypos;
+ }
line = get_cursor_line_ptr();
if (cin_ispreproc(line)) /* ignore #defines, #if, etc. */
@@ -1401,10 +1406,12 @@ static pos_T *find_start_brace(void)
pos = NULL;
/* ignore the { if it's in a // or / * * / comment */
if ((colnr_T)cin_skip2pos(trypos) == trypos->col
- && (pos = ind_find_start_CORS()) == NULL) /* XXX */
+ && (pos = ind_find_start_CORS(NULL)) == NULL) { // XXX
break;
- if (pos != NULL)
+ }
+ if (pos != NULL) {
curwin->w_cursor.lnum = pos->lnum;
+ }
}
curwin->w_cursor = cursor_save;
return trypos;
@@ -1443,7 +1450,7 @@ retry:
pos_copy = *trypos; /* copy trypos, findmatch will change it */
trypos = &pos_copy;
curwin->w_cursor = *trypos;
- if ((trypos_wk = ind_find_start_CORS()) != NULL) { /* XXX */
+ if ((trypos_wk = ind_find_start_CORS(NULL)) != NULL) { // XXX
ind_maxp_wk = ind_maxparen - (int)(cursor_save.lnum
- trypos_wk->lnum);
if (ind_maxp_wk > 0) {
@@ -1793,6 +1800,7 @@ int get_c_indent(void)
int cont_amount = 0; /* amount for continuation line */
int original_line_islabel;
int added_to_amount = 0;
+ linenr_T raw_string_start = 0;
cpp_baseclass_cache_T cache_cpp_baseclass = { false, { MAXLNUM, 0 } };
/* make a copy, value is changed below */
@@ -2059,8 +2067,8 @@ int get_c_indent(void)
}
curwin->w_cursor.lnum = lnum;
- /* Skip a comment or raw string. XXX */
- if ((trypos = ind_find_start_CORS()) != NULL) {
+ // Skip a comment or raw string. XXX
+ if ((trypos = ind_find_start_CORS(NULL)) != NULL) {
lnum = trypos->lnum + 1;
continue;
}
@@ -2443,7 +2451,7 @@ int get_c_indent(void)
* If we're in a comment or raw string now, skip to
* the start of it.
*/
- trypos = ind_find_start_CORS();
+ trypos = ind_find_start_CORS(NULL);
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
@@ -2552,7 +2560,7 @@ int get_c_indent(void)
/* If we're in a comment or raw string now, skip
* to the start of it. */
- trypos = ind_find_start_CORS();
+ trypos = ind_find_start_CORS(NULL);
if (trypos != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
@@ -2581,11 +2589,10 @@ int get_c_indent(void)
break;
}
- /*
- * If we're in a comment or raw string now, skip to the start
- * of it.
- */ /* XXX */
- if ((trypos = ind_find_start_CORS()) != NULL) {
+ // If we're in a comment or raw string now, skip to the start
+ // of it.
+ // XXX
+ if ((trypos = ind_find_start_CORS(&raw_string_start)) != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
continue;
@@ -3096,7 +3103,8 @@ int get_c_indent(void)
}
if (lookfor != LOOKFOR_TERM
&& lookfor != LOOKFOR_JS_KEY
- && lookfor != LOOKFOR_COMMA) {
+ && lookfor != LOOKFOR_COMMA
+ && raw_string_start != curwin->w_cursor.lnum) {
lookfor = LOOKFOR_UNTERM;
}
}
@@ -3351,11 +3359,10 @@ term_again:
l = get_cursor_line_ptr();
- /*
- * If we're in a comment or raw string now, skip to the start
- * of it.
- */ /* XXX */
- if ((trypos = ind_find_start_CORS()) != NULL) {
+ // If we're in a comment or raw string now, skip to the start
+ // of it.
+ // XXX
+ if ((trypos = ind_find_start_CORS(NULL)) != NULL) {
curwin->w_cursor.lnum = trypos->lnum + 1;
curwin->w_cursor.col = 0;
continue;
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index e1bbb03d3f..47f40da72e 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -13,7 +13,7 @@ local function _os_proc_info(pid)
if pid == nil or pid <= 0 or type(pid) ~= 'number' then
error('invalid pid')
end
- local cmd = { 'ps', '-p', pid, '-o', 'ucomm=', }
+ local cmd = { 'ps', '-p', pid, '-o', 'comm=', }
local err, name = _system(cmd)
if 1 == err and string.gsub(name, '%s*', '') == '' then
return {} -- Process not found.
@@ -23,7 +23,7 @@ local function _os_proc_info(pid)
end
local _, ppid = _system({ 'ps', '-p', pid, '-o', 'ppid=', })
-- Remove trailing whitespace.
- name = string.gsub(name, '%s+$', '')
+ name = string.gsub(string.gsub(name, '%s+$', ''), '^.*/', '')
ppid = string.gsub(ppid, '%s+$', '')
ppid = tonumber(ppid) == nil and -1 or tonumber(ppid)
return {
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 96c2168bca..af7c194edc 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -605,9 +605,14 @@ void getout(int exitval)
buf_T *buf = wp->w_buffer;
if (buf_get_changedtick(buf) != -1) {
+ bufref_T bufref;
+
+ set_bufref(&bufref, buf);
apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname,
buf->b_fname, false, buf);
- buf_set_changedtick(buf, -1); // note that we did it already
+ if (bufref_valid(&bufref)) {
+ buf_set_changedtick(buf, -1); // note that we did it already
+ }
// start all over, autocommands may mess up the lists
next_tp = first_tabpage;
break;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 6831e48719..61d944eb75 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3955,7 +3955,7 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
/* Don't count the last line break if 'noeol' and ('bin' or
* 'nofixeol'). */
if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
- && buf->b_ml.ml_line_count == lnum) {
+ && lnum > buf->b_ml.ml_line_count) {
size -= ffdos + 1;
}
}
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index d3d0968a5c..8789075c44 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -619,7 +619,6 @@ void free_all_mem(void)
/* Obviously named calls. */
free_all_autocmds();
- free_all_options();
free_all_marks();
alist_clear(&global_alist);
free_homedir();
@@ -657,6 +656,9 @@ void free_all_mem(void)
/* Destroy all windows. Must come before freeing buffers. */
win_free_all();
+ // Free all option values. Must come after closing windows.
+ free_all_options();
+
free_cmdline_buf();
/* Clear registers. */
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 947cd0735e..4b0824c90f 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -610,7 +610,7 @@ static bool emsgfv(const char *fmt, va_list ap)
/// detected when fuzzing vim.
void iemsg(const char *s)
{
- msg((char_u *)s);
+ emsg((char_u *)s);
#ifdef ABORT_ON_INTERNAL_ERROR
abort();
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 08d1df7f08..50cc9dae02 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -884,8 +884,7 @@ open_line (
&& curbuf->b_p_lisp
&& curbuf->b_p_ai) {
fixthisline(get_lisp_indent);
- p = get_cursor_line_ptr();
- ai_col = (colnr_T)(skipwhite(p) - p);
+ ai_col = (colnr_T)getwhitecols_curline();
}
/*
* May do indenting after opening a new line.
@@ -898,8 +897,7 @@ open_line (
? KEY_OPEN_FORW
: KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum))) {
do_c_expr_indent();
- p = get_cursor_line_ptr();
- ai_col = (colnr_T)(skipwhite(p) - p);
+ ai_col = (colnr_T)getwhitecols_curline();
}
if (vreplace_mode != 0)
State = vreplace_mode;
@@ -1607,11 +1605,19 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
char_u *oldp = ml_get(lnum);
colnr_T oldlen = (colnr_T)STRLEN(oldp);
- /*
- * Can't do anything when the cursor is on the NUL after the line.
- */
- if (col >= oldlen)
+ // Can't do anything when the cursor is on the NUL after the line.
+ if (col >= oldlen) {
+ return FAIL;
+ }
+ // If "count" is zero there is nothing to do.
+ if (count == 0) {
+ return OK;
+ }
+ // If "count" is negative the caller must be doing something wrong.
+ if (count < 1) {
+ IEMSGN("E950: Invalid count for del_bytes(): %ld", count);
return FAIL;
+ }
/* If 'delcombine' is set and deleting (less than) one character, only
* delete the last combining character. */
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 3244d83a93..bb4aea0986 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -505,6 +505,11 @@ end:
static void unsubscribe(Channel *channel, char *event)
{
char *event_string = pmap_get(cstr_t)(event_strings, event);
+ if (!event_string) {
+ WLOG("RPC: ch %" PRIu64 ": tried to unsubscribe unknown event '%s'",
+ channel->id, event);
+ return;
+ }
pmap_del(cstr_t)(channel->rpc.subscribed_events, event_string);
map_foreach_value(channels, channel, {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 8fdc660b68..0bf93ee001 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2991,6 +2991,43 @@ void reset_VIsual(void)
}
}
+// Check for a balloon-eval special item to include when searching for an
+// identifier. When "dir" is BACKWARD "ptr[-1]" must be valid!
+// Returns true if the character at "*ptr" should be included.
+// "dir" is FORWARD or BACKWARD, the direction of searching.
+// "*colp" is in/decremented if "ptr[-dir]" should also be included.
+// "bnp" points to a counter for square brackets.
+static bool find_is_eval_item(
+ const char_u *const ptr,
+ int *const colp,
+ int *const bnp,
+ const int dir)
+{
+ // Accept everything inside [].
+ if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) {
+ *bnp += 1;
+ }
+ if (*bnp > 0) {
+ if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD)) {
+ *bnp -= 1;
+ }
+ return true;
+ }
+
+ // skip over "s.var"
+ if (*ptr == '.') {
+ return true;
+ }
+
+ // two-character item: s->var
+ if (ptr[dir == BACKWARD ? 0 : 1] == '>'
+ && ptr[dir == BACKWARD ? -1 : 0] == '-') {
+ *colp += dir;
+ return true;
+ }
+ return false;
+}
+
/*
* Find the identifier under or to the right of the cursor.
* "find_type" can have one of three values:
@@ -3031,6 +3068,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
int this_class = 0;
int prev_class;
int prevcol;
+ int bn = 0; // bracket nesting
/*
* if i == 0: try to find an identifier
@@ -3042,30 +3080,40 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
* 1. skip to start of identifier/string
*/
col = startcol;
- if (has_mbyte) {
- while (ptr[col] != NUL) {
- this_class = mb_get_class(ptr + col);
- if (this_class != 0 && (i == 1 || this_class != 1))
- break;
- col += (*mb_ptr2len)(ptr + col);
+ while (ptr[col] != NUL) {
+ // Stop at a ']' to evaluate "a[x]".
+ if ((find_type & FIND_EVAL) && ptr[col] == ']') {
+ break;
}
- } else
- while (ptr[col] != NUL
- && (i == 0 ? !vim_iswordc(ptr[col]) : ascii_iswhite(ptr[col]))
- )
- ++col;
+ this_class = mb_get_class(ptr + col);
+ if (this_class != 0 && (i == 1 || this_class != 1)) {
+ break;
+ }
+ col += utfc_ptr2len(ptr + col);
+ }
+ // When starting on a ']' count it, so that we include the '['.
+ bn = ptr[col] == ']';
//
// 2. Back up to start of identifier/string.
//
// Remember class of character under cursor.
- this_class = mb_get_class(ptr + col);
+ if ((find_type & FIND_EVAL) && ptr[col] == ']') {
+ this_class = mb_get_class((char_u *)"a");
+ } else {
+ this_class = mb_get_class(ptr + col);
+ }
while (col > 0 && this_class != 0) {
prevcol = col - 1 - utf_head_off(ptr, ptr + col - 1);
prev_class = mb_get_class(ptr + prevcol);
if (this_class != prev_class
- && (i == 0 || prev_class == 0 || (find_type & FIND_IDENT))) {
+ && (i == 0
+ || prev_class == 0
+ || (find_type & FIND_IDENT))
+ && (!(find_type & FIND_EVAL)
+ || prevcol == 0
+ || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD))) {
break;
}
col = prevcol;
@@ -3096,21 +3144,20 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
/*
* 3. Find the end if the identifier/string.
*/
+ bn = 0;
+ startcol -= col;
col = 0;
- if (has_mbyte) {
- /* Search for point of changing multibyte character class. */
- this_class = mb_get_class(ptr);
- while (ptr[col] != NUL
- && ((i == 0 ? mb_get_class(ptr + col) == this_class
- : mb_get_class(ptr + col) != 0)
- ))
- col += (*mb_ptr2len)(ptr + col);
- } else
- while ((i == 0 ? vim_iswordc(ptr[col])
- : (ptr[col] != NUL && !ascii_iswhite(ptr[col])))
- ) {
- ++col;
- }
+ // Search for point of changing multibyte character class.
+ this_class = mb_get_class(ptr);
+ while (ptr[col] != NUL
+ && ((i == 0
+ ? mb_get_class(ptr + col) == this_class
+ : mb_get_class(ptr + col) != 0)
+ || ((find_type & FIND_EVAL)
+ && col <= (int)startcol
+ && find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
+ col += utfc_ptr2len(ptr + col);
+ }
assert(col >= 0);
return (size_t)col;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index a3fd6e11fd..23948df769 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1110,7 +1110,7 @@ int insert_reg(
)
{
int retval = OK;
- int allocated;
+ bool allocated;
/*
* It is possible to get into an endless loop by having CTRL-R a in
@@ -1187,82 +1187,92 @@ static void stuffescaped(const char *arg, int literally)
}
}
-/*
- * If "regname" is a special register, return TRUE and store a pointer to its
- * value in "argp".
- */
-int get_spec_reg(
+// If "regname" is a special register, return true and store a pointer to its
+// value in "argp".
+bool get_spec_reg(
int regname,
char_u **argp,
- int *allocated, /* return: TRUE when value was allocated */
- int errmsg /* give error message when failing */
+ bool *allocated, // return: true when value was allocated
+ bool errmsg // give error message when failing
)
{
size_t cnt;
*argp = NULL;
- *allocated = FALSE;
+ *allocated = false;
switch (regname) {
case '%': /* file name */
if (errmsg)
check_fname(); /* will give emsg if not set */
*argp = curbuf->b_fname;
- return TRUE;
+ return true;
- case '#': /* alternate file name */
- *argp = getaltfname(errmsg); /* may give emsg if not set */
- return TRUE;
+ case '#': // alternate file name
+ *argp = getaltfname(errmsg); // may give emsg if not set
+ return true;
case '=': /* result of expression */
*argp = get_expr_line();
- *allocated = TRUE;
- return TRUE;
+ *allocated = true;
+ return true;
case ':': /* last command line */
if (last_cmdline == NULL && errmsg)
EMSG(_(e_nolastcmd));
*argp = last_cmdline;
- return TRUE;
+ return true;
case '/': /* last search-pattern */
if (last_search_pat() == NULL && errmsg)
EMSG(_(e_noprevre));
*argp = last_search_pat();
- return TRUE;
+ return true;
case '.': /* last inserted text */
*argp = get_last_insert_save();
- *allocated = TRUE;
- if (*argp == NULL && errmsg)
+ *allocated = true;
+ if (*argp == NULL && errmsg) {
EMSG(_(e_noinstext));
- return TRUE;
+ }
+ return true;
- case Ctrl_F: /* Filename under cursor */
- case Ctrl_P: /* Path under cursor, expand via "path" */
- if (!errmsg)
- return FALSE;
- *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP
- | (regname == Ctrl_P ? FNAME_EXP : 0), 1L, NULL);
- *allocated = TRUE;
- return TRUE;
+ case Ctrl_F: // Filename under cursor
+ case Ctrl_P: // Path under cursor, expand via "path"
+ if (!errmsg) {
+ return false;
+ }
+ *argp = file_name_at_cursor(
+ FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0),
+ 1L, NULL);
+ *allocated = true;
+ return true;
- case Ctrl_W: /* word under cursor */
- case Ctrl_A: /* WORD (mnemonic All) under cursor */
- if (!errmsg)
- return FALSE;
+ case Ctrl_W: // word under cursor
+ case Ctrl_A: // WORD (mnemonic All) under cursor
+ if (!errmsg) {
+ return false;
+ }
cnt = find_ident_under_cursor(argp, (regname == Ctrl_W
? (FIND_IDENT|FIND_STRING)
: FIND_STRING));
*argp = cnt ? vim_strnsave(*argp, cnt) : NULL;
- *allocated = TRUE;
- return TRUE;
+ *allocated = true;
+ return true;
+
+ case Ctrl_L: // Line under cursor
+ if (!errmsg) {
+ return false;
+ }
+
+ *argp = ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false);
+ return true;
case '_': /* black hole: always empty */
*argp = (char_u *)"";
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Paste a yank register into the command line.
@@ -2004,6 +2014,7 @@ void op_insert(oparg_T *oap, long count1)
{
long ins_len, pre_textlen = 0;
char_u *firstline, *ins_text;
+ colnr_T ind_pre = 0;
struct block_def bd;
int i;
pos_T t1;
@@ -2034,9 +2045,13 @@ void op_insert(oparg_T *oap, long count1)
}
// Get the info about the block before entering the text
block_prep(oap, &bd, oap->start.lnum, true);
+ // Get indent information
+ ind_pre = (colnr_T)getwhitecols_curline();
firstline = ml_get(oap->start.lnum) + bd.textcol;
- if (oap->op_type == OP_APPEND)
+
+ if (oap->op_type == OP_APPEND) {
firstline += bd.textlen;
+ }
pre_textlen = (long)STRLEN(firstline);
}
@@ -2089,10 +2104,23 @@ void op_insert(oparg_T *oap, long count1)
if (oap->motion_type == kMTBlockWise) {
struct block_def bd2;
-
- /* The user may have moved the cursor before inserting something, try
- * to adjust the block for that. */
- if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX) {
+ bool did_indent = false;
+
+ // if indent kicked in, the firstline might have changed
+ // but only do that, if the indent actually increased
+ const colnr_T ind_post = (colnr_T)getwhitecols_curline();
+ if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre) {
+ bd.textcol += ind_post - ind_pre;
+ bd.start_vcol += ind_post - ind_pre;
+ did_indent = true;
+ }
+
+ // The user may have moved the cursor before inserting something, try
+ // to adjust the block for that. But only do it, if the difference
+ // does not come from indent kicking in.
+ if (oap->start.lnum == curbuf->b_op_start_orig.lnum
+ && !bd.is_MAX
+ && !did_indent) {
if (oap->op_type == OP_INSERT
&& oap->start.col + oap->start.coladd
!= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
@@ -2206,7 +2234,7 @@ int op_change(oparg_T *oap)
}
firstline = ml_get(oap->start.lnum);
pre_textlen = (long)STRLEN(firstline);
- pre_indent = (long)(skipwhite(firstline) - firstline);
+ pre_indent = (long)getwhitecols(firstline);
bd.textcol = curwin->w_cursor.col;
}
@@ -2227,7 +2255,7 @@ int op_change(oparg_T *oap)
// the indent, exclude that indent change from the inserted text.
firstline = ml_get(oap->start.lnum);
if (bd.textcol > (colnr_T)pre_indent) {
- long new_indent = (long)(skipwhite(firstline) - firstline);
+ long new_indent = (long)getwhitecols(firstline);
pre_textlen += new_indent - pre_indent;
bd.textcol += (colnr_T)(new_indent - pre_indent);
@@ -2651,7 +2679,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
int lendiff = 0;
pos_T old_pos;
char_u *insert_string = NULL;
- int allocated = FALSE;
+ bool allocated = false;
long cnt;
if (flags & PUT_FIXINDENT)
@@ -2747,9 +2775,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
* For special registers '%' (file name), '#' (alternate file name) and
* ':' (last command line), etc. we have to create a fake yank register.
*/
- if (get_spec_reg(regname, &insert_string, &allocated, TRUE)) {
- if (insert_string == NULL)
+ if (get_spec_reg(regname, &insert_string, &allocated, true)) {
+ if (insert_string == NULL) {
return;
+ }
}
if (!curbuf->terminal) {
@@ -4108,10 +4137,9 @@ format_lines (
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len);
- } else if (second_indent > 0) { /* the "leader" for FO_Q_SECOND */
- char_u *p = get_cursor_line_ptr();
- int indent = (int)(skipwhite(p) - p);
+ (long)-next_leader_len);
+ } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
+ int indent = (int)getwhitecols_curline();
if (indent > 0) {
(void)del_bytes(indent, FALSE, FALSE);
@@ -4906,10 +4934,11 @@ void *get_reg_contents(int regname, int flags)
return NULL;
char_u *retval;
- int allocated;
- if (get_spec_reg(regname, &retval, &allocated, FALSE)) {
- if (retval == NULL)
+ bool allocated;
+ if (get_spec_reg(regname, &retval, &allocated, false)) {
+ if (retval == NULL) {
return NULL;
+ }
if (allocated) {
return get_reg_wrap_one_line(retval, flags);
}
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 7cd11e5c31..f8bec30988 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1771,10 +1771,9 @@ return {
{
full_name='printheader', abbreviation='pheader',
type='string', scope={'global'},
- gettext=true,
vi_def=true,
varname='p_header',
- defaults={if_true={vi=N_("%<%f%h%m%=Page %N")}}
+ defaults={if_true={vi="%<%f%h%m%=Page %N"}}
},
{
full_name='printmbcharset', abbreviation='pmbcs',
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 6997156d4c..2f90d0bc9e 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -457,12 +457,15 @@ void expand_env_esc(char_u *restrict srcp,
} else if ((src[0] == ' ' || src[0] == ',') && !one) {
at_start = true;
}
- *dst++ = *src++;
- --dstlen;
+ if (dstlen > 0) {
+ *dst++ = *src++;
+ dstlen--;
- if (prefix != NULL && src - prefix_len >= srcp
- && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
- at_start = true;
+ if (prefix != NULL
+ && src - prefix_len >= srcp
+ && STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
+ at_start = true;
+ }
}
}
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index b79e8f4546..5a43f1b619 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -149,7 +149,7 @@ void redraw_later(int type)
void redraw_win_later(win_T *wp, int type)
{
- if (wp->w_redr_type < type) {
+ if (!exiting && wp->w_redr_type < type) {
wp->w_redr_type = type;
if (type >= NOT_VALID)
wp->w_lines_valid = 0;
@@ -1448,7 +1448,11 @@ static void win_update(win_T *wp)
wp->w_lines[idx].wl_lnum = lnum;
wp->w_lines[idx].wl_valid = true;
- if (row > wp->w_height) { // past end of screen
+
+ // Past end of the window or end of the screen. Note that after
+ // resizing wp->w_height may be end up too big. That's a problem
+ // elsewhere, but prevent a crash here.
+ if (row > wp->w_height || row + wp->w_winrow >= Rows) {
// we may need the size of that too long line later on
if (dollar_vcol == -1) {
wp->w_lines[idx].wl_size = plines_win(wp, lnum, true);
@@ -2460,9 +2464,10 @@ win_line (
ptr = line;
if (has_spell) {
- /* For checking first word with a capital skip white space. */
- if (cap_col == 0)
- cap_col = (int)(skipwhite(line) - line);
+ // For checking first word with a capital skip white space.
+ if (cap_col == 0) {
+ cap_col = (int)getwhitecols(line);
+ }
/* To be able to spell-check over line boundaries copy the end of the
* current line into nextline[]. Above the start of the next line was
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 97975c037b..9280bb6fa4 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -521,7 +521,7 @@ int searchit(
buffer without a window! */
buf_T *buf,
pos_T *pos,
- int dir,
+ Direction dir,
char_u *pat,
long count,
int options,
@@ -570,8 +570,12 @@ int searchit(
&& pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
&& pos->col < MAXCOL - 2) {
// Watch out for the "col" being MAXCOL - 2, used in a closed fold.
- ptr = ml_get_buf(buf, pos->lnum, false) + pos->col;
- start_char_len = *ptr == NUL ? 1 : (*mb_ptr2len)(ptr);
+ ptr = ml_get_buf(buf, pos->lnum, false);
+ if ((int)STRLEN(ptr) <= pos->col) {
+ start_char_len = 1;
+ } else {
+ start_char_len = utfc_ptr2len(ptr + pos->col);
+ }
} else {
start_char_len = 1;
}
@@ -1846,7 +1850,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
} else { /* Searching backwards */
/*
* A comment may contain / * or / /, it may also start or end
- * with / * /. Ignore a / * after / /.
+ * with / * /. Ignore a / * after / / and after *.
*/
if (pos.col == 0)
continue;
@@ -1871,6 +1875,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
} else if ( linep[pos.col - 1] == '/'
&& linep[pos.col] == '*'
+ && (pos.col == 1 || linep[pos.col - 2] != '*')
&& (int)pos.col < comment_col) {
count++;
match_pos = pos;
@@ -2203,21 +2208,17 @@ showmatch(
}
}
-/*
- * findsent(dir, count) - Find the start of the next sentence in direction
- * "dir" Sentences are supposed to end in ".", "!" or "?" followed by white
- * space or a line break. Also stop at an empty line.
- * Return OK if the next sentence was found.
- */
-int findsent(int dir, long count)
+// Find the start of the next sentence, searching in the direction specified
+// by the "dir" argument. The cursor is positioned on the start of the next
+// sentence when found. If the next sentence is found, return OK. Return FAIL
+// otherwise. See ":h sentence" for the precise definition of a "sentence"
+// text object.
+int findsent(Direction dir, long count)
{
pos_T pos, tpos;
int c;
int (*func)(pos_T *);
- int startlnum;
- int noskip = FALSE; /* do not skip blanks */
- int cpo_J;
- int found_dot;
+ bool noskip = false; // do not skip blanks
pos = curwin->w_cursor;
if (dir == FORWARD)
@@ -2251,30 +2252,30 @@ int findsent(int dir, long count)
decl(&pos);
}
- // go back to the previous non-blank char
- found_dot = false;
- while ((c = gchar_pos(&pos)) == ' ' || c == '\t'
- || (dir == BACKWARD
- && vim_strchr((char_u *)".!?)]\"'", c) != NULL)) {
- if (vim_strchr((char_u *)".!?", c) != NULL) {
- /* Only skip over a '.', '!' and '?' once. */
- if (found_dot)
- break;
- found_dot = TRUE;
+ // go back to the previous non-white non-punctuation character
+ bool found_dot = false;
+ while (c = gchar_pos(&pos), ascii_iswhite(c)
+ || vim_strchr((char_u *)".!?)]\"'", c) != NULL) {
+ tpos = pos;
+ if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
+ break;
}
- if (decl(&pos) == -1) {
+ if (found_dot) {
break;
}
- // when going forward: Stop in front of empty line
- if (LINEEMPTY(pos.lnum) && dir == FORWARD) {
- incl(&pos);
- goto found;
+ if (vim_strchr((char_u *) ".!?", c) != NULL) {
+ found_dot = true;
+ }
+ if (vim_strchr((char_u *) ")]\"'", c) != NULL
+ && vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL) {
+ break;
}
+ decl(&pos);
}
- /* remember the line where the search started */
- startlnum = pos.lnum;
- cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
+ // remember the line where the search started
+ const int startlnum = pos.lnum;
+ const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
for (;; ) { /* find end of sentence */
c = gchar_pos(&pos);
@@ -2302,7 +2303,7 @@ int findsent(int dir, long count)
if ((*func)(&pos) == -1) {
if (count)
return FAIL;
- noskip = TRUE;
+ noskip = true;
break;
}
}
@@ -3296,7 +3297,7 @@ int
current_tagblock(
oparg_T *oap,
long count_arg,
- int include /* TRUE == include white space */
+ bool include // true == include white space
)
{
long count = count_arg;
@@ -3310,7 +3311,7 @@ current_tagblock(
char_u *cp;
int len;
int r;
- int do_include = include;
+ bool do_include = include;
bool save_p_ws = p_ws;
int retval = FAIL;
int is_inclusive = true;
@@ -3436,10 +3437,12 @@ again:
}
curwin->w_cursor = end_pos;
- /* If we now have the same text as before reset "do_include" and try
- * again. */
- if (equalpos(start_pos, old_start) && equalpos(end_pos, old_end)) {
- do_include = TRUE;
+ // If we are in Visual mode and now have the same text as before set
+ // "do_include" and try again.
+ if (VIsual_active
+ && equalpos(start_pos, old_start)
+ && equalpos(end_pos, old_end)) {
+ do_include = true;
curwin->w_cursor = old_start;
count = count_arg;
goto again;
@@ -3948,8 +3951,10 @@ current_search(
if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))
dec_cursor();
- pos_T orig_pos; /* position of the cursor at beginning */
- pos_T pos; /* position after the pattern */
+ pos_T orig_pos; // position of the cursor at beginning
+ pos_T first_match; // position of first match
+ pos_T pos; // position after the pattern
+ int result; // result of various function calls
if (VIsual_active) {
orig_pos = pos = curwin->w_cursor;
@@ -3964,8 +3969,9 @@ current_search(
orig_pos = pos = curwin->w_cursor;
}
- // Is the pattern is zero-width?
- int one_char = is_one_char(spats[last_idx].pat, true, &curwin->w_cursor);
+ // Is the pattern is zero-width?, this time, don't care about the direction
+ int one_char = is_one_char(spats[last_idx].pat, true, &curwin->w_cursor,
+ FORWARD);
if (one_char == -1) {
p_ws = old_p_ws;
return FAIL; /* pattern not found */
@@ -3983,9 +3989,9 @@ current_search(
if (!dir && !one_char)
flags = SEARCH_END;
- int result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD),
- spats[last_idx].pat, i ? count : 1,
- SEARCH_KEEP | flags, RE_SEARCH, 0, NULL);
+ result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD),
+ spats[last_idx].pat, i ? count : 1,
+ SEARCH_KEEP | flags, RE_SEARCH, 0, NULL);
/* First search may fail, but then start searching from the
* beginning of the file (cursor might be on the search match)
@@ -4007,15 +4013,19 @@ current_search(
ml_get(curwin->w_buffer->b_ml.ml_line_count));
}
}
+ if (i == 0) {
+ first_match = pos;
+ }
p_ws = old_p_ws;
}
- int flags = forward ? SEARCH_END : 0;
+ const int flags = forward ? SEARCH_END : SEARCH_START;
pos_T start_pos = pos;
+ const Direction direction = forward ? FORWARD : BACKWARD;
// Check again from the current cursor position,
// since the next match might actually be only one char wide
- one_char = is_one_char(spats[last_idx].pat, false, &pos);
+ one_char = is_one_char(spats[last_idx].pat, false, &pos, direction);
if (one_char < 0) {
// search failed, abort
return FAIL;
@@ -4023,9 +4033,26 @@ current_search(
/* move to match, except for zero-width matches, in which case, we are
* already on the next match */
- if (!one_char)
- searchit(curwin, curbuf, &pos, (forward ? FORWARD : BACKWARD),
- spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, 0, NULL);
+ if (!one_char) {
+ p_ws = false;
+ for (int i = 0; i < 2; i++) {
+ result = searchit(curwin, curbuf, &pos, direction,
+ spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH,
+ 0, NULL);
+ // Search successfull, break out from the loop
+ if (result) {
+ break;
+ }
+ // search failed, try again from the last search position match
+ pos = first_match;
+ }
+ }
+
+ p_ws = old_p_ws;
+ // not found
+ if (!result) {
+ return FAIL;
+ }
if (!VIsual_active)
VIsual = start_pos;
@@ -4059,8 +4086,10 @@ current_search(
/// Check if the pattern is one character long or zero-width.
/// If move is true, check from the beginning of the buffer,
/// else from position "cur".
+/// "direction" is FORWARD or BACKWARD.
/// Returns TRUE, FALSE or -1 for failure.
-static int is_one_char(char_u *pattern, bool move, pos_T *cur)
+static int is_one_char(char_u *pattern, bool move, pos_T *cur,
+ Direction direction)
{
regmmatch_T regmatch;
int nmatched = 0;
@@ -4087,7 +4116,7 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur)
// accept a match at the cursor position
flag = SEARCH_START;
}
- if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1,
+ if (searchit(curwin, curbuf, &pos, direction, pattern, 1,
SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position.
@@ -4099,7 +4128,9 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur)
if (!nmatched) {
break;
}
- } while (regmatch.startpos[0].col < pos.col);
+ } while (direction == FORWARD
+ ? regmatch.startpos[0].col < pos.col
+ : regmatch.startpos[0].col > pos.col);
if (!called_emsg) {
result = (nmatched != 0
@@ -4599,7 +4630,7 @@ search_line:
if (depth == -1) {
// match in current file
if (l_g_do_tagpreview != 0) {
- if (!GETFILE_SUCCESS(getfile(0, curwin_save->w_buffer->b_fname,
+ if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL,
NULL, true, lnum, false))) {
break; // failed to jump to file
}
@@ -4607,6 +4638,7 @@ search_line:
setpcmark();
}
curwin->w_cursor.lnum = lnum;
+ check_cursor();
} else {
if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true,
files[depth].lnum, false))) {
diff --git a/src/nvim/search.h b/src/nvim/search.h
index cb50742990..cb094aab8c 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -4,7 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
-#include "nvim/types.h"
+#include "nvim/vim.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/normal.h"
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index f72c47bd15..0ac1dd95e2 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -1399,13 +1399,14 @@ spell_move_to (
capcol = 0;
// For checking first word with a capital skip white space.
- if (capcol == 0)
- capcol = (int)(skipwhite(line) - line);
- else if (curline && wp == curwin) {
+ if (capcol == 0) {
+ capcol = (int)getwhitecols(line);
+ } else if (curline && wp == curwin) {
// For spellbadword(): check if first word needs a capital.
- col = (int)(skipwhite(line) - line);
- if (check_need_cap(lnum, col))
+ col = (int)getwhitecols(line);
+ if (check_need_cap(lnum, col)) {
capcol = col;
+ }
// Need to get the line again, may have looked at the previous
// one.
@@ -2977,7 +2978,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
line = get_cursor_line_ptr();
endcol = 0;
- if ((int)(skipwhite(line) - line) >= (int)col) {
+ if (getwhitecols(line) >= (int)col) {
// At start of line, check if previous line is empty or sentence
// ends there.
if (lnum == 1)
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index f24de72743..17c4a75a64 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -1217,6 +1217,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
str_arg_l = 3;
zero_padding = 0;
} else {
+ // Regular float number
format[0] = '%';
size_t l = 1;
if (force_sign) {
@@ -1241,7 +1242,6 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec);
format[l + 1] = NUL;
- // Regular float number
str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f);
assert(str_arg_l < sizeof(tmp));
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 1e3dc04049..10cbd91e36 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -14,117 +14,42 @@ export NVIM_PRG := $(NVIM_PRG)
export TMPDIR := $(abspath ../../../Xtest-tmpdir)
SCRIPTS_DEFAULT = \
- test14.out \
- test24.out \
- test37.out \
- test42.out \
- test48.out \
- test52.out \
- test64.out \
+ test14.out \
+ test24.out \
+ test37.out \
+ test42.out \
+ test48.out \
+ test52.out \
+ test64.out \
ifneq ($(OS),Windows_NT)
SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \
- test17.out \
- test49.out \
+ test17.out \
+ test49.out \
endif
SCRIPTS ?= $(SCRIPTS_DEFAULT)
# Tests using runtest.vim.
-# Keep test_alot*.res as the last one, sort the others.
-NEW_TESTS ?= \
- test_arabic.res \
- test_autocmd.res \
- test_bufwintabinfo.res \
- test_changedtick.res \
- test_charsearch.res \
- test_cindent.res \
- test_clientserver.res \
- test_close_count.res \
- test_cmdline.res \
- test_command_count.res \
- test_cscope.res \
- test_curswant.res \
- test_digraph.res \
- test_edit.res \
- test_erasebackword.res \
- test_exists.res \
- test_diffmode.res \
- test_farsi.res \
- test_file_size.res \
- test_filter_map.res \
- test_find_complete.res \
- test_fixeol.res \
- test_findfile.res \
- test_fnameescape.res \
- test_fold.res \
- test_ga.res \
- test_getvar.res \
- test_glob2regpat.res \
- test_gf.res \
- test_gn.res \
- test_hardcopy.res \
- test_help_tagjump.res \
- test_hide.res \
- test_highlight.res \
- test_history.res \
- test_hlsearch.res \
- test_increment.res \
- test_increment_dbcs.res \
- test_ins_complete.res \
- test_lambda.res \
- test_langmap.res \
- test_let.res \
- test_lineending.res \
- test_listdict.res \
- test_listchars.res \
- test_makeencoding.res \
- test_marks.res \
- test_match.res \
- test_matchadd_conceal.res \
- test_mksession.res \
- test_nested_function.res \
- test_normal.res \
- test_number.res \
- test_options.res \
- test_profile.res \
- test_put.res \
- test_python2.res \
- test_python3.res \
- test_quickfix.res \
- test_quotestar.res \
- test_recover.res \
- test_registers.res \
- test_retab.res \
- test_scrollbind.res \
- test_search.res \
- test_signs.res \
- test_smartindent.res \
- test_spell.res \
- test_stat.res \
- test_startup.res \
- test_substitute.res \
- test_swap.res \
- test_syntax.res \
- test_system.res \
- test_tab.res \
- test_tabpage.res \
- test_textobjects.res \
- test_timers.res \
- test_undo.res \
- test_usercommands.res \
- test_user_func.res \
- test_vimscript.res \
- test_visual.res \
- test_winbuf_close.res \
- test_window_id.res \
- test_windows_home.res \
- test_wordcount.res \
- test_writefile.res \
- test_alot_latin.res \
- test_alot_utf8.res \
- test_alot.res
+NEW_TESTS_ALOT := test_alot_utf8 test_alot
+NEW_TESTS_IN_ALOT := $(shell sed '/^source/ s/^source //;s/\.vim$$//' test_alot*.vim)
+# Ignored tests.
+# test_alot_latin1: Nvim does not allow setting encoding.
+# test_arglist: ported to Lua, but kept for easier merging.
+# test_autochdir: ported to Lua, but kept for easier merging.
+# test_eval_func: used as include in old-style test (test_eval.in).
+# test_listlbr: Nvim does not allow setting encoding.
+# test_largefile: uses too much resources to run on CI.
+NEW_TESTS_IGNORE := $(NEW_TESTS_IN_ALOT) $(NEW_TESTS_ALOT) \
+ test_alot_latin \
+ test_arglist \
+ test_autochdir \
+ test_eval_func \
+ test_listlbr \
+ test_largefile \
+
+NEW_TESTS = $(addsuffix .res,$(sort $(filter-out $(NEW_TESTS_IGNORE),$(basename $(notdir $(wildcard test_*.vim))))) $(NEW_TESTS_ALOT))
SCRIPTS_GUI := test16.out
@@ -218,6 +143,7 @@ test1.out: .gdbinit test1.in
@/bin/sh runnvim.sh --oldesttest $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in
@rm -rf X* test.ok viminfo
+# Explicit dependencies.
test49.out: test49.vim
nolog:
diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim
new file mode 100644
index 0000000000..4a8d59952e
--- /dev/null
+++ b/src/nvim/testdir/test_blockedit.vim
@@ -0,0 +1,20 @@
+" Test for block inserting
+"
+" TODO: rewrite test39.in into this new style test
+
+func Test_blockinsert_indent()
+ new
+ filetype plugin indent on
+ setlocal sw=2 et ft=vim
+ call setline(1, ['let a=[', ' ''eins'',', ' ''zwei'',', ' ''drei'']'])
+ call cursor(2, 3)
+ exe "norm! \<c-v>2jI\\ \<esc>"
+ call assert_equal(['let a=[', ' \ ''eins'',', ' \ ''zwei'',', ' \ ''drei'']'],
+ \ getline(1,'$'))
+ " reset to sane state
+ filetype off
+ bwipe!
+endfunc
+
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim
index a592cd7b11..a6b4524cc0 100644
--- a/src/nvim/testdir/test_bufwintabinfo.vim
+++ b/src/nvim/testdir/test_bufwintabinfo.vim
@@ -39,17 +39,35 @@ function Test_getbufwintabinfo()
let w2_id = win_getid()
tabnew | let w3_id = win_getid()
new | let w4_id = win_getid()
- new | let w5_id = win_getid()
+ vert new | let w5_id = win_getid()
call setwinvar(0, 'signal', 'green')
tabfirst
let winlist = getwininfo()
call assert_equal(5, len(winlist))
+ call assert_equal(winwidth(1), winlist[0].width)
+ call assert_equal(0, winlist[0].wincol)
+ let tablineheight = winlist[0].winrow == 1 ? 1 : 0
+ call assert_equal(tablineheight, winlist[0].winrow) " tabline adds one
+
call assert_equal(winbufnr(2), winlist[1].bufnr)
call assert_equal(winheight(2), winlist[1].height)
+ call assert_equal(0, winlist[1].wincol)
+ call assert_equal(tablineheight + winheight(1) + 1, winlist[1].winrow)
+
call assert_equal(1, winlist[2].winnr)
+ call assert_equal(tablineheight, winlist[2].winrow)
+ call assert_equal(0, winlist[2].wincol)
+
+ call assert_equal(winlist[2].width + 1, winlist[3].wincol)
+ call assert_equal(0, winlist[4].wincol)
+
+ call assert_equal(1, winlist[0].tabnr)
+ call assert_equal(1, winlist[1].tabnr)
+ call assert_equal(2, winlist[2].tabnr)
call assert_equal(2, winlist[3].tabnr)
+ call assert_equal(2, winlist[4].tabnr)
+
call assert_equal('green', winlist[2].variables.signal)
- call assert_equal(winwidth(1), winlist[0].width)
call assert_equal(w4_id, winlist[3].winid)
let winfo = getwininfo(w5_id)[0]
call assert_equal(2, winfo.tabnr)
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index e573419bd0..770ed55b8d 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -8,6 +8,60 @@ endfunc
func Test_cd_up_and_down()
let path = getcwd()
cd ..
+ call assert_notequal(path, getcwd())
exe 'cd ' . path
call assert_equal(path, getcwd())
endfunc
+
+func Test_cd_no_arg()
+ if has('unix')
+ " Test that cd without argument goes to $HOME directory on Unix systems.
+ let path = getcwd()
+ cd
+ call assert_equal($HOME, getcwd())
+ call assert_notequal(path, getcwd())
+ exe 'cd ' . path
+ call assert_equal(path, getcwd())
+ else
+ " Test that cd without argument echoes cwd on non-Unix systems.
+ call assert_match(getcwd(), execute('cd'))
+ endif
+endfunc
+
+func Test_cd_minus()
+ " Test the :cd - goes back to the previous directory.
+ let path = getcwd()
+ cd ..
+ let path_dotdot = getcwd()
+ call assert_notequal(path, path_dotdot)
+ cd -
+ call assert_equal(path, getcwd())
+ cd -
+ call assert_equal(path_dotdot, getcwd())
+ cd -
+ call assert_equal(path, getcwd())
+endfunc
+
+func Test_cd_with_cpo_chdir()
+ e Xfoo
+ call setline(1, 'foo')
+ let path = getcwd()
+ " set cpo+=.
+
+ " :cd should fail when buffer is modified and 'cpo' contains dot.
+ " call assert_fails('cd ..', 'E747:')
+ call assert_equal(path, getcwd())
+
+ " :cd with exclamation mark should succeed.
+ cd! ..
+ call assert_notequal(path, getcwd())
+
+ " :cd should succeed when buffer has been written.
+ w!
+ exe 'cd ' . path
+ call assert_equal(path, getcwd())
+
+ call delete('Xfoo')
+ set cpo&
+ bw!
+endfunc
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
index 444c4c4109..7c2c5e341c 100644
--- a/src/nvim/testdir/test_cindent.vim
+++ b/src/nvim/testdir/test_cindent.vim
@@ -68,9 +68,38 @@ func Test_cino_extern_c()
call assert_equal(pair[2], getline(len(lines) + 1), 'Failed for "' . string(lines) . '"')
endfor
+ bwipe!
+endfunc
+func Test_cindent_rawstring()
+ new
+ setl cindent
+ call feedkeys("i" .
+ \ "int main() {\<CR>" .
+ \ "R\"(\<CR>" .
+ \ ")\";\<CR>" .
+ \ "statement;\<Esc>", "x")
+ call assert_equal("\tstatement;", getline(line('.')))
+ bw!
+endfunc
- bwipe!
+func Test_cindent_expr()
+ new
+ func! MyIndentFunction()
+ return v:lnum == 1 ? shiftwidth() : 0
+ endfunc
+ setl expandtab sw=8 indentkeys+=; indentexpr=MyIndentFunction()
+ call setline(1, ['var_a = something()', 'b = something()'])
+ call cursor(1, 1)
+ call feedkeys("^\<c-v>j$A;\<esc>", 'tnix')
+ call assert_equal([' var_a = something();', 'b = something();'], getline(1, '$'))
+
+ %d
+ call setline(1, [' var_a = something()', ' b = something()'])
+ call cursor(1, 1)
+ call feedkeys("^\<c-v>j$A;\<esc>", 'tnix')
+ call assert_equal([' var_a = something();', ' b = something()'], getline(1, '$'))
+ bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 5a43838218..c302948ba3 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -137,6 +137,11 @@ func Test_getcompletion()
let l = getcompletion('v:notexists', 'var')
call assert_equal([], l)
+ args a.c b.c
+ let l = getcompletion('', 'arglist')
+ call assert_equal(['a.c', 'b.c'], l)
+ %argdelete
+
let l = getcompletion('', 'augroup')
call assert_true(index(l, 'END') >= 0)
let l = getcompletion('blahblah', 'augroup')
@@ -222,6 +227,11 @@ func Test_getcompletion()
let l = getcompletion('not', 'messages')
call assert_equal([], l)
+ let l = getcompletion('', 'mapclear')
+ call assert_true(index(l, '<buffer>') >= 0)
+ let l = getcompletion('not', 'mapclear')
+ call assert_equal([], l)
+
if has('cscope')
let l = getcompletion('', 'cscope')
let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show']
@@ -311,6 +321,9 @@ func Test_paste_in_cmdline()
call feedkeys("ft:aaa \<C-R>\<C-F> bbb\<C-B>\"\<CR>", 'tx')
call assert_equal('"aaa /tmp/some bbb', @:)
+ call feedkeys(":aaa \<C-R>\<C-L> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa '.getline(1).' bbb', @:)
+
set incsearch
call feedkeys("fy:aaa veryl\<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')
call assert_equal('"aaa verylongword bbb', @:)
@@ -375,6 +388,27 @@ func Test_cmdline_complete_user_cmd()
delcommand Foo
endfunc
+func Test_cmdline_write_alternatefile()
+ new
+ call setline('.', ['one', 'two'])
+ f foo.txt
+ new
+ f #-A
+ call assert_equal('foo.txt-A', expand('%'))
+ f #<-B.txt
+ call assert_equal('foo-B.txt', expand('%'))
+ f %<
+ call assert_equal('foo-B', expand('%'))
+ new
+ call assert_fails('f #<', 'E95')
+ bw!
+ f foo-B.txt
+ f %<-A
+ call assert_equal('foo-B-A', expand('%'))
+ bw!
+ bw!
+endfunc
+
" using a leading backslash here
set cpo+=C
@@ -430,6 +464,22 @@ func Test_getcmdtype()
cunmap <F6>
endfunc
+func Test_getcmdwintype()
+ call feedkeys("q/:let a = getcmdwintype()\<CR>:q\<CR>", 'x!')
+ call assert_equal('/', a)
+
+ call feedkeys("q?:let a = getcmdwintype()\<CR>:q\<CR>", 'x!')
+ call assert_equal('?', a)
+
+ call feedkeys("q::let a = getcmdwintype()\<CR>:q\<CR>", 'x!')
+ call assert_equal(':', a)
+
+ call feedkeys(":\<C-F>:let a = getcmdwintype()\<CR>:q\<CR>", 'x!')
+ call assert_equal(':', a)
+
+ call assert_equal('', getcmdwintype())
+endfunc
+
func Test_verbosefile()
set verbosefile=Xlog
echomsg 'foo'
@@ -440,4 +490,25 @@ func Test_verbosefile()
call delete('Xlog')
endfunc
+func Test_setcmdpos()
+ func InsertTextAtPos(text, pos)
+ call assert_equal(0, setcmdpos(a:pos))
+ return a:text
+ endfunc
+
+ " setcmdpos() with position in the middle of the command line.
+ call feedkeys(":\"12\<C-R>=InsertTextAtPos('a', 3)\<CR>b\<CR>", 'xt')
+ call assert_equal('"1ab2', @:)
+
+ call feedkeys(":\"12\<C-R>\<C-R>=InsertTextAtPos('a', 3)\<CR>b\<CR>", 'xt')
+ call assert_equal('"1b2a', @:)
+
+ " setcmdpos() with position beyond the end of the command line.
+ call feedkeys(":\"12\<C-B>\<C-R>=InsertTextAtPos('a', 10)\<CR>b\<CR>", 'xt')
+ call assert_equal('"12ab', @:)
+
+ " setcmdpos() returns 1 when not editing the command line.
+ call assert_equal(1, setcmdpos(3))
+endfunc
+
set cpo&
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index e2a035b0b2..8fde63b55f 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -701,6 +701,7 @@ endfunc
func Test_byte2line_line2byte()
new
+ set endofline
call setline(1, ['a', 'bc', 'd'])
set fileformat=unix
@@ -721,7 +722,16 @@ func Test_byte2line_line2byte()
call assert_equal([-1, -1, 1, 4, 8, 11, -1],
\ map(range(-1, 5), 'line2byte(v:val)'))
- set fileformat&
+ bw!
+ set noendofline nofixendofline
+ normal a-
+ for ff in ["unix", "mac", "dos"]
+ let &fileformat = ff
+ call assert_equal(1, line2byte(1))
+ call assert_equal(2, line2byte(2)) " line2byte(line("$") + 1) is the buffer size plus one (as per :help line2byte).
+ endfor
+
+ set endofline& fixendofline& fileformat&
bw!
endfunc
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index ef1bf1075b..c352379697 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -7,7 +7,8 @@ func Test_gf_url()
\ "first test for URL://machine.name/tmp/vimtest2a and other text",
\ "second test for URL://machine.name/tmp/vimtest2b. And other text",
\ "third test for URL:\\\\machine.name\\vimtest2c and other text",
- \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text"
+ \ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text",
+ \ "fifth test for URL://machine.name/tmp?q=vim&opt=yes and other text",
\ ])
call cursor(1,1)
call search("^first")
@@ -28,6 +29,10 @@ func Test_gf_url()
call search("URL")
call assert_equal("URL:\\\\machine.name\\tmp\\vimtest2d", expand("<cfile>"))
+ call search("^fifth")
+ call search("URL")
+ call assert_equal("URL://machine.name/tmp?q=vim&opt=yes", expand("<cfile>"))
+
set isf&vim
enew!
endfunc
diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim
index b2a2937d88..405425a42b 100644
--- a/src/nvim/testdir/test_gn.vim
+++ b/src/nvim/testdir/test_gn.vim
@@ -5,51 +5,51 @@ func Test_gn_command()
noautocmd new
" replace a single char by itsself quoted:
call setline('.', 'abc x def x ghi x jkl')
- let @/='x'
+ let @/ = 'x'
exe "norm! cgn'x'\<esc>.."
call assert_equal("abc 'x' def 'x' ghi 'x' jkl", getline('.'))
sil! %d_
" simple search match
call setline('.', 'foobar')
- let @/='foobar'
+ let @/ = 'foobar'
exe "norm! gncsearchmatch"
call assert_equal('searchmatch', getline('.'))
sil! %d _
" replace a multi-line match
call setline('.', ['', 'one', 'two'])
- let @/='one\_s*two\_s'
+ let @/ = 'one\_s*two\_s'
exe "norm! gnceins\<CR>zwei"
call assert_equal(['','eins','zwei'], getline(1,'$'))
sil! %d _
" test count argument
call setline('.', ['', 'abcdx | abcdx | abcdx'])
- let @/='[a]bcdx'
+ let @/ = '[a]bcdx'
exe "norm! 2gnd"
call assert_equal(['','abcdx | | abcdx'], getline(1,'$'))
sil! %d _
" join lines
call setline('.', ['join ', 'lines'])
- let @/='$'
+ let @/ = '$'
exe "norm! 0gnd"
call assert_equal(['join lines'], getline(1,'$'))
sil! %d _
" zero-width match
call setline('.', ['', 'zero width pattern'])
- let @/='\>\zs'
+ let @/ = '\>\zs'
exe "norm! 0gnd"
call assert_equal(['', 'zerowidth pattern'], getline(1,'$'))
sil! %d _
" delete first and last chars
call setline('.', ['delete first and last chars'])
- let @/='^'
+ let @/ = '^'
exe "norm! 0gnd$"
- let @/='\zs'
+ let @/ = '\zs'
exe "norm! gnd"
call assert_equal(['elete first and last char'], getline(1,'$'))
sil! %d _
@@ -62,14 +62,14 @@ func Test_gn_command()
" backwards search
call setline('.', ['my very excellent mother just served us nachos'])
- let @/='mother'
+ let @/ = 'mother'
exe "norm! $cgNmongoose"
call assert_equal(['my very excellent mongoose just served us nachos'], getline(1,'$'))
sil! %d _
" search for single char
call setline('.', ['','for (i=0; i<=10; i++)'])
- let @/='i'
+ let @/ = 'i'
exe "norm! cgnj"
call assert_equal(['','for (j=0; i<=10; i++)'], getline(1,'$'))
sil! %d _
@@ -77,28 +77,28 @@ func Test_gn_command()
" search hex char
call setline('.', ['','Y'])
set noignorecase
- let @/='\%x59'
+ let @/ = '\%x59'
exe "norm! gnd"
call assert_equal(['',''], getline(1,'$'))
sil! %d _
" test repeating gdn
call setline('.', ['', '1', 'Johnny', '2', 'Johnny', '3'])
- let @/='Johnny'
+ let @/ = 'Johnny'
exe "norm! dgn."
call assert_equal(['','1', '', '2', '', '3'], getline(1,'$'))
sil! %d _
" test repeating gUgn
call setline('.', ['', '1', 'Depp', '2', 'Depp', '3'])
- let @/='Depp'
+ let @/ = 'Depp'
exe "norm! gUgn."
call assert_equal(['', '1', 'DEPP', '2', 'DEPP', '3'], getline(1,'$'))
sil! %d _
" test using look-ahead assertions
call setline('.', ['a:10', '', 'a:1', '', 'a:20'])
- let @/='a:0\@!\zs\d\+'
+ let @/ = 'a:0\@!\zs\d\+'
exe "norm! 2nygno\<esc>p"
call assert_equal(['a:10', '', 'a:1', '1', '', 'a:20'], getline(1,'$'))
sil! %d _
@@ -111,6 +111,24 @@ func Test_gn_command()
call assert_equal(['foo baz'], getline(1,'$'))
sil! %d_
+ " search upwards with nowrapscan set
+ call setline('.', ['foo', 'bar', 'foo', 'baz'])
+ set nowrapscan
+ let @/ = 'foo'
+ $
+ norm! dgN
+ call assert_equal(['foo', 'bar', '', 'baz'], getline(1,'$'))
+ sil! %d_
+
+ " search using the \zs atom
+ call setline(1, [' nnoremap', '' , 'nnoremap'])
+ set wrapscan&vim
+ let @/ = '\_s\zsnnoremap'
+ $
+ norm! cgnmatch
+ call assert_equal([' nnoremap', '', 'match'], getline(1,'$'))
+ sil! %d_
+
set wrapscan&vim
set belloff&vim
endfu
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index c307e33cbf..5ff63e58ba 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -217,3 +217,22 @@ function Test_CompleteDoneList()
let s:called_completedone = 0
au! CompleteDone
endfunc
+
+func Test_omni_dash()
+ func Omni(findstart, base)
+ if a:findstart
+ return 5
+ else
+ echom a:base
+ return ['-help', '-v']
+ endif
+ endfunc
+ set omnifunc=Omni
+ new
+ exe "normal Gofind -\<C-x>\<C-o>"
+ call assert_equal("\n-\nmatch 1 of 2", execute(':2mess'))
+
+ bwipe!
+ delfunc Omni
+ set omnifunc=
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 4c63bd1f71..d07b3fdbce 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -392,10 +392,31 @@ func! Test_normal10_expand()
call setline(1, ['1', 'ifooar,,cbar'])
2
norm! $
- let a=expand('<cword>')
- let b=expand('<cWORD>')
- call assert_equal('cbar', a)
- call assert_equal('ifooar,,cbar', b)
+ call assert_equal('cbar', expand('<cword>'))
+ call assert_equal('ifooar,,cbar', expand('<cWORD>'))
+
+ call setline(1, ['prx = list[idx];'])
+ 1
+ let expected = ['', 'prx', 'prx', 'prx',
+ \ 'list', 'list', 'list', 'list', 'list', 'list', 'list',
+ \ 'idx', 'idx', 'idx', 'idx',
+ \ 'list[idx]',
+ \ '];',
+ \ ]
+ for i in range(1, 16)
+ exe 'norm ' . i . '|'
+ call assert_equal(expected[i], expand('<cexpr>'), 'i == ' . i)
+ endfor
+
+ if executable('echo')
+ " Test expand(`...`) i.e. backticks command expansion.
+ " MS-Windows has a trailing space.
+ call assert_match('^abcde *$', expand('`echo abcde`'))
+ endif
+
+ " Test expand(`=...`) i.e. backticks expression expansion
+ call assert_equal('5', expand('`=2+3`'))
+
" clean up
bw!
endfunc
@@ -1536,12 +1557,12 @@ fun! Test_normal29_brace()
\ 'the ''{'' flag is in ''cpoptions'' then ''{'' in the first column is used as a',
\ 'paragraph boundary |posix|.',
\ '{',
- \ 'This is no paragaraph',
+ \ 'This is no paragraph',
\ 'unless the ''{'' is set',
\ 'in ''cpoptions''',
\ '}',
\ '.IP',
- \ 'The nroff macros IP seperates a paragraph',
+ \ 'The nroff macros IP separates a paragraph',
\ 'That means, it must be a ''.''',
\ 'followed by IP',
\ '.LPIt does not matter, if afterwards some',
@@ -1556,7 +1577,7 @@ fun! Test_normal29_brace()
1
norm! 0d2}
call assert_equal(['.IP',
- \ 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''', 'followed by IP',
+ \ 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''', 'followed by IP',
\ '.LPIt does not matter, if afterwards some', 'more characters follow.', '.SHAlso section boundaries from the nroff',
\ 'macros terminate a paragraph. That means', 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
norm! 0d}
@@ -1576,21 +1597,21 @@ fun! Test_normal29_brace()
" set cpo+={
" 1
" norm! 0d2}
- " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
- " \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''',
+ " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
+ " \ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',
" \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',
" \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',
" \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
" $
" norm! d}
- " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
- " \ '.IP', 'The nroff macros IP seperates a paragraph', 'That means, it must be a ''.''',
+ " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}',
+ " \ '.IP', 'The nroff macros IP separates a paragraph', 'That means, it must be a ''.''',
" \ 'followed by IP', '.LPIt does not matter, if afterwards some', 'more characters follow.',
" \ '.SHAlso section boundaries from the nroff', 'macros terminate a paragraph. That means',
" \ 'a character like this:', '.NH', 'End of text here', ''], getline(1,'$'))
" norm! gg}
" norm! d5}
- " call assert_equal(['{', 'This is no paragaraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$'))
+ " call assert_equal(['{', 'This is no paragraph', 'unless the ''{'' is set', 'in ''cpoptions''', '}', ''], getline(1,'$'))
" clean up
set cpo-={
@@ -1809,11 +1830,6 @@ fun! Test_normal33_g_cmd2()
call assert_equal(15, col('.'))
call assert_equal('l', getreg(0))
- " Test for g Ctrl-G
- set ff=unix
- let a=execute(":norm! g\<c-g>")
- call assert_match('Col 15 of 43; Line 2 of 2; Word 2 of 2; Byte 16 of 45', a)
-
" Test for gI
norm! gIfoo
call assert_equal(['', 'fooabcdefghijk lmno0123456789AMNOPQRSTUVWXYZ'], getline(1,'$'))
@@ -1832,6 +1848,81 @@ fun! Test_normal33_g_cmd2()
bw!
endfunc
+func! Test_g_ctrl_g()
+ new
+
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\n--No lines in buffer--", a)
+
+ call setline(1, ['first line', 'second line'])
+
+ " Test g CTRL-g with dos, mac and unix file type.
+ norm! gojll
+ set ff=dos
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\nCol 3 of 11; Line 2 of 2; Word 3 of 4; Byte 15 of 25", a)
+
+ set ff=mac
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\nCol 3 of 11; Line 2 of 2; Word 3 of 4; Byte 14 of 23", a)
+
+ set ff=unix
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\nCol 3 of 11; Line 2 of 2; Word 3 of 4; Byte 14 of 23", a)
+
+ " Test g CTRL-g in visual mode (v)
+ let a = execute(":norm! gojllvlg\<c-g>")
+ call assert_equal("\nSelected 1 of 2 Lines; 1 of 4 Words; 2 of 23 Bytes", a)
+
+ " Test g CTRL-g in visual mode (CTRL-V) with end col > start col
+ let a = execute(":norm! \<Esc>gojll\<C-V>kllg\<c-g>")
+ call assert_equal("\nSelected 3 Cols; 2 of 2 Lines; 2 of 4 Words; 6 of 23 Bytes", a)
+
+ " Test g_CTRL-g in visual mode (CTRL-V) with end col < start col
+ let a = execute(":norm! \<Esc>goll\<C-V>jhhg\<c-g>")
+ call assert_equal("\nSelected 3 Cols; 2 of 2 Lines; 2 of 4 Words; 6 of 23 Bytes", a)
+
+ " Test g CTRL-g in visual mode (CTRL-V) with end_vcol being MAXCOL
+ let a = execute(":norm! \<Esc>gojll\<C-V>k$g\<c-g>")
+ call assert_equal("\nSelected 2 of 2 Lines; 4 of 4 Words; 17 of 23 Bytes", a)
+
+ " There should be one byte less with noeol
+ set bin noeol
+ let a = execute(":norm! \<Esc>gog\<c-g>")
+ call assert_equal("\nCol 1 of 10; Line 1 of 2; Word 1 of 4; Char 1 of 23; Byte 1 of 22", a)
+ set bin & eol&
+
+ if has('multi_byte')
+ call setline(1, ['Français', '日本語'])
+
+ let a = execute(":norm! \<Esc>gojlg\<c-g>")
+ call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20", a)
+
+ let a = execute(":norm! \<Esc>gojvlg\<c-g>")
+ call assert_equal("\nSelected 1 of 2 Lines; 1 of 2 Words; 2 of 13 Chars; 6 of 20 Bytes", a)
+
+ let a = execute(":norm! \<Esc>goll\<c-v>jlg\<c-g>")
+ call assert_equal("\nSelected 4 Cols; 2 of 2 Lines; 2 of 2 Words; 6 of 13 Chars; 11 of 20 Bytes", a)
+
+ set fenc=utf8 bomb
+ let a = execute(":norm! \<Esc>gojlg\<c-g>")
+ call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+3 for BOM)", a)
+
+ set fenc=utf16 bomb
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+2 for BOM)", a)
+
+ set fenc=utf32 bomb
+ let a = execute(":norm! g\<c-g>")
+ call assert_equal("\nCol 4-3 of 9-6; Line 2 of 2; Word 2 of 2; Char 11 of 13; Byte 16 of 20(+4 for BOM)", a)
+
+ set fenc& bomb&
+ endif
+
+ set ff&
+ bwipe!
+endfunc
+
fun! Test_normal34_g_cmd3()
if !has("multi_byte")
return
@@ -2180,10 +2271,11 @@ func! Test_normal44_textobjects2()
endfunc
func! Test_normal45_drop()
- if !has("dnd")
+ if !has('dnd')
return
endif
- " basic test for :drop command
+
+ " basic test for drag-n-drop
" unfortunately, without a gui, we can't really test much here,
" so simply test that ~p fails (which uses the drop register)
new
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index f0aec42ae1..62d40f71af 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -338,4 +338,20 @@ func Test_copy_winopt()
bnext
call assert_equal(4,&numberwidth)
bw!
+
+ set hidden&
+endfunc
+
+func Test_shortmess_F()
+ new
+ call assert_match('\[No Name\]', execute('file'))
+ set shortmess+=F
+ call assert_match('\[No Name\]', execute('file'))
+ call assert_match('^\s*$', execute('file foo'))
+ call assert_match('foo', execute('file'))
+ set shortmess-=F
+ call assert_match('bar', execute('file bar'))
+ call assert_match('bar', execute('file'))
+ set shortmess&
+ bwipe
endfunc
diff --git a/src/nvim/testdir/test_preview.vim b/src/nvim/testdir/test_preview.vim
new file mode 100644
index 0000000000..91923fb1e9
--- /dev/null
+++ b/src/nvim/testdir/test_preview.vim
@@ -0,0 +1,13 @@
+" Tests for the preview window
+
+func Test_Psearch()
+ " this used to cause ml_get errors
+ help
+ let wincount = winnr('$')
+ 0f
+ ps.
+ call assert_equal(wincount + 1, winnr('$'))
+ pclose
+ call assert_equal(wincount, winnr('$'))
+ bwipe
+endfunc
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 7b77402115..638c6802d4 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -264,3 +264,27 @@ func Test_default_term()
call assert_match('nvim', out)
let $TERM = save_term
endfunc
+
+func Test_zzz_startinsert()
+ " Test :startinsert
+ call writefile(['123456'], 'Xtestout')
+ let after = [
+ \ ':startinsert',
+ \ 'call feedkeys("foobar\<c-o>:wq\<cr>","t")'
+ \ ]
+ if RunVim([], after, 'Xtestout')
+ let lines = readfile('Xtestout')
+ call assert_equal(['foobar123456'], lines)
+ endif
+ " Test :startinsert!
+ call writefile(['123456'], 'Xtestout')
+ let after = [
+ \ ':startinsert!',
+ \ 'call feedkeys("foobar\<c-o>:wq\<cr>","t")'
+ \ ]
+ if RunVim([], after, 'Xtestout')
+ let lines = readfile('Xtestout')
+ call assert_equal(['123456foobar'], lines)
+ endif
+ call delete('Xtestout')
+endfunc
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index a2eec2cc11..add9b3d7cf 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -42,40 +42,38 @@ function Test_tabpage()
call assert_true(t:val_num == 100 && t:val_str == 'SetTabVar test' && t:val_list == ['red', 'blue', 'green'])
tabclose
- if has('nvim') || has('gui') || has('clientserver')
- " Test for ":tab drop exist-file" to keep current window.
- sp test1
- tab drop test1
- call assert_true(tabpagenr('$') == 1 && winnr('$') == 2 && winnr() == 1)
- close
- "
- "
- " Test for ":tab drop new-file" to keep current window of tabpage 1.
- split
- tab drop newfile
- call assert_true(tabpagenr('$') == 2 && tabpagewinnr(1, '$') == 2 && tabpagewinnr(1) == 1)
- tabclose
- q
- "
- "
- " Test for ":tab drop multi-opend-file" to keep current tabpage and window.
- new test1
- tabnew
- new test1
- tab drop test1
- call assert_true(tabpagenr() == 2 && tabpagewinnr(2, '$') == 2 && tabpagewinnr(2) == 1)
- tabclose
- q
- "
- "
- " Test for ":tab drop vertical-split-window" to jump test1 buffer
- tabedit test1
- vnew
- tabfirst
- tab drop test1
- call assert_equal([2, 2, 2, 2], [tabpagenr('$'), tabpagenr(), tabpagewinnr(2, '$'), tabpagewinnr(2)])
- 1tabonly
- endif
+ " Test for ":tab drop exist-file" to keep current window.
+ sp test1
+ tab drop test1
+ call assert_true(tabpagenr('$') == 1 && winnr('$') == 2 && winnr() == 1)
+ close
+ "
+ "
+ " Test for ":tab drop new-file" to keep current window of tabpage 1.
+ split
+ tab drop newfile
+ call assert_true(tabpagenr('$') == 2 && tabpagewinnr(1, '$') == 2 && tabpagewinnr(1) == 1)
+ tabclose
+ q
+ "
+ "
+ " Test for ":tab drop multi-opend-file" to keep current tabpage and window.
+ new test1
+ tabnew
+ new test1
+ tab drop test1
+ call assert_true(tabpagenr() == 2 && tabpagewinnr(2, '$') == 2 && tabpagewinnr(2) == 1)
+ tabclose
+ q
+ "
+ "
+ " Test for ":tab drop vertical-split-window" to jump test1 buffer
+ tabedit test1
+ vnew
+ tabfirst
+ tab drop test1
+ call assert_equal([2, 2, 2, 2], [tabpagenr('$'), tabpagenr(), tabpagewinnr(2, '$'), tabpagewinnr(2)])
+ 1tabonly
"
"
for i in range(9) | tabnew | endfor
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index 684f197f5f..6a2f5044cc 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -121,6 +121,23 @@ func Test_string_html_objects()
enew!
endfunc
+func Test_empty_html_tag()
+ new
+ call setline(1, '<div></div>')
+ normal 0citxxx
+ call assert_equal('<div>xxx</div>', getline(1))
+
+ call setline(1, '<div></div>')
+ normal 0f<cityyy
+ call assert_equal('<div>yyy</div>', getline(1))
+
+ call setline(1, '<div></div>')
+ normal 0f<vitsaaa
+ call assert_equal('aaa', getline(1))
+
+ bwipe!
+endfunc
+
" Tests for match() and matchstr()
func Test_match()
call assert_equal("b", matchstr("abcd", ".", 0, 2))
@@ -152,3 +169,91 @@ func Test_match()
call assert_equal(3 , match('abc', '\zs', 3, 1))
call assert_equal(-1, match('abc', '\zs', 4, 1))
endfunc
+
+" This was causing an illegal memory access
+func Test_inner_tag()
+ new
+ norm ixxx
+ call feedkeys("v", 'xt')
+ insert
+x
+x
+.
+ norm it
+ q!
+endfunc
+
+func Test_sentence()
+ enew!
+ call setline(1, 'A sentence. A sentence? A sentence!')
+
+ normal yis
+ call assert_equal('A sentence.', @")
+ normal yas
+ call assert_equal('A sentence. ', @")
+
+ normal )
+
+ normal yis
+ call assert_equal('A sentence?', @")
+ normal yas
+ call assert_equal('A sentence? ', @")
+
+ normal )
+
+ normal yis
+ call assert_equal('A sentence!', @")
+ normal yas
+ call assert_equal(' A sentence!', @")
+
+ normal 0
+ normal 2yis
+ call assert_equal('A sentence. ', @")
+ normal 3yis
+ call assert_equal('A sentence. A sentence?', @")
+ normal 2yas
+ call assert_equal('A sentence. A sentence? ', @")
+
+ %delete _
+endfunc
+
+func Test_sentence_with_quotes()
+ enew!
+ call setline(1, 'A "sentence." A sentence.')
+
+ normal yis
+ call assert_equal('A "sentence."', @")
+ normal yas
+ call assert_equal('A "sentence." ', @")
+
+ normal )
+
+ normal yis
+ call assert_equal('A sentence.', @")
+ normal yas
+ call assert_equal(' A sentence.', @")
+
+ %delete _
+endfunc
+
+func! Test_sentence_with_cursor_on_delimiter()
+ enew!
+ call setline(1, "A '([sentence.])' A sentence.")
+
+ normal! 15|yis
+ call assert_equal("A '([sentence.])'", @")
+ normal! 15|yas
+ call assert_equal("A '([sentence.])' ", @")
+
+ normal! 16|yis
+ call assert_equal("A '([sentence.])'", @")
+ normal! 16|yas
+ call assert_equal("A '([sentence.])' ", @")
+
+ normal! 17|yis
+ call assert_equal("A '([sentence.])'", @")
+ normal! 17|yas
+ call assert_equal("A '([sentence.])' ", @")
+
+ %delete _
+endfunc
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index f31499607b..83ede1dc37 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -390,3 +390,47 @@ funct Test_undofile()
set undodir&
endfunc
+
+func Test_undo_0()
+ new
+ set ul=100
+ normal i1
+ undo
+ normal i2
+ undo
+ normal i3
+
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('3', getline(1))
+ call assert_equal(3, d.seq_cur)
+
+ undo 2
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('2', getline(1))
+ call assert_equal(2, d.seq_cur)
+
+ undo 1
+ undo 0
+ let d = undotree()
+ call assert_equal('', getline(1))
+ call assert_equal(0, d.seq_cur)
+
+ redo
+ let d = undotree()
+ call assert_equal('1', getline(1))
+ call assert_equal(1, d.seq_cur)
+
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim
index ed64dd79b7..e4618610cd 100644
--- a/src/nvim/testdir/test_winbuf_close.vim
+++ b/src/nvim/testdir/test_winbuf_close.vim
@@ -122,3 +122,39 @@ func Test_winbuf_close()
call delete('Xtest2')
call delete('Xtest3')
endfunc
+
+" Test that ":close" will respect 'winfixheight' when possible.
+func Test_winfixheight_on_close()
+ set nosplitbelow nosplitright
+
+ split | split | vsplit
+
+ $wincmd w
+ setlocal winfixheight
+ let l:height = winheight(0)
+
+ 3close
+
+ call assert_equal(l:height, winheight(0))
+
+ %bwipeout!
+ setlocal nowinfixheight splitbelow& splitright&
+endfunc
+
+" Test that ":close" will respect 'winfixwidth' when possible.
+func Test_winfixwidth_on_close()
+ set nosplitbelow nosplitright
+
+ vsplit | vsplit | split
+
+ $wincmd w
+ setlocal winfixwidth
+ let l:width = winwidth(0)
+
+ 3close
+
+ call assert_equal(l:width, winwidth(0))
+
+ %bwipeout!
+ setlocal nowinfixwidth splitbelow& splitright&
+endfunction
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index ad60c8d3c7..b3ab6957dc 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -374,6 +374,19 @@ func Test_equalalways_on_close()
set equalalways&
endfunc
+func Test_win_screenpos()
+ call assert_equal(1, winnr('$'))
+ split
+ vsplit
+ 10wincmd _
+ 30wincmd |
+ call assert_equal([1, 1], win_screenpos(1))
+ call assert_equal([1, 32], win_screenpos(2))
+ call assert_equal([12, 1], win_screenpos(3))
+ call assert_equal([0, 0], win_screenpos(4))
+ only
+endfunc
+
func Test_window_jump_tag()
help
/iccf
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index f4eb50b3b5..e15b9ec796 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -122,7 +122,7 @@ static long u_newcount, u_oldcount;
* When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
* the action that "u" should do.
*/
-static int undo_undoes = FALSE;
+static bool undo_undoes = false;
static int lastmark = 0;
@@ -591,7 +591,7 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
uep->ue_next = curbuf->b_u_newhead->uh_entry;
curbuf->b_u_newhead->uh_entry = uep;
curbuf->b_u_synced = false;
- undo_undoes = FALSE;
+ undo_undoes = false;
#ifdef U_DEBUG
u_check(FALSE);
@@ -1675,10 +1675,11 @@ void u_undo(int count)
count = 1;
}
- if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
- undo_undoes = TRUE;
- else
+ if (vim_strchr(p_cpo, CPO_UNDO) == NULL) {
+ undo_undoes = true;
+ } else {
undo_undoes = !undo_undoes;
+ }
u_doit(count, false, true);
}
@@ -1804,31 +1805,29 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
u_undo_end(undo_undoes, false, quiet);
}
-/*
- * Undo or redo over the timeline.
- * When "step" is negative go back in time, otherwise goes forward in time.
- * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
- * seconds.
- * When "file" is TRUE use "step" as a number of file writes.
- * When "absolute" is TRUE use "step" as the sequence number to jump to.
- * "sec" must be FALSE then.
- */
-void undo_time(long step, int sec, int file, int absolute)
+// Undo or redo over the timeline.
+// When "step" is negative go back in time, otherwise goes forward in time.
+// When "sec" is false make "step" steps, when "sec" is true use "step" as
+// seconds.
+// When "file" is true use "step" as a number of file writes.
+// When "absolute" is true use "step" as the sequence number to jump to.
+// "sec" must be false then.
+void undo_time(long step, bool sec, bool file, bool absolute)
{
long target;
long closest;
long closest_start;
long closest_seq = 0;
long val;
- u_header_T *uhp;
+ u_header_T *uhp = NULL;
u_header_T *last;
int mark;
int nomark;
int round;
- int dosec = sec;
- int dofile = file;
- int above = FALSE;
- int did_undo = TRUE;
+ bool dosec = sec;
+ bool dofile = file;
+ bool above = false;
+ bool did_undo = true;
/* First make sure the current undoable change is synced. */
if (curbuf->b_u_synced == false)
@@ -1842,13 +1841,7 @@ void undo_time(long step, int sec, int file, int absolute)
/* "target" is the node below which we want to be.
* Init "closest" to a value we can't reach. */
if (absolute) {
- if (step == 0) {
- // target 0 does not exist, got to 1 and above it.
- target = 1;
- above = true;
- } else {
- target = step;
- }
+ target = step;
closest = -1;
} else {
if (dosec) {
@@ -1873,7 +1866,7 @@ void undo_time(long step, int sec, int file, int absolute)
if (target <= 0)
/* Go to before first write: before the oldest change. Use
* the sequence number for that. */
- dofile = FALSE;
+ dofile = false;
} else {
/* Moving forward to a newer write. */
target = curbuf->b_u_save_nr_cur + step;
@@ -1881,7 +1874,7 @@ void undo_time(long step, int sec, int file, int absolute)
/* Go to after last write: after the latest change. Use
* the sequence number for that. */
target = curbuf->b_u_seq_last + 1;
- dofile = FALSE;
+ dofile = false;
}
}
} else
@@ -1906,6 +1899,11 @@ void undo_time(long step, int sec, int file, int absolute)
closest_start = closest;
closest_seq = curbuf->b_u_seq_cur;
+ // When "target" is 0; Back to origin.
+ if (target == 0) {
+ goto found;
+ }
+
/*
* May do this twice:
* 1. Search for "target", update "closest" to the best match found.
@@ -2015,17 +2013,17 @@ void undo_time(long step, int sec, int file, int absolute)
}
target = closest_seq;
- dosec = FALSE;
- dofile = FALSE;
- if (step < 0)
- above = TRUE; /* stop above the header */
+ dosec = false;
+ dofile = false;
+ if (step < 0) {
+ above = true; // stop above the header
+ }
}
- /* If we found it: Follow the path to go to where we want to be. */
- if (uhp != NULL) {
- /*
- * First go up the tree as much as needed.
- */
+found:
+ // If we found it: Follow the path to go to where we want to be.
+ if (uhp != NULL || target == 0) {
+ // First go up the tree as much as needed.
while (!got_int) {
/* Do the change warning now, for the same reason as above. */
change_warning(0);
@@ -2035,83 +2033,97 @@ void undo_time(long step, int sec, int file, int absolute)
uhp = curbuf->b_u_newhead;
else
uhp = uhp->uh_next.ptr;
- if (uhp == NULL || uhp->uh_walk != mark
- || (uhp->uh_seq == target && !above))
+ if (uhp == NULL
+ || (target > 0 && uhp->uh_walk != mark)
+ || (uhp->uh_seq == target && !above)) {
break;
+ }
curbuf->b_u_curhead = uhp;
u_undoredo(true, true);
- uhp->uh_walk = nomark; // don't go back down here
+ if (target > 0) {
+ uhp->uh_walk = nomark; // don't go back down here
+ }
}
- /*
- * And now go down the tree (redo), branching off where needed.
- */
- while (!got_int) {
- /* Do the change warning now, for the same reason as above. */
- change_warning(0);
+ // When back to origin, redo is not needed.
+ if (target > 0) {
+ // And now go down the tree (redo), branching off where needed.
+ while (!got_int) {
+ // Do the change warning now, for the same reason as above.
+ change_warning(0);
- uhp = curbuf->b_u_curhead;
- if (uhp == NULL)
- break;
-
- /* Go back to the first branch with a mark. */
- while (uhp->uh_alt_prev.ptr != NULL
- && uhp->uh_alt_prev.ptr->uh_walk == mark)
- uhp = uhp->uh_alt_prev.ptr;
+ uhp = curbuf->b_u_curhead;
+ if (uhp == NULL) {
+ break;
+ }
- /* Find the last branch with a mark, that's the one. */
- last = uhp;
- while (last->uh_alt_next.ptr != NULL
- && last->uh_alt_next.ptr->uh_walk == mark)
- last = last->uh_alt_next.ptr;
- if (last != uhp) {
- /* Make the used branch the first entry in the list of
- * alternatives to make "u" and CTRL-R take this branch. */
- while (uhp->uh_alt_prev.ptr != NULL)
+ // Go back to the first branch with a mark.
+ while (uhp->uh_alt_prev.ptr != NULL
+ && uhp->uh_alt_prev.ptr->uh_walk == mark) {
uhp = uhp->uh_alt_prev.ptr;
- if (last->uh_alt_next.ptr != NULL)
- last->uh_alt_next.ptr->uh_alt_prev.ptr =
- last->uh_alt_prev.ptr;
- last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
- last->uh_alt_prev.ptr = NULL;
- last->uh_alt_next.ptr = uhp;
- uhp->uh_alt_prev.ptr = last;
-
- if (curbuf->b_u_oldhead == uhp)
- curbuf->b_u_oldhead = last;
- uhp = last;
- if (uhp->uh_next.ptr != NULL)
- uhp->uh_next.ptr->uh_prev.ptr = uhp;
- }
- curbuf->b_u_curhead = uhp;
+ }
- if (uhp->uh_walk != mark)
- break; /* must have reached the target */
+ // Find the last branch with a mark, that's the one.
+ last = uhp;
+ while (last->uh_alt_next.ptr != NULL
+ && last->uh_alt_next.ptr->uh_walk == mark) {
+ last = last->uh_alt_next.ptr;
+ }
+ if (last != uhp) {
+ // Make the used branch the first entry in the list of
+ // alternatives to make "u" and CTRL-R take this branch.
+ while (uhp->uh_alt_prev.ptr != NULL) {
+ uhp = uhp->uh_alt_prev.ptr;
+ }
+ if (last->uh_alt_next.ptr != NULL) {
+ last->uh_alt_next.ptr->uh_alt_prev.ptr = last->uh_alt_prev.ptr;
+ }
+ last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr;
+ last->uh_alt_prev.ptr = NULL;
+ last->uh_alt_next.ptr = uhp;
+ uhp->uh_alt_prev.ptr = last;
- /* Stop when going backwards in time and didn't find the exact
- * header we were looking for. */
- if (uhp->uh_seq == target && above) {
- curbuf->b_u_seq_cur = target - 1;
- break;
- }
+ if (curbuf->b_u_oldhead == uhp) {
+ curbuf->b_u_oldhead = last;
+ }
+ uhp = last;
+ if (uhp->uh_next.ptr != NULL) {
+ uhp->uh_next.ptr->uh_prev.ptr = uhp;
+ }
+ }
+ curbuf->b_u_curhead = uhp;
- u_undoredo(false, true);
+ if (uhp->uh_walk != mark) {
+ break; // must have reached the target
+ }
- /* Advance "curhead" to below the header we last used. If it
- * becomes NULL then we need to set "newhead" to this leaf. */
- if (uhp->uh_prev.ptr == NULL)
- curbuf->b_u_newhead = uhp;
- curbuf->b_u_curhead = uhp->uh_prev.ptr;
- did_undo = FALSE;
+ // Stop when going backwards in time and didn't find the exact
+ // header we were looking for.
+ if (uhp->uh_seq == target && above) {
+ curbuf->b_u_seq_cur = target - 1;
+ break;
+ }
- if (uhp->uh_seq == target) /* found it! */
- break;
+ u_undoredo(false, true);
- uhp = uhp->uh_prev.ptr;
- if (uhp == NULL || uhp->uh_walk != mark) {
- // Need to redo more but can't find it...
- internal_error("undo_time()");
- break;
+ // Advance "curhead" to below the header we last used. If it
+ // becomes NULL then we need to set "newhead" to this leaf.
+ if (uhp->uh_prev.ptr == NULL) {
+ curbuf->b_u_newhead = uhp;
+ }
+ curbuf->b_u_curhead = uhp->uh_prev.ptr;
+ did_undo = false;
+
+ if (uhp->uh_seq == target) { // found it!
+ break;
+ }
+
+ uhp = uhp->uh_prev.ptr;
+ if (uhp == NULL || uhp->uh_walk != mark) {
+ // Need to redo more but can't find it...
+ internal_error("undo_time()");
+ break;
+ }
}
}
}
@@ -2375,8 +2387,8 @@ static void u_undoredo(int undo, bool do_buf_event)
/// Otherwise, report the number of changes (this may be incorrect
/// in some cases, but it's better than nothing).
static void u_undo_end(
- int did_undo, ///< just did an undo
- int absolute, ///< used ":undo N"
+ bool did_undo, ///< just did an undo
+ bool absolute, ///< used ":undo N"
bool quiet)
{
char *msgstr;
@@ -2416,13 +2428,15 @@ static void u_undo_end(
/* For ":undo N" we prefer a "after #N" message. */
if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) {
uhp = curbuf->b_u_curhead->uh_next.ptr;
- did_undo = FALSE;
- } else if (did_undo)
+ did_undo = false;
+ } else if (did_undo) {
uhp = curbuf->b_u_curhead;
- else
+ } else {
uhp = curbuf->b_u_curhead->uh_next.ptr;
- } else
+ }
+ } else {
uhp = curbuf->b_u_newhead;
+ }
if (uhp == NULL)
*msgbuf = NUL;
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index bddf092789..767936ecee 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -155,6 +155,8 @@ enum {
EXPAND_USER_ADDR_TYPE,
EXPAND_PACKADD,
EXPAND_MESSAGES,
+ EXPAND_MAPCLEAR,
+ EXPAND_ARGLIST,
EXPAND_CHECKHEALTH,
};
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9515b88248..055564c1e0 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -565,6 +565,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
int before;
int minheight;
int wmh1;
+ bool did_set_fraction = false;
if (flags & WSP_TOP)
oldwin = firstwin;
@@ -729,6 +730,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
* 'winfixheight' window. Take them from a window above or below
* instead, if possible. */
if (oldwin->w_p_wfh) {
+ // Set w_fraction now so that the cursor keeps the same relative
+ // vertical position using the old height.
+ set_fraction(oldwin);
+ did_set_fraction = true;
+
win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
oldwin);
oldwin_height = oldwin->w_height;
@@ -843,8 +849,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
/* Set w_fraction now so that the cursor keeps the same relative
* vertical position. */
- if (oldwin->w_height > 0)
+ if (!did_set_fraction) {
set_fraction(oldwin);
+ }
wp->w_fraction = oldwin->w_fraction;
if (flags & WSP_VERT) {
@@ -2330,14 +2337,14 @@ winframe_remove (
return wp;
}
-/*
- * Find out which frame is going to get the freed up space when "win" is
- * closed.
- * if 'splitbelow'/'splitleft' the space goes to the window above/left.
- * if 'nosplitbelow'/'nosplitleft' the space goes to the window below/right.
- * This makes opening a window and closing it immediately keep the same window
- * layout.
- */
+// Return a pointer to the frame that will receive the empty screen space that
+// is left over after "win" is closed.
+//
+// If 'splitbelow' or 'splitright' is set, the space goes above or to the left
+// by default. Otherwise, the free space goes below or to the right. The
+// result is that opening a window and then immediately closing it will
+// preserve the initial window layout. The 'wfh' and 'wfw' settings are
+// respected when possible.
static frame_T *
win_altframe (
win_T *win,
@@ -2345,20 +2352,40 @@ win_altframe (
)
{
frame_T *frp;
- int b;
- if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
- /* Last window in this tab page, will go to next tab page. */
+ if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) {
return alt_tabpage()->tp_curwin->w_frame;
+ }
frp = win->w_frame;
- if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW)
- b = p_spr;
- else
- b = p_sb;
- if ((!b && frp->fr_next != NULL) || frp->fr_prev == NULL)
+
+ if (frp->fr_prev == NULL) {
return frp->fr_next;
- return frp->fr_prev;
+ }
+ if (frp->fr_next == NULL) {
+ return frp->fr_prev;
+ }
+
+ frame_T *target_fr = frp->fr_next;
+ frame_T *other_fr = frp->fr_prev;
+ if (p_spr || p_sb) {
+ target_fr = frp->fr_prev;
+ other_fr = frp->fr_next;
+ }
+
+ // If 'wfh' or 'wfw' is set for the target and not for the alternate
+ // window, reverse the selection.
+ if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW) {
+ if (frame_fixed_width(target_fr) && !frame_fixed_width(other_fr)) {
+ target_fr = other_fr;
+ }
+ } else {
+ if (frame_fixed_height(target_fr) && !frame_fixed_height(other_fr)) {
+ target_fr = other_fr;
+ }
+ }
+
+ return target_fr;
}
/*
@@ -4791,10 +4818,13 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
#define FRACTION_MULT 16384L
// Set wp->w_fraction for the current w_wrow and w_height.
+// Has no effect when the window is less than two lines.
void set_fraction(win_T *wp)
{
- wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2)
+ if (wp->w_height > 1) {
+ wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2)
/ (long)wp->w_height;
+ }
}
/*
@@ -5110,6 +5140,8 @@ file_name_in_line (
{
char_u *ptr;
size_t len;
+ bool in_type = true;
+ bool is_url = false;
/*
* search forward for what could be the start of a file name
@@ -5146,7 +5178,19 @@ file_name_in_line (
*/
len = 0;
while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
- || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))) {
+ || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))
+ || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL)) {
+ // After type:// we also include ?, & and = as valid characters, so that
+ // http://google.com?q=this&that=ok works.
+ if ((ptr[len] >= 'A' && ptr[len] <= 'Z')
+ || (ptr[len] >= 'a' && ptr[len] <= 'z')) {
+ if (in_type && path_is_url((char *)ptr + len + 1)) {
+ is_url = true;
+ }
+ } else {
+ in_type = false;
+ }
+
if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
// Skip over the "\" in "\ ".
++len;