aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c4
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/charset.c71
-rw-r--r--src/nvim/charset.h8
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/eval.c289
-rw-r--r--src/nvim/eval_defs.h10
-rw-r--r--src/nvim/event/process.c25
-rw-r--r--src/nvim/ex_cmds.c9
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c15
-rw-r--r--src/nvim/ex_docmd.c11
-rw-r--r--src/nvim/fileio.c20
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/macros.h5
-rw-r--r--src/nvim/main.c4
-rw-r--r--src/nvim/map.c5
-rw-r--r--src/nvim/mbyte.c80
-rw-r--r--src/nvim/memline.c7
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/mouse.c76
-rw-r--r--src/nvim/msgpack_rpc/server.c4
-rw-r--r--src/nvim/ops.c16
-rw-r--r--src/nvim/option.c11
-rw-r--r--src/nvim/os/fileio.c1
-rw-r--r--src/nvim/os/fs.c97
-rw-r--r--src/nvim/os/pty_process_unix.c1
-rw-r--r--src/nvim/po/ja.po2
-rw-r--r--src/nvim/regexp.c26
-rw-r--r--src/nvim/regexp_nfa.c23
-rw-r--r--src/nvim/screen.c10
-rw-r--r--src/nvim/syntax.c18
-rw-r--r--src/nvim/terminal.c19
-rw-r--r--src/nvim/testdir/Makefile6
-rw-r--r--src/nvim/testdir/runtest.vim10
-rw-r--r--src/nvim/testdir/test30.in230
-rw-r--r--src/nvim/testdir/test30.ok130
-rw-r--r--src/nvim/testdir/test_alot.vim6
-rw-r--r--src/nvim/testdir/test_assign.vim9
-rw-r--r--src/nvim/testdir/test_cmdline.vim120
-rw-r--r--src/nvim/testdir/test_popup.vim30
-rw-r--r--src/nvim/testdir/test_viml.vim21
-rw-r--r--src/nvim/testdir/test_visual.vim18
-rw-r--r--src/nvim/testdir/test_window_id.vim71
-rw-r--r--src/nvim/version.c620
-rw-r--r--src/nvim/window.c98
46 files changed, 1647 insertions, 609 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 438a85dd5d..c934d44e70 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4030,8 +4030,8 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
if (!buf->b_p_bin) {
char_u *rfname;
- /* If the file name is a shortcut file, use the file it links to. */
- rfname = mch_resolve_shortcut(*ffname);
+ // If the file name is a shortcut file, use the file it links to.
+ rfname = os_resolve_shortcut(*ffname);
if (rfname != NULL) {
xfree(*ffname);
*ffname = rfname;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index b515c4e1e4..46687f344c 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -937,8 +937,9 @@ struct matchitem {
*/
struct window_S {
uint64_t handle;
- buf_T *w_buffer; /* buffer we are a window into (used
- often, keep it the first item!) */
+ int w_id; ///< unique window ID
+ buf_T *w_buffer; ///< buffer we are a window into (used
+ ///< often, keep it the first item!)
synblock_T *w_s; /* for :ownsyntax */
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 5ae4416052..22ca0fb0cc 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -43,20 +43,29 @@ static bool chartab_initialized = false;
#define GET_CHARTAB(buf, c) \
((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f)))
-/// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword
+// Table used below, see init_chartab() for an explanation
+static char_u g_chartab[256];
+
+// Flags for g_chartab[].
+#define CT_CELL_MASK 0x07 ///< mask: nr of display cells (1, 2 or 4)
+#define CT_PRINT_CHAR 0x10 ///< flag: set for printable chars
+#define CT_ID_CHAR 0x20 ///< flag: set for ID chars
+#define CT_FNAME_CHAR 0x40 ///< flag: set for file name chars
+
+/// Fill g_chartab[]. Also fills curbuf->b_chartab[] with flags for keyword
/// characters for current buffer.
///
/// Depends on the option settings 'iskeyword', 'isident', 'isfname',
/// 'isprint' and 'encoding'.
///
-/// The index in chartab[] depends on 'encoding':
+/// The index in g_chartab[] depends on 'encoding':
/// - For non-multi-byte index with the byte (same as the character).
/// - For DBCS index with the first byte.
/// - For UTF-8 index with the character (when first byte is up to 0x80 it is
/// the same as the character, if the first byte is 0x80 and above it depends
/// on further bytes).
///
-/// The contents of chartab[]:
+/// The contents of g_chartab[]:
/// - The lower two bits, masked by CT_CELL_MASK, give the number of display
/// cells the character occupies (1 or 2). Not valid for UTF-8 above 0x80.
/// - CT_PRINT_CHAR bit is set when the character is printable (no need to
@@ -94,32 +103,32 @@ int buf_init_chartab(buf_T *buf, int global)
c = 0;
while (c < ' ') {
- chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
}
while (c <= '~') {
- chartab[c++] = 1 + CT_PRINT_CHAR;
+ g_chartab[c++] = 1 + CT_PRINT_CHAR;
}
if (p_altkeymap) {
while (c < YE) {
- chartab[c++] = 1 + CT_PRINT_CHAR;
+ g_chartab[c++] = 1 + CT_PRINT_CHAR;
}
}
while (c < 256) {
if (enc_utf8 && (c >= 0xa0)) {
// UTF-8: bytes 0xa0 - 0xff are printable (latin1)
- chartab[c++] = CT_PRINT_CHAR + 1;
+ g_chartab[c++] = CT_PRINT_CHAR + 1;
} else if ((enc_dbcs == DBCS_JPNU) && (c == 0x8e)) {
// euc-jp characters starting with 0x8e are single width
- chartab[c++] = CT_PRINT_CHAR + 1;
+ g_chartab[c++] = CT_PRINT_CHAR + 1;
} else if ((enc_dbcs != 0) && (MB_BYTE2LEN(c) == 2)) {
// other double-byte chars can be printable AND double-width
- chartab[c++] = CT_PRINT_CHAR + 2;
+ g_chartab[c++] = CT_PRINT_CHAR + 2;
} else {
// the rest is unprintable by default
- chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
}
}
@@ -128,7 +137,7 @@ int buf_init_chartab(buf_T *buf, int global)
if (((enc_dbcs != 0) && (MB_BYTE2LEN(c) > 1))
|| ((enc_dbcs == DBCS_JPNU) && (c == 0x8e))
|| (enc_utf8 && (c >= 0xa0))) {
- chartab[c] |= CT_FNAME_CHAR;
+ g_chartab[c] |= CT_FNAME_CHAR;
}
}
}
@@ -231,9 +240,9 @@ int buf_init_chartab(buf_T *buf, int global)
if (i == 0) {
// (re)set ID flag
if (tilde) {
- chartab[c] &= (uint8_t)~CT_ID_CHAR;
+ g_chartab[c] &= (uint8_t)~CT_ID_CHAR;
} else {
- chartab[c] |= CT_ID_CHAR;
+ g_chartab[c] |= CT_ID_CHAR;
}
} else if (i == 1) {
// (re)set printable
@@ -244,20 +253,20 @@ int buf_init_chartab(buf_T *buf, int global)
|| (p_altkeymap && (F_isalpha(c) || F_isdigit(c))))
&& !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) {
if (tilde) {
- chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK)
- + ((dy_flags & DY_UHEX) ? 4 : 2));
- chartab[c] &= (uint8_t)~CT_PRINT_CHAR;
+ g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
+ + ((dy_flags & DY_UHEX) ? 4 : 2));
+ g_chartab[c] &= (uint8_t)~CT_PRINT_CHAR;
} else {
- chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1);
- chartab[c] |= CT_PRINT_CHAR;
+ g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1);
+ g_chartab[c] |= CT_PRINT_CHAR;
}
}
} else if (i == 2) {
// (re)set fname flag
if (tilde) {
- chartab[c] &= (uint8_t)~CT_FNAME_CHAR;
+ g_chartab[c] &= (uint8_t)~CT_FNAME_CHAR;
} else {
- chartab[c] |= CT_FNAME_CHAR;
+ g_chartab[c] |= CT_FNAME_CHAR;
}
} else { // i == 3
// (re)set keyword flag
@@ -492,9 +501,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
return buf;
}
-// Catch 22: chartab[] can't be initialized before the options are
+// Catch 22: g_chartab[] can't be initialized before the options are
// initialized, and initializing options may cause transchar() to be called!
-// When chartab_initialized == false don't use chartab[].
+// When chartab_initialized == false don't use g_chartab[].
// Does NOT work for multi-byte characters, c must be <= 255.
// Also doesn't work for the first byte of a multi-byte, "c" must be a
// character!
@@ -633,7 +642,7 @@ int byte2cells(int b)
if (enc_utf8 && (b >= 0x80)) {
return 0;
}
- return chartab[b] & CT_CELL_MASK;
+ return g_chartab[b] & CT_CELL_MASK;
}
/// Return number of display cells occupied by character "c".
@@ -665,7 +674,7 @@ int char2cells(int c)
return 2;
}
}
- return chartab[c & 0xff] & CT_CELL_MASK;
+ return g_chartab[c & 0xff] & CT_CELL_MASK;
}
/// Return number of display cells occupied by character at "*p".
@@ -682,7 +691,7 @@ int ptr2cells(char_u *p)
}
// For DBCS we can tell the cell count from the first byte.
- return chartab[*p] & CT_CELL_MASK;
+ return g_chartab[*p] & CT_CELL_MASK;
}
/// Return the number of character cells string "s" will take on the screen,
@@ -806,7 +815,7 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
bool vim_isIDc(int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR);
+ return c > 0 && c < 0x100 && (g_chartab[c] & CT_ID_CHAR);
}
/// Check that "c" is a keyword character:
@@ -878,7 +887,7 @@ bool vim_iswordp_buf(char_u *p, buf_T *buf)
bool vim_isfilec(int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR));
+ return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_FNAME_CHAR));
}
/// Check that "c" is a valid file-name character or a wildcard character
@@ -906,7 +915,7 @@ bool vim_isprintc(int c)
if (enc_utf8 && (c >= 0x100)) {
return utf_printable(c);
}
- return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR));
+ return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_PRINT_CHAR));
}
/// Strict version of vim_isprintc(c), don't return true if "c" is the head
@@ -925,7 +934,7 @@ bool vim_isprintc_strict(int c)
if (enc_utf8 && (c >= 0x100)) {
return utf_printable(c);
}
- return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR));
+ return c >= 0x100 || (c > 0 && (g_chartab[c] & CT_PRINT_CHAR));
}
/// like chartabsize(), but also check for line breaks on the screen
@@ -1247,7 +1256,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
if (enc_utf8 && (c >= 0x80)) {
incr = utf_ptr2cells(ptr);
} else {
- incr = CHARSIZE(c);
+ incr = g_chartab[c] & CT_CELL_MASK;
}
// If a double-cell char doesn't fit at the end of a line
@@ -1261,7 +1270,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
head = 1;
}
} else {
- incr = CHARSIZE(c);
+ incr = g_chartab[c] & CT_CELL_MASK;
}
}
diff --git a/src/nvim/charset.h b/src/nvim/charset.h
index 995ad123ae..78d6f2a76c 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -1,14 +1,6 @@
#ifndef NVIM_CHARSET_H
#define NVIM_CHARSET_H
-/*
- * Flags for chartab[].
- */
-#define CT_CELL_MASK 0x07 /* mask: nr of display cells (1, 2 or 4) */
-#define CT_PRINT_CHAR 0x10 /* flag: set for printable chars */
-#define CT_ID_CHAR 0x20 /* flag: set for ID chars */
-#define CT_FNAME_CHAR 0x40 /* flag: set for file name chars */
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "charset.h.generated.h"
#endif
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 03ef41f849..98ec9ae280 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -2385,6 +2385,7 @@ void set_completion(colnr_T startcol, list_T *list)
} else {
ins_complete(Ctrl_N, false);
}
+ compl_enter_selects = compl_no_insert;
// Lazily show the popup menu, unless we got interrupted.
if (!compl_interrupted) {
@@ -3989,6 +3990,7 @@ static void ins_compl_insert(void)
dict_add_nr_str(dict, "info", 0L,
EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO]));
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
+ compl_curr_match = compl_shown_match;
}
/*
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 55fa974797..7839a7f645 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3297,6 +3297,26 @@ char_u *get_user_var_name(expand_T *xp, int idx)
}
+/// Return TRUE if "pat" matches "text".
+/// Does not use 'cpo' and always uses 'magic'.
+static int pattern_match(char_u *pat, char_u *text, int ic)
+{
+ int matches = 0;
+ regmatch_T regmatch;
+
+ // avoid 'l' flag in 'cpoptions'
+ char_u *save_cpo = p_cpo;
+ p_cpo = (char_u *)"";
+ regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ if (regmatch.regprog != NULL) {
+ regmatch.rm_ic = ic;
+ matches = vim_regexec_nl(&regmatch, text, (colnr_T)0);
+ vim_regfree(regmatch.regprog);
+ }
+ p_cpo = save_cpo;
+ return matches;
+}
+
/*
* types for expressions.
*/
@@ -3572,9 +3592,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
long n1, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
- regmatch_T regmatch;
int ic;
- char_u *save_cpo;
/*
* Get the first variable.
@@ -3783,19 +3801,10 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
case TYPE_MATCH:
case TYPE_NOMATCH:
- /* avoid 'l' flag in 'cpoptions' */
- save_cpo = p_cpo;
- p_cpo = (char_u *)"";
- regmatch.regprog = vim_regcomp(s2,
- RE_MAGIC + RE_STRING);
- regmatch.rm_ic = ic;
- if (regmatch.regprog != NULL) {
- n1 = vim_regexec_nl(&regmatch, s1, (colnr_T)0);
- vim_regfree(regmatch.regprog);
- if (type == TYPE_NOMATCH)
- n1 = !n1;
+ n1 = pattern_match(s2, s1, ic);
+ if (type == TYPE_NOMATCH) {
+ n1 = !n1;
}
- p_cpo = save_cpo;
break;
case TYPE_UNKNOWN: break; /* avoid gcc warning */
@@ -6697,6 +6706,9 @@ static struct fst {
{ "assert_exception", 1, 2, f_assert_exception },
{ "assert_fails", 1, 2, f_assert_fails },
{ "assert_false", 1, 2, f_assert_false },
+ { "assert_match", 2, 3, f_assert_match },
+ { "assert_notequal", 2, 3, f_assert_notequal },
+ { "assert_notmatch", 2, 3, f_assert_notmatch },
{ "assert_true", 1, 2, f_assert_true },
{ "atan", 1, 1, f_atan },
{ "atan2", 2, 2, f_atan2 },
@@ -6715,7 +6727,6 @@ static struct fst {
{ "byteidx", 2, 2, f_byteidx },
{ "byteidxcomp", 2, 2, f_byteidxcomp },
{ "call", 2, 3, f_call },
- { "capture", 1, 1, f_capture },
{ "ceil", 1, 1, f_ceil },
{ "changenr", 0, 0, f_changenr },
{ "char2nr", 1, 2, f_char2nr },
@@ -6744,6 +6755,7 @@ static struct fst {
{ "eval", 1, 1, f_eval },
{ "eventhandler", 0, 0, f_eventhandler },
{ "executable", 1, 1, f_executable },
+ { "execute", 1, 1, f_execute },
{ "exepath", 1, 1, f_exepath },
{ "exists", 1, 1, f_exists },
{ "exp", 1, 1, f_exp },
@@ -6779,6 +6791,7 @@ static struct fst {
{ "getcmdpos", 0, 0, f_getcmdpos },
{ "getcmdtype", 0, 0, f_getcmdtype },
{ "getcmdwintype", 0, 0, f_getcmdwintype },
+ { "getcompletion", 2, 2, f_getcompletion },
{ "getcurpos", 0, 0, f_getcurpos },
{ "getcwd", 0, 2, f_getcwd },
{ "getfontname", 0, 1, f_getfontname },
@@ -6974,6 +6987,10 @@ static struct fst {
{ "virtcol", 1, 1, f_virtcol },
{ "visualmode", 0, 1, f_visualmode },
{ "wildmenumode", 0, 0, f_wildmenumode },
+ { "win_getid", 0, 2, f_win_getid },
+ { "win_gotoid", 1, 1, f_win_gotoid },
+ { "win_id2tabwin", 1, 1, f_win_id2tabwin },
+ { "win_id2win", 1, 1, f_win_id2win },
{ "winbufnr", 1, 1, f_winbufnr },
{ "wincol", 0, 0, f_wincol },
{ "winheight", 1, 1, f_winheight },
@@ -7606,7 +7623,7 @@ static void prepare_assert_error(garray_T *gap)
// Fill "gap" with information about an assert error.
static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
char_u *exp_str, typval_T *exp_tv,
- typval_T *got_tv)
+ typval_T *got_tv, assert_type_T atype)
{
char_u *tofree;
@@ -7615,7 +7632,11 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
ga_concat(gap, tofree);
xfree(tofree);
} else {
- ga_concat(gap, (char_u *)"Expected ");
+ if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
+ ga_concat(gap, (char_u *)"Pattern ");
+ } else {
+ ga_concat(gap, (char_u *)"Expected ");
+ }
if (exp_str == NULL) {
tofree = (char_u *) encode_tv2string(exp_tv, NULL);
ga_concat(gap, tofree);
@@ -7623,8 +7644,16 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
} else {
ga_concat(gap, exp_str);
}
- tofree = (char_u *) encode_tv2string(got_tv, NULL);
- ga_concat(gap, (char_u *)" but got ");
+ tofree = (char_u *)encode_tv2string(got_tv, NULL);
+ if (atype == ASSERT_MATCH) {
+ ga_concat(gap, (char_u *)" does not match ");
+ } else if (atype == ASSERT_NOTMATCH) {
+ ga_concat(gap, (char_u *)" does match ");
+ } else if (atype == ASSERT_NOTEQUAL) {
+ ga_concat(gap, (char_u *)" differs from ");
+ } else {
+ ga_concat(gap, (char_u *)" but got ");
+ }
ga_concat(gap, tofree);
xfree(tofree);
}
@@ -7643,20 +7672,32 @@ static void assert_error(garray_T *gap)
gap->ga_data, gap->ga_len);
}
-// "assert_equal(expected, actual[, msg])" function
-static void f_assert_equal(typval_T *argvars, typval_T *rettv)
+static void assert_equal_common(typval_T *argvars, assert_type_T atype)
{
garray_T ga;
- if (!tv_equal(&argvars[0], &argvars[1], false, false)) {
+ if (tv_equal(&argvars[0], &argvars[1], false, false)
+ != (atype == ASSERT_EQUAL)) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL,
- &argvars[0], &argvars[1]);
+ &argvars[0], &argvars[1], atype);
assert_error(&ga);
ga_clear(&ga);
}
}
+// "assert_equal(expected, actual[, msg])" function
+static void f_assert_equal(typval_T *argvars, typval_T *rettv)
+{
+ assert_equal_common(argvars, ASSERT_EQUAL);
+}
+
+// "assert_notequal(expected, actual[, msg])" function
+static void f_assert_notequal(typval_T *argvars, typval_T *rettv)
+{
+ assert_equal_common(argvars, ASSERT_NOTEQUAL);
+}
+
/// "assert_exception(string[, msg])" function
static void f_assert_exception(typval_T *argvars, typval_T *rettv)
{
@@ -7672,7 +7713,7 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv)
&& strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
- &vimvars[VV_EXCEPTION].vv_tv);
+ &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
}
@@ -7702,7 +7743,7 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv)
|| strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
- &vimvars[VV_ERRMSG].vv_tv);
+ &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
}
@@ -7732,7 +7773,7 @@ static void assert_bool(typval_T *argvars, bool is_true)
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1],
(char_u *)(is_true ? "True" : "False"),
- NULL, &argvars[0]);
+ NULL, &argvars[0], ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
}
@@ -7744,6 +7785,36 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv)
assert_bool(argvars, false);
}
+static void assert_match_common(typval_T *argvars, assert_type_T atype)
+{
+ char_u buf1[NUMBUFLEN];
+ char_u buf2[NUMBUFLEN];
+ char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1);
+ char_u *text = get_tv_string_buf_chk(&argvars[1], buf2);
+
+ if (pat == NULL || text == NULL) {
+ EMSG(_(e_invarg));
+ } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
+ garray_T ga;
+ prepare_assert_error(&ga);
+ fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
+ assert_error(&ga);
+ ga_clear(&ga);
+ }
+}
+
+/// "assert_match(pattern, actual[, msg])" function
+static void f_assert_match(typval_T *argvars, typval_T *rettv)
+{
+ assert_match_common(argvars, ASSERT_MATCH);
+}
+
+/// "assert_notmatch(pattern, actual[, msg])" function
+static void f_assert_notmatch(typval_T *argvars, typval_T *rettv)
+{
+ assert_match_common(argvars, ASSERT_NOTMATCH);
+}
+
// "assert_true(actual[, msg])" function
static void f_assert_true(typval_T *argvars, typval_T *rettv)
{
@@ -8087,38 +8158,6 @@ static void f_call(typval_T *argvars, typval_T *rettv)
(void)func_call(func, &argvars[1], selfdict, rettv);
}
-// "capture(command)" function
-static void f_capture(typval_T *argvars, typval_T *rettv)
-{
- int save_msg_silent = msg_silent;
- garray_T *save_capture_ga = capture_ga;
-
- if (check_secure()) {
- return;
- }
-
- garray_T capture_local;
- capture_ga = &capture_local;
- ga_init(capture_ga, (int)sizeof(char), 80);
-
- msg_silent++;
- if (argvars[0].v_type != VAR_LIST) {
- do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
- } else if (argvars[0].vval.v_list != NULL) {
- for (listitem_T *li = argvars[0].vval.v_list->lv_first;
- li != NULL; li = li->li_next) {
- do_cmdline_cmd((char *)get_tv_string(&li->li_tv));
- }
- }
- msg_silent = save_msg_silent;
-
- ga_append(capture_ga, NUL);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = capture_ga->ga_data;
-
- capture_ga = save_capture_ga;
-}
-
/*
* "ceil({float})" function
*/
@@ -8803,6 +8842,38 @@ static void f_executable(typval_T *argvars, typval_T *rettv)
|| (gettail_dir(name) != name && os_can_exe(name, NULL, false));
}
+// "execute(command)" function
+static void f_execute(typval_T *argvars, typval_T *rettv)
+{
+ int save_msg_silent = msg_silent;
+ garray_T *save_capture_ga = capture_ga;
+
+ if (check_secure()) {
+ return;
+ }
+
+ garray_T capture_local;
+ capture_ga = &capture_local;
+ ga_init(capture_ga, (int)sizeof(char), 80);
+
+ msg_silent++;
+ if (argvars[0].v_type != VAR_LIST) {
+ do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
+ } else if (argvars[0].vval.v_list != NULL) {
+ for (listitem_T *li = argvars[0].vval.v_list->lv_first;
+ li != NULL; li = li->li_next) {
+ do_cmdline_cmd((char *)get_tv_string(&li->li_tv));
+ }
+ }
+ msg_silent = save_msg_silent;
+
+ ga_append(capture_ga, NUL);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = capture_ga->ga_data;
+
+ capture_ga = save_capture_ga;
+}
+
/// "exepath()" function
static void f_exepath(typval_T *argvars, typval_T *rettv)
{
@@ -9917,6 +9988,50 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string[0] = cmdwin_type;
}
+// "getcompletion()" function
+static void f_getcompletion(typval_T *argvars, typval_T *rettv)
+{
+ char_u *pat;
+ expand_T xpc;
+ int options = WILD_KEEP_ALL | WILD_SILENT | WILD_USE_NL
+ | WILD_LIST_NOTFOUND | WILD_NO_BEEP;
+
+ if (p_wic) {
+ options |= WILD_ICASE;
+ }
+
+ ExpandInit(&xpc);
+ xpc.xp_pattern = get_tv_string(&argvars[0]);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1]));
+ if (xpc.xp_context == EXPAND_NOTHING) {
+ if (argvars[1].v_type == VAR_STRING) {
+ EMSG2(_(e_invarg2), argvars[1].vval.v_string);
+ } else {
+ EMSG(_(e_invarg));
+ }
+ return;
+ }
+
+ if (xpc.xp_context == EXPAND_MENUS) {
+ set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, false);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
+ pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
+ if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL)) {
+ int i;
+
+ ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
+
+ for (i = 0; i < xpc.xp_numfiles; i++) {
+ list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
+ }
+ }
+ xfree(pat);
+ ExpandCleanup(&xpc);
+}
+
/// `getcwd([{win}[, {tab}]])` function
///
/// Every scope not specified implies the currently selected scope object.
@@ -13526,11 +13641,12 @@ static void f_resolve(typval_T *argvars, typval_T *rettv)
{
char_u *v = NULL;
- v = mch_resolve_shortcut(p);
- if (v != NULL)
+ v = os_resolve_shortcut(p);
+ if (v != NULL) {
rettv->vval.v_string = v;
- else
+ } else {
rettv->vval.v_string = vim_strsave(p);
+ }
}
#else
# ifdef HAVE_READLINK
@@ -17038,6 +17154,32 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = 1;
}
+/// "win_getid()" function
+static void f_win_getid(typval_T *argvars, typval_T *rettv)
+{
+ rettv->vval.v_number = win_getid(argvars);
+}
+
+/// "win_gotoid()" function
+static void f_win_gotoid(typval_T *argvars, typval_T *rettv)
+{
+ rettv->vval.v_number = win_gotoid(argvars);
+}
+
+/// "win_id2tabwin()" function
+static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv)
+{
+ if (rettv_list_alloc(rettv) != FAIL) {
+ win_id2tabwin(argvars, rettv->vval.v_list);
+ }
+}
+
+/// "win_id2win()" function
+static void f_win_id2win(typval_T *argvars, typval_T *rettv)
+{
+ rettv->vval.v_number = win_id2win(argvars);
+}
+
/*
* "winbufnr(nr)" function
*/
@@ -18325,7 +18467,7 @@ static void init_tv(typval_T *varp)
* caller of incompatible types: it sets *denote to TRUE if "denote"
* is not NULL or returns -1 otherwise.
*/
-static long get_tv_number(typval_T *varp)
+long get_tv_number(typval_T *varp)
{
int error = FALSE;
@@ -18886,19 +19028,6 @@ set_var (
|| tv_check_lock(v->di_tv.v_lock, name, false)) {
return;
}
- if (v->di_tv.v_type != tv->v_type
- && !((v->di_tv.v_type == VAR_STRING
- || v->di_tv.v_type == VAR_NUMBER)
- && (tv->v_type == VAR_STRING
- || tv->v_type == VAR_NUMBER))
- && !((v->di_tv.v_type == VAR_NUMBER
- || v->di_tv.v_type == VAR_FLOAT)
- && (tv->v_type == VAR_NUMBER
- || tv->v_type == VAR_FLOAT))
- ) {
- EMSG2(_("E706: Variable type mismatch for: %s"), name);
- return;
- }
// Handle setting internal v: variables separately where needed to
// prevent changing the type.
@@ -18908,7 +19037,7 @@ set_var (
if (copy || tv->v_type != VAR_STRING)
v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv));
else {
- /* Take over the string to avoid an extra alloc/free. */
+ // Take over the string to avoid an extra alloc/free.
v->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
@@ -22344,7 +22473,10 @@ bool eval_has_provider(char *name)
} \
}
- static int has_clipboard = -1, has_python = -1, has_python3 = -1;
+ static int has_clipboard = -1;
+ static int has_python = -1;
+ static int has_python3 = -1;
+ static int has_ruby = -1;
if (!strcmp(name, "clipboard")) {
check_provider(clipboard);
@@ -22355,6 +22487,9 @@ bool eval_has_provider(char *name)
} else if (!strcmp(name, "python")) {
check_provider(python);
return has_python;
+ } else if (!strcmp(name, "ruby")) {
+ check_provider(ruby);
+ return has_ruby;
}
return false;
diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h
index d5c9b2c1ec..884c987f10 100644
--- a/src/nvim/eval_defs.h
+++ b/src/nvim/eval_defs.h
@@ -162,4 +162,14 @@ typedef struct list_stack_S {
/// Convert a hashitem pointer to a dictitem pointer
#define HI2DI(hi) HIKEY2DI((hi)->hi_key)
+/// Type of assert_* check being performed
+typedef enum
+{
+ ASSERT_EQUAL,
+ ASSERT_NOTEQUAL,
+ ASSERT_MATCH,
+ ASSERT_NOTMATCH,
+ ASSERT_OTHER,
+} assert_type_T;
+
#endif // NVIM_EVAL_DEFS_H
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 1c4c9737c3..317e40e43a 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -116,23 +116,20 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
process_is_tearing_down = true;
kl_iter(WatcherPtr, loop->children, current) {
Process *proc = (*current)->data;
- if (proc->detach) {
+ if (proc->detach || proc->type == kProcessTypePty) {
// Close handles to process without killing it.
CREATE_EVENT(loop->events, process_close_handles, 1, proc);
} else {
- if (proc->type == kProcessTypeUv) {
- uv_kill(proc->pid, SIGTERM);
- proc->term_sent = true;
- process_stop(proc);
- } else { // kProcessTypePty
- process_close_streams(proc);
- pty_process_close_master((PtyProcess *)proc);
- }
+ uv_kill(proc->pid, SIGTERM);
+ proc->term_sent = true;
+ process_stop(proc);
}
}
- // Wait until all children exit
- LOOP_PROCESS_EVENTS_UNTIL(loop, loop->events, -1, kl_empty(loop->children));
+ // Wait until all children exit and all close events are processed.
+ LOOP_PROCESS_EVENTS_UNTIL(
+ loop, loop->events, -1,
+ kl_empty(loop->children) && queue_empty(loop->events));
pty_process_teardown(loop);
}
@@ -315,8 +312,10 @@ static void decref(Process *proc)
static void process_close(Process *proc)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (process_is_tearing_down && proc->detach && proc->closed) {
- // If a detached process dies while tearing down it might get closed twice.
+ if (process_is_tearing_down && (proc->detach || proc->type == kProcessTypePty)
+ && proc->closed) {
+ // If a detached/pty process dies while tearing down it might get closed
+ // twice.
return;
}
assert(!proc->closed);
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index da64533708..5de9ac0523 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4821,9 +4821,8 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
int mix = FALSE; /* detected mixed encodings */
// Find all *.txt files.
- size_t dirlen = STRLEN(dir);
- STRCPY(NameBuff, dir);
- STRCAT(NameBuff, "/**/*");
+ size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
+ STRCAT(NameBuff, "/**/*"); // NOLINT
STRCAT(NameBuff, ext);
// Note: We cannot just do `&NameBuff` because it is a statically sized array
@@ -4841,7 +4840,7 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
* Open the tags file for writing.
* Do this before scanning through all the files.
*/
- STRCPY(NameBuff, dir);
+ STRLCPY(NameBuff, dir, sizeof(NameBuff));
add_pathsep((char *)NameBuff);
STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2);
fd_tags = mch_fopen((char *)NameBuff, "w");
@@ -5016,7 +5015,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
char_u **files;
// Get a list of all files in the help directory and in subdirectories.
- STRCPY(NameBuff, dirname);
+ STRLCPY(NameBuff, dirname, sizeof(NameBuff));
add_pathsep((char *)NameBuff);
STRCAT(NameBuff, "**");
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 76191d5a56..3f5d9b3244 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2170,19 +2170,19 @@ return {
command='ruby',
flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
addr_type=ADDR_LINES,
- func='ex_script_ni',
+ func='ex_ruby',
},
{
command='rubydo',
flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
addr_type=ADDR_LINES,
- func='ex_ni',
+ func='ex_rubydo',
},
{
command='rubyfile',
flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
addr_type=ADDR_LINES,
- func='ex_ni',
+ func='ex_rubyfile',
},
{
command='rviminfo',
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index b56b1cf013..6d24ba91f2 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -897,6 +897,21 @@ void ex_pydo(exarg_T *eap)
script_host_do_range("python", eap);
}
+void ex_ruby(exarg_T *eap)
+{
+ script_host_execute("ruby", eap);
+}
+
+void ex_rubyfile(exarg_T *eap)
+{
+ script_host_execute_file("ruby", eap);
+}
+
+void ex_rubydo(exarg_T *eap)
+{
+ script_host_do_range("ruby", eap);
+}
+
void ex_python3(exarg_T *eap)
{
script_host_execute("python3", eap);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 9bc7ec39da..8bae817211 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5597,6 +5597,17 @@ int parse_compl_arg(char_u *value, int vallen, int *complp,
return OK;
}
+int cmdcomplete_str_to_type(char_u *complete_str)
+{
+ for (int i = 0; command_complete[i].expand != 0; i++) {
+ if (STRCMP(complete_str, command_complete[i].name) == 0) {
+ return command_complete[i].expand;
+ }
+ }
+
+ return EXPAND_NOTHING;
+}
+
static void ex_colorscheme(exarg_T *eap)
{
if (*eap->arg == NUL) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 154558b332..3b598bd64b 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -493,22 +493,14 @@ readfile (
curbuf->b_flags &= ~(BF_NEW | BF_NEW_W);
}
- /*
- * Check readonly by trying to open the file for writing.
- * If this fails, we know that the file is readonly.
- */
- file_readonly = FALSE;
+ // Check readonly.
+ file_readonly = false;
if (!read_buffer && !read_stdin) {
- if (!newfile || readonlymode) {
- file_readonly = TRUE;
- } else if ((fd = os_open((char *)fname, O_RDWR, 0)) < 0) {
- // opening in readwrite mode failed => file is readonly
- file_readonly = TRUE;
- }
- if (file_readonly == TRUE) {
- // try to open readonly
- fd = os_open((char *)fname, O_RDONLY, 0);
+ if (!newfile || readonlymode || !(perm & 0222)
+ || !os_file_is_writable((char *)fname)) {
+ file_readonly = true;
}
+ fd = os_open((char *)fname, O_RDONLY, 0);
}
if (fd < 0) { /* cannot open at all */
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index ed9862a264..950ceb4c74 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -914,9 +914,6 @@ EXTERN int KeyTyped; // TRUE if user typed current char
EXTERN int KeyStuffed; // TRUE if current char from stuffbuf
EXTERN int maptick INIT(= 0); // tick for each non-mapped char
-EXTERN uint8_t chartab[256]; // table used in charset.c; See
- // init_chartab() for explanation
-
EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */
EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */
EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
@@ -989,7 +986,7 @@ EXTERN int redir_off INIT(= false); // no redirection for a moment
EXTERN FILE *redir_fd INIT(= NULL); // message redirection file
EXTERN int redir_reg INIT(= 0); // message redirection register
EXTERN int redir_vname INIT(= 0); // message redirection variable
-EXTERN garray_T *capture_ga INIT(= NULL); // capture() buffer
+EXTERN garray_T *capture_ga INIT(= NULL); // captured output for execute()
EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 5f69fa2f6a..503daa9648 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -73,11 +73,6 @@
/* Returns empty string if it is NULL. */
#define EMPTY_IF_NULL(x) ((x) ? (x) : (char_u *)"")
-/* macro version of chartab().
- * Only works with values 0-255!
- * Doesn't work for UTF-8 mode with chars >= 0x80. */
-#define CHARSIZE(c) (chartab[c] & CT_CELL_MASK)
-
/*
* Adjust chars in a language according to 'langmap' option.
* NOTE that there is no noticeable overhead if 'langmap' is not set.
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 64b5de8663..e052d0d315 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -670,8 +670,8 @@ static void init_locale(void)
{
char_u *p;
- /* expand_env() doesn't work yet, because chartab[] is not initialized
- * yet, call vim_getenv() directly */
+ // expand_env() doesn't work yet, because g_chartab[] is not
+ // initialized yet, call vim_getenv() directly
p = (char_u *)vim_getenv("VIMRUNTIME");
if (p != NULL && *p != NUL) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 03439e7a9c..398e74268f 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -129,7 +129,10 @@ static inline khint_t String_hash(String s)
static inline bool String_eq(String a, String b)
{
- return strncmp(a.data, b.data, MIN(a.size, b.size)) == 0;
+ if (a.size != b.size) {
+ return false;
+ }
+ return memcmp(a.data, b.data, a.size) == 0;
}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 0ba9f8b076..26d94aa6fa 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1885,6 +1885,86 @@ static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2)
return n1 == 0 ? -1 : 1;
}
+#ifdef WIN32
+#ifndef CP_UTF8
+# define CP_UTF8 65001 /* magic number from winnls.h */
+#endif
+
+int utf8_to_utf16(const char *str, WCHAR **strw)
+ FUNC_ATTR_NONNULL_ALL
+{
+ ssize_t wchar_len = 0;
+
+ // Compute the length needed to store the converted widechar string.
+ wchar_len = MultiByteToWideChar(CP_UTF8,
+ 0, // dwFlags: must be 0 for utf8
+ str, // lpMultiByteStr: string to convert
+ -1, // -1 => process up to NUL
+ NULL, // lpWideCharStr: converted string
+ 0); // 0 => return length, don't convert
+ if (wchar_len == 0) {
+ return GetLastError();
+ }
+
+ ssize_t buf_sz = wchar_len * sizeof(WCHAR);
+
+ if (buf_sz == 0) {
+ *strw = NULL;
+ return 0;
+ }
+
+ char *buf = xmalloc(buf_sz);
+ char *pos = buf;
+
+ int r = MultiByteToWideChar(CP_UTF8,
+ 0,
+ str,
+ -1,
+ (WCHAR *)pos,
+ wchar_len);
+ assert(r == wchar_len);
+ *strw = (WCHAR *)pos;
+
+ return 0;
+}
+
+int utf16_to_utf8(const WCHAR *strw, char **str)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // Compute the space required to store the string as UTF-8.
+ ssize_t utf8_len = WideCharToMultiByte(CP_UTF8,
+ 0,
+ strw,
+ -1,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ if (utf8_len == 0) {
+ return GetLastError();
+ }
+
+ ssize_t buf_sz = utf8_len * sizeof(char);
+ char *buf = xmalloc(buf_sz);
+ char *pos = buf;
+
+ // Convert string to UTF-8.
+ int r = WideCharToMultiByte(CP_UTF8,
+ 0,
+ strw,
+ -1,
+ (LPSTR *)pos,
+ utf8_len,
+ NULL,
+ NULL);
+ assert(r == utf8_len);
+ *str = pos;
+
+ return 0;
+}
+
+#endif
+
/*
* Version of strnicmp() that handles multi-byte characters.
* Needed for Big5, Shift-JIS and UTF-8 encoding. Other DBCS encodings can
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 673205f08f..08e82071d7 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3165,9 +3165,10 @@ attention_message (
}
/* Some of these messages are long to allow translation to
* other languages. */
- MSG_PUTS(_(
- "\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes."));
- MSG_PUTS(_(" Quit, or continue with caution.\n"));
+ MSG_PUTS(_("\n(1) Another program may be editing the same file. If this is"
+ " the case,\n be careful not to end up with two different"
+ " instances of the same\n file when making changes."
+ " Quit, or continue with caution.\n"));
MSG_PUTS(_("(2) An edit session for this file crashed.\n"));
MSG_PUTS(_(" If this is the case, use \":recover\" or \"vim -r "));
msg_outtrans(buf->b_fname);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 77e8f0e4f2..3c310ed309 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2396,7 +2396,7 @@ static void redir_write(char_u *str, int maxlen)
return;
}
- // Append output to capture().
+ // Append output for execute().
if (capture_ga) {
size_t len = 0;
while (str[len] && (maxlen < 0 ? 1 : (len < (size_t)maxlen))) {
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 2f499e477c..5efac2623c 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -6,6 +6,7 @@
#include "nvim/window.h"
#include "nvim/strings.h"
#include "nvim/screen.h"
+#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/os_unix.h"
#include "nvim/fold.h"
@@ -303,6 +304,10 @@ retnomove:
mouse_past_bottom = true;
}
+ if (!(flags & MOUSE_RELEASED) && which_button == MOUSE_LEFT) {
+ col = mouse_adjust_click(curwin, row, col);
+ }
+
// Start Visual mode before coladvance(), for when 'sel' != "old"
if ((flags & MOUSE_MAY_VIS) && !VIsual_active) {
check_visual_highlight();
@@ -597,3 +602,74 @@ bool mouse_scroll_horiz(int dir)
return leftcol_changed();
}
+
+// Adjust the clicked column position if there are concealed characters
+// before the current column. But only when it's absolutely necessary.
+static int mouse_adjust_click(win_T *wp, int row, int col)
+{
+ if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0
+ && wp->w_leftcol < curbuf->b_p_smc && conceal_cursor_line(wp))) {
+ return col;
+ }
+
+ int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum));
+ int vend = getviscol2(end, 0);
+
+ if (col >= vend) {
+ return col;
+ }
+
+ int i = wp->w_leftcol;
+
+ if (row > 0) {
+ i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp)
+ - wp->w_leftcol) + wp->w_skipcol;
+ }
+
+ int start_col = i;
+ int matchid;
+ int last_matchid;
+ int bcol = end - (vend - col);
+
+ while (i < bcol) {
+ matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i);
+
+ if (matchid != 0) {
+ if (wp->w_p_cole == 3) {
+ bcol++;
+ } else {
+ if (row > 0 && i == start_col) {
+ // Check if the current concealed character is actually part of
+ // the previous wrapped row's conceal group.
+ last_matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum,
+ i - 1);
+ if (last_matchid == matchid) {
+ bcol++;
+ }
+ } else if (wp->w_p_cole == 1
+ || (wp->w_p_cole == 2
+ && (lcs_conceal != NUL
+ || syn_get_sub_char() != NUL))) {
+ // At least one placeholder character will be displayed.
+ bcol--;
+ }
+
+ last_matchid = matchid;
+
+ // Adjust for concealed text that spans more than one character.
+ do {
+ i++;
+ bcol++;
+ matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i);
+ } while (last_matchid == matchid);
+
+ continue;
+ }
+ }
+
+ i++;
+ }
+
+ return getviscol2(bcol, 0);
+}
+
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index abbd3e8aff..d7c2926a0f 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -40,6 +40,10 @@ bool server_init(void)
listen_address = server_address_new();
}
+ if (!listen_address) {
+ return false;
+ }
+
bool ok = (server_start(listen_address) == 0);
if (must_free) {
xfree((char *) listen_address);
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index a498fc481a..e8a79fa820 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -339,10 +339,16 @@ static void shift_block(oparg_T *oap, int amount)
total += bd.pre_whitesp; /* all virtual WS up to & incl a split TAB */
ws_vcol = bd.start_vcol - bd.pre_whitesp;
if (bd.startspaces) {
- if (has_mbyte)
- bd.textstart += (*mb_ptr2len)(bd.textstart);
- else
- ++bd.textstart;
+ if (has_mbyte) {
+ if ((*mb_ptr2len)(bd.textstart) == 1) {
+ bd.textstart++;
+ } else {
+ ws_vcol = 0;
+ bd.startspaces = 0;
+ }
+ } else {
+ bd.textstart++;
+ }
}
for (; ascii_iswhite(*bd.textstart); ) {
// TODO: is passing bd.textstart for start of the line OK?
@@ -5452,7 +5458,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
yankreg_T *target;
if (cb_flags & CB_UNNAMEDPLUS) {
- *name = cb_flags & CB_UNNAMED ? '"': '+';
+ *name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
target = &y_regs[PLUS_REGISTER];
} else {
*name = '*';
diff --git a/src/nvim/option.c b/src/nvim/option.c
index a7b44b372c..6baf8c65ce 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2451,16 +2451,13 @@ did_set_string_option (
else if (varp == &curwin->w_p_briopt) {
if (briopt_check(curwin) == FAIL)
errmsg = e_invarg;
- }
- /*
- * 'isident', 'iskeyword', 'isprint or 'isfname' option: refill chartab[]
- * If the new option is invalid, use old value. 'lisp' option: refill
- * chartab[] for '-' char
- */
- else if ( varp == &p_isi
+ } else if (varp == &p_isi
|| varp == &(curbuf->b_p_isk)
|| varp == &p_isp
|| varp == &p_isf) {
+ // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
+ // If the new option is invalid, use old value. 'lisp' option: refill
+ // g_chartab[] for '-' char
if (init_chartab() == FAIL) {
did_chartab = TRUE; /* need to restore it below */
errmsg = e_invarg; /* error in value */
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 6cee102305..cf5bfd60ae 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -4,7 +4,6 @@
/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite
/// replacement.
-#include <unistd.h>
#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index d12d34d595..cd943c4843 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -3,7 +3,6 @@
#include <stddef.h>
#include <assert.h>
#include <limits.h>
-#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@@ -26,6 +25,10 @@
#include "nvim/path.h"
#include "nvim/strings.h"
+#ifdef WIN32
+#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fs.c.generated.h"
#endif
@@ -923,10 +926,100 @@ bool os_fileid_equal(const FileID *file_id_1, const FileID *file_id_2)
/// @param file_info Pointer to a `FileInfo`
/// @return `true` if the `FileID` and the `FileInfo` represent te same file.
bool os_fileid_equal_fileinfo(const FileID *file_id,
- const FileInfo *file_info)
+ const FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL
{
return file_id->inode == file_info->stat.st_ino
&& file_id->device_id == file_info->stat.st_dev;
}
+#ifdef WIN32
+# include <shlobj.h>
+
+/// When "fname" is the name of a shortcut (*.lnk) resolve the file it points
+/// to and return that name in allocated memory.
+/// Otherwise NULL is returned.
+char_u * os_resolve_shortcut(char_u *fname)
+{
+ HRESULT hr;
+ IPersistFile *ppf = NULL;
+ OLECHAR wsz[MAX_PATH];
+ char_u *rfname = NULL;
+ int len;
+ int conversion_result;
+ IShellLinkW *pslw = NULL;
+ WIN32_FIND_DATAW ffdw;
+
+ // Check if the file name ends in ".lnk". Avoid calling CoCreateInstance(),
+ // it's quite slow.
+ if (fname == NULL) {
+ return rfname;
+ }
+ len = (int)STRLEN(fname);
+ if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) {
+ return rfname;
+ }
+
+ CoInitialize(NULL);
+
+ // create a link manager object and request its interface
+ hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkW, (void **)&pslw);
+ if (hr == S_OK) {
+ WCHAR *p;
+ int conversion_result = utf8_to_utf16((char *)fname, &p);
+ if (conversion_result != 0) {
+ EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result));
+ }
+
+ if (p != NULL) {
+ // Get a pointer to the IPersistFile interface.
+ hr = pslw->lpVtbl->QueryInterface(
+ pslw, &IID_IPersistFile, (void **)&ppf);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+
+ // "load" the name and resolve the link
+ hr = ppf->lpVtbl->Load(ppf, p, STGM_READ);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+
+# if 0 // This makes Vim wait a long time if the target does not exist.
+ hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI);
+ if (hr != S_OK) {
+ goto shortcut_errorw;
+ }
+# endif
+
+ // Get the path to the link target.
+ ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR));
+ hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0);
+ if (hr == S_OK && wsz[0] != NUL) {
+ int conversion_result = utf16_to_utf8(wsz, &rfname);
+ if (conversion_result != 0) {
+ EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result));
+ }
+ }
+
+shortcut_errorw:
+ xfree(p);
+ goto shortcut_end;
+ }
+ }
+
+shortcut_end:
+ // Release all interface pointers (both belong to the same object)
+ if (ppf != NULL) {
+ ppf->lpVtbl->Release(ppf);
+ }
+ if (pslw != NULL) {
+ pslw->lpVtbl->Release(pslw);
+ }
+
+ CoUninitialize();
+ return rfname;
+}
+
+#endif
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 436de030ba..b57a69b82b 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -3,7 +3,6 @@
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/wait.h>
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index 8a3fcb8f78..0326f33bb5 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -5653,7 +5653,7 @@ msgstr "単語 '%.*s' が %s から削除されました"
#: ../spell.c:8117
#, c-format
msgid "Word '%.*s' added to %s"
-msgstr "%s に単語が追加されました"
+msgstr "単語 '%.*s' が %s へ追加されました"
#: ../spell.c:8381
msgid "E763: Word characters differ between spell files"
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 886a48e7c5..f8fd7d4ef8 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1389,6 +1389,10 @@ int vim_regcomp_had_eol(void)
return had_eol;
}
+// variables for parsing reginput
+static int at_start; // True when on the first character
+static int prev_at_start; // True when on the second character
+
/*
* Parse regular expression, i.e. main body or parenthesized thing.
*
@@ -1768,6 +1772,7 @@ static char_u *regatom(int *flagp)
int c;
char_u *p;
int extra = 0;
+ int save_prev_at_start = prev_at_start;
*flagp = WORST; /* Tentatively. */
@@ -2143,17 +2148,21 @@ static char_u *regatom(int *flagp)
}
break;
} else if (c == 'l' || c == 'c' || c == 'v') {
- if (c == 'l')
+ if (c == 'l') {
ret = regnode(RE_LNUM);
- else if (c == 'c')
+ if (save_prev_at_start) {
+ at_start = true;
+ }
+ } else if (c == 'c') {
ret = regnode(RE_COL);
- else
+ } else {
ret = regnode(RE_VCOL);
- if (ret == JUST_CALC_SIZE)
+ }
+ if (ret == JUST_CALC_SIZE) {
regsize += 5;
- else {
- /* put the number and the optional
- * comparator after the opcode */
+ } else {
+ // put the number and the optional
+ // comparator after the opcode
regcode = re_put_uint32(regcode, n);
*regcode++ = cmp;
}
@@ -2679,9 +2688,6 @@ static void regoptail(char_u *p, char_u *val)
* Functions for getting characters from the regexp input.
*/
-static int at_start; /* True when on the first character */
-static int prev_at_start; /* True when on the second character */
-
/*
* Start parsing at "str".
*/
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index f97dce9e0d..92dbd693ea 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -1096,6 +1096,7 @@ static int nfa_regatom(void)
int startc = -1;
int endc = -1;
int oldstartc = -1;
+ int save_prev_at_start = prev_at_start;
c = getchr();
switch (c) {
@@ -1412,18 +1413,22 @@ static int nfa_regatom(void)
c = getchr();
}
if (c == 'l' || c == 'c' || c == 'v') {
- if (c == 'l')
- /* \%{n}l \%{n}<l \%{n}>l */
+ if (c == 'l') {
+ // \%{n}l \%{n}<l \%{n}>l
EMIT(cmp == '<' ? NFA_LNUM_LT :
- cmp == '>' ? NFA_LNUM_GT : NFA_LNUM);
- else if (c == 'c')
- /* \%{n}c \%{n}<c \%{n}>c */
+ cmp == '>' ? NFA_LNUM_GT : NFA_LNUM);
+ if (save_prev_at_start) {
+ at_start = true;
+ }
+ } else if (c == 'c') {
+ // \%{n}c \%{n}<c \%{n}>c
EMIT(cmp == '<' ? NFA_COL_LT :
- cmp == '>' ? NFA_COL_GT : NFA_COL);
- else
- /* \%{n}v \%{n}<v \%{n}>v */
+ cmp == '>' ? NFA_COL_GT : NFA_COL);
+ } else {
+ // \%{n}v \%{n}<v \%{n}>v
EMIT(cmp == '<' ? NFA_VCOL_LT :
- cmp == '>' ? NFA_VCOL_GT : NFA_VCOL);
+ cmp == '>' ? NFA_VCOL_GT : NFA_VCOL);
+ }
EMIT(n);
break;
} else if (c == '\'' && n == 0) {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 34eef83164..d67142822f 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3416,12 +3416,10 @@ win_line (
/*
* Handling of non-printable characters.
*/
- if (!(chartab[c & 0xff] & CT_PRINT_CHAR)) {
- /*
- * when getting a character from the file, we may have to
- * turn it into something else on the way to putting it
- * into "ScreenLines".
- */
+ if (!vim_isprintc(c)) {
+ // when getting a character from the file, we may have to
+ // turn it into something else on the way to putting it
+ // into "ScreenLines".
if (c == TAB && (!wp->w_p_list || lcs_tab1)) {
int tab_len = 0;
long vcol_adjusted = vcol; // removed showbreak length
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 27855184df..3215f7ea14 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5661,6 +5661,24 @@ int get_syntax_info(int *seqnrp)
return current_flags;
}
+
+/// Get the sequence number of the concealed file position.
+///
+/// @return seqnr if the file position is concealed, 0 otherwise.
+int syn_get_concealed_id(win_T *wp, linenr_T lnum, colnr_T col)
+{
+ int seqnr;
+ int syntax_flags;
+
+ (void)syn_get_id(wp, lnum, col, false, NULL, false);
+ syntax_flags = get_syntax_info(&seqnr);
+
+ if (syntax_flags & HL_CONCEAL) {
+ return seqnr;
+ }
+ return 0;
+}
+
/*
* Return conceal substitution character
*/
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index fd416b3dcc..6f50c03be9 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -241,6 +241,7 @@ Terminal *terminal_open(TerminalOptions opts)
set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL);
set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL);
set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL);
+ buf_set_term_title(curbuf, (char *)curbuf->b_ffname);
RESET_BINDING(curwin);
// Apply TermOpen autocmds so the user can configure the terminal
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
@@ -618,6 +619,17 @@ static int term_movecursor(VTermPos new, VTermPos old, int visible,
return 1;
}
+static void buf_set_term_title(buf_T *buf, char *title)
+ FUNC_ATTR_NONNULL_ALL
+{
+ Error err;
+ api_free_object(dict_set_value(buf->b_vars,
+ cstr_as_string("term_title"),
+ STRING_OBJ(cstr_as_string(title)),
+ false,
+ &err));
+}
+
static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
{
Terminal *term = data;
@@ -633,12 +645,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
case VTERM_PROP_TITLE: {
buf_T *buf = handle_get_buffer(term->buf_handle);
- Error err;
- api_free_object(dict_set_value(buf->b_vars,
- cstr_as_string("term_title"),
- STRING_OBJ(cstr_as_string(val->string)),
- false,
- &err));
+ buf_set_term_title(buf, val->string);
break;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 4b0b5e8d26..4979aae57a 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -14,7 +14,6 @@ SCRIPTS := \
test14.out \
test17.out \
test24.out \
- test30.out \
test32.out \
test37.out \
test40.out \
@@ -33,15 +32,14 @@ SCRIPTS := \
# Tests using runtest.vim.vim.
# Keep test_alot*.res as the last one, sort the others.
NEW_TESTS = \
- test_cursor_func.res \
test_hardcopy.res \
test_help_tagjump.res \
test_langmap.res \
- test_menu.res \
test_syntax.res \
test_timers.res \
- test_unlet.res \
test_viml.res \
+ test_visual.res \
+ test_window_id.res \
test_alot.res
SCRIPTS_GUI := test16.out
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 1b1f5d7688..74bbf418fa 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -2,6 +2,11 @@
" When the script is successful the .res file will be created.
" Errors are appended to the test.log file.
"
+" To execute only specific test functions, add a second argument. It will be
+" matched against the names of the Test_ function. E.g.:
+" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
+" The output can be found in the "messages" file.
+"
" The test script may contain anything, only functions that start with
" "Test_" are special. These will be invoked and should contain assert
" functions. See test_assert.vim for an example.
@@ -68,6 +73,11 @@ silent function /^Test_
redir END
let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))
+" If there is an extra argument filter the function names against it.
+if argc() > 1
+ let tests = filter(tests, 'v:val =~ argv(1)')
+endif
+
" Execute the tests in alphabetical order.
for test in sort(tests)
echo 'Executing ' . test
diff --git a/src/nvim/testdir/test30.in b/src/nvim/testdir/test30.in
deleted file mode 100644
index 56d5d5c6c2..0000000000
--- a/src/nvim/testdir/test30.in
+++ /dev/null
@@ -1,230 +0,0 @@
-Test for a lot of variations of the 'fileformats' option
-
-Note: This test will fail if "cat" is not available.
-
-STARTTEST
-:" first write three test files, one in each format
-:set fileformat=unix
-:set fileformats=
-:/^unix/;/eof/-1w! XXUnix
-:/^dos/;/eof/-1w! XXDos
-:set bin noeol
-:$w! XXMac
-Gonoeol
-:$w! XXEol
-:set nobin eol
-:enew!
-:bwipe XXUnix XXDos XXMac
-:" create mixed format files
-:if has("win32")
-: !copy /b XXUnix+XXDos XXUxDs
-: !copy /b XXUnix+XXMac XXUxMac
-: !copy /b XXDos+XXMac XXDosMac
-: !copy /b XXMac+XXEol XXMacEol
-: !copy /b XXUnix+XXDos+XXMac XXUxDsMc
-:else
-: !cat XXUnix XXDos >XXUxDs
-: !cat XXUnix XXMac >XXUxMac
-: !cat XXDos XXMac >XXDosMac
-: !cat XXMac XXEol >XXMacEol
-: !cat XXUnix XXDos XXMac >XXUxDsMc
-:endif
-:"
-:" try reading and writing with 'fileformats' empty
-:set fileformat=unix
-:e! XXUnix
-:w! test.out
-:e! XXDos
-:w! XXtt01
-:e! XXMac
-:w! XXtt02
-:bwipe XXUnix XXDos XXMac
-:set fileformat=dos
-:e! XXUnix
-:w! XXtt11
-:e! XXDos
-:w! XXtt12
-:e! XXMac
-:w! XXtt13
-:bwipe XXUnix XXDos XXMac
-:set fileformat=mac
-:e! XXUnix
-:w! XXtt21
-:e! XXDos
-:w! XXtt22
-:e! XXMac
-:w! XXtt23
-:bwipe XXUnix XXDos XXMac
-:"
-:" try reading and writing with 'fileformats' set to one format
-:set fileformats=unix
-:e! XXUxDsMc
-:w! XXtt31
-:bwipe XXUxDsMc
-:set fileformats=dos
-:e! XXUxDsMc
-:w! XXtt32
-:bwipe XXUxDsMc
-:set fileformats=mac
-:e! XXUxDsMc
-:w! XXtt33
-:bwipe XXUxDsMc
-:"
-:" try reading and writing with 'fileformats' set to two formats
-:set fileformats=unix,dos
-:e! XXUxDsMc
-:w! XXtt41
-:bwipe XXUxDsMc
-:e! XXUxMac
-:w! XXtt42
-:bwipe XXUxMac
-:e! XXDosMac
-:w! XXtt43
-:bwipe XXDosMac
-:set fileformats=unix,mac
-:e! XXUxDs
-:w! XXtt51
-:bwipe XXUxDs
-:e! XXUxDsMc
-:w! XXtt52
-:bwipe XXUxDsMc
-:e! XXDosMac
-:w! XXtt53
-:bwipe XXDosMac
-:e! XXEol
-ggO=&ffs
-:=&ff
-:w! XXtt54
-:bwipe XXEol
-:set fileformats=dos,mac
-:e! XXUxDs
-:w! XXtt61
-:bwipe XXUxDs
-:e! XXUxMac
-ggO=&ffs
-:=&ff
-:w! XXtt62
-:bwipe XXUxMac
-:e! XXUxDsMc
-:w! XXtt63
-:bwipe XXUxDsMc
-:e! XXMacEol
-ggO=&ffs
-:=&ff
-:w! XXtt64
-:bwipe XXMacEol
-:"
-:" try reading and writing with 'fileformats' set to three formats
-:set fileformats=unix,dos,mac
-:e! XXUxDsMc
-:w! XXtt71
-:bwipe XXUxDsMc
-:e! XXEol
-ggO=&ffs
-:=&ff
-:w! XXtt72
-:bwipe XXEol
-:set fileformats=mac,dos,unix
-:e! XXUxDsMc
-:w! XXtt81
-:bwipe XXUxDsMc
-:e! XXEol
-ggO=&ffs
-:=&ff
-:w! XXtt82
-:bwipe XXEol
-:" try with 'binary' set
-:set fileformats=mac,unix,dos
-:set binary
-:e! XXUxDsMc
-:w! XXtt91
-:bwipe XXUxDsMc
-:set fileformats=mac
-:e! XXUxDsMc
-:w! XXtt92
-:bwipe XXUxDsMc
-:set fileformats=dos
-:e! XXUxDsMc
-:w! XXtt93
-:"
-:" Append "END" to each file so that we can see what the last written char was.
-:set fileformat=unix nobin
-ggdGaEND:w >>XXtt01
-:w >>XXtt02
-:w >>XXtt11
-:w >>XXtt12
-:w >>XXtt13
-:w >>XXtt21
-:w >>XXtt22
-:w >>XXtt23
-:w >>XXtt31
-:w >>XXtt32
-:w >>XXtt33
-:w >>XXtt41
-:w >>XXtt42
-:w >>XXtt43
-:w >>XXtt51
-:w >>XXtt52
-:w >>XXtt53
-:w >>XXtt54
-:w >>XXtt61
-:w >>XXtt62
-:w >>XXtt63
-:w >>XXtt64
-:w >>XXtt71
-:w >>XXtt72
-:w >>XXtt81
-:w >>XXtt82
-:w >>XXtt91
-:w >>XXtt92
-:w >>XXtt93
-:"
-:" Concatenate the results.
-:" Make fileformat of test.out the native fileformat.
-:" Add a newline at the end.
-:set binary
-:e! test.out
-:$r XXtt01
-:$r XXtt02
-Go1:$r XXtt11
-:$r XXtt12
-:$r XXtt13
-Go2:$r XXtt21
-:$r XXtt22
-:$r XXtt23
-Go3:$r XXtt31
-:$r XXtt32
-:$r XXtt33
-Go4:$r XXtt41
-:$r XXtt42
-:$r XXtt43
-Go5:$r XXtt51
-:$r XXtt52
-:$r XXtt53
-:$r XXtt54
-Go6:$r XXtt61
-:$r XXtt62
-:$r XXtt63
-:$r XXtt64
-Go7:$r XXtt71
-:$r XXtt72
-Go8:$r XXtt81
-:$r XXtt82
-Go9:$r XXtt91
-:$r XXtt92
-:$r XXtt93
-Go10:$r XXUnix
-:set nobinary ff&
-:w
-:qa!
-ENDTEST
-
-unix
-unix
-eof
-
-dos
-dos
-eof
-
-mac mac
diff --git a/src/nvim/testdir/test30.ok b/src/nvim/testdir/test30.ok
deleted file mode 100644
index b35f4f5904..0000000000
--- a/src/nvim/testdir/test30.ok
+++ /dev/null
@@ -1,130 +0,0 @@
-unix
-unix
-dos
-dos
-END
-mac mac
-END
-1
-unix
-unix
-END
-dos
-dos
-END
-mac mac
-END
-2
-unix
-unix
- END
-dos
-dos
- END
-mac mac END
-3
-unix
-unix
-dos
-dos
-mac mac
-END
-unix
-unix
-dos
-dos
-mac mac
-END
-unix
-unix
-dos
-dos
-mac mac END
-4
-unix
-unix
-dos
-dos
-mac mac
-END
-unix
-unix
-mac mac
-END
-dos
-dos
-mac mac
-END
-5
-unix
-unix
-dos
-dos
-END
-unix
-unix
-dos
-dos
-mac mac
-END
-dos
-dos
-mac mac END
-unix,mac:unix
-noeol
-END
-6
-unix
-unix
-dos
-dos
-END
-dos,mac:dos
-unix
-unix
-mac mac
-END
-unix
-unix
-dos
-dos
-mac mac
-END
-dos,mac:mac mac mac noeol END
-7
-unix
-unix
-dos
-dos
-mac mac
-END
-unix,dos,mac:unix
-noeol
-END
-8
-unix
-unix
-dos
-dos
-mac mac
-END
-mac,dos,unix:mac noeol END
-9
-unix
-unix
-dos
-dos
-mac mac END
-unix
-unix
-dos
-dos
-mac mac END
-unix
-unix
-dos
-dos
-mac mac END
-10
-unix
-unix
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 1d1da94bac..30b8a9ceb8 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -1,3 +1,9 @@
" A series of tests that can run in one Vim invocation.
" This makes testing go faster, since Vim doesn't need to restart.
+source test_assign.vim
+source test_cursor_func.vim
+source test_cmdline.vim
+source test_menu.vim
+source test_popup.vim
+source test_unlet.vim
diff --git a/src/nvim/testdir/test_assign.vim b/src/nvim/testdir/test_assign.vim
new file mode 100644
index 0000000000..3d2e7a8998
--- /dev/null
+++ b/src/nvim/testdir/test_assign.vim
@@ -0,0 +1,9 @@
+" Test for assignment
+
+func Test_no_type_checking()
+ let v = 1
+ let v = [1,2,3]
+ let v = {'a':1, 'b':2}
+ let v = 3.4
+ let v = 'hello'
+endfunc
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
new file mode 100644
index 0000000000..438b20cc5e
--- /dev/null
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -0,0 +1,120 @@
+" Tests for editing the command line.
+
+func Test_complete_tab()
+ call writefile(['testfile'], 'Xtestfile')
+ call feedkeys(":e Xtest\t\r", "tx")
+ call assert_equal('testfile', getline(1))
+ call delete('Xtestfile')
+endfunc
+
+func Test_complete_list()
+ " We can't see the output, but at least we check the code runs properly.
+ call feedkeys(":e test\<C-D>\r", "tx")
+ call assert_equal('test', expand('%:t'))
+endfunc
+
+func Test_complete_wildmenu()
+ call writefile(['testfile1'], 'Xtestfile1')
+ call writefile(['testfile2'], 'Xtestfile2')
+ set wildmenu
+ call feedkeys(":e Xtest\t\t\r", "tx")
+ call assert_equal('testfile2', getline(1))
+
+ call delete('Xtestfile1')
+ call delete('Xtestfile2')
+ set nowildmenu
+endfunc
+
+func Test_getcompletion()
+ if !has('cmdline_compl')
+ return
+ endif
+ let groupcount = len(getcompletion('', 'event'))
+ call assert_true(groupcount > 0)
+ let matchcount = len(getcompletion('File', 'event'))
+ call assert_true(matchcount > 0)
+ call assert_true(groupcount > matchcount)
+
+ if has('menu')
+ source $VIMRUNTIME/menu.vim
+ let matchcount = len(getcompletion('', 'menu'))
+ call assert_true(matchcount > 0)
+ call assert_equal(['File.'], getcompletion('File', 'menu'))
+ call assert_true(matchcount > 0)
+ let matchcount = len(getcompletion('File.', 'menu'))
+ call assert_true(matchcount > 0)
+ endif
+
+ let l = getcompletion('v:n', 'var')
+ call assert_true(index(l, 'v:null') >= 0)
+
+ let l = getcompletion('', 'augroup')
+ call assert_true(index(l, 'END') >= 0)
+
+ let l = getcompletion('', 'behave')
+ call assert_true(index(l, 'mswin') >= 0)
+
+ let l = getcompletion('', 'color')
+ call assert_true(index(l, 'default') >= 0)
+
+ let l = getcompletion('', 'command')
+ call assert_true(index(l, 'sleep') >= 0)
+
+ let l = getcompletion('', 'dir')
+ call assert_true(index(l, 'sautest') >= 0)
+
+ let l = getcompletion('exe', 'expression')
+ call assert_true(index(l, 'executable(') >= 0)
+
+ let l = getcompletion('tag', 'function')
+ call assert_true(index(l, 'taglist(') >= 0)
+
+ let l = getcompletion('run', 'file')
+ call assert_true(index(l, 'runtest.vim') >= 0)
+
+ let l = getcompletion('ha', 'filetype')
+ call assert_true(index(l, 'hamster') >= 0)
+
+ let l = getcompletion('z', 'syntax')
+ call assert_true(index(l, 'zimbu') >= 0)
+
+ let l = getcompletion('jikes', 'compiler')
+ call assert_true(index(l, 'jikes') >= 0)
+
+ let l = getcompletion('time', 'option')
+ call assert_true(index(l, 'timeoutlen') >= 0)
+
+ let l = getcompletion('er', 'highlight')
+ call assert_true(index(l, 'ErrorMsg') >= 0)
+
+ " For others test if the name is recognized.
+ let names = ['buffer', 'environment', 'file_in_path',
+ \ 'mapping', 'shellcmd', 'tag', 'tag_listfiles', 'user']
+ if has('cscope')
+ call add(names, 'cscope')
+ endif
+ if has('cmdline_hist')
+ call add(names, 'history')
+ endif
+ if has('gettext')
+ call add(names, 'locale')
+ endif
+ if has('profile')
+ call add(names, 'syntime')
+ endif
+ if has('signs')
+ call add(names, 'sign')
+ endif
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", "word\tfile\tcmd"], 'Xtags')
+
+ for name in names
+ let matchcount = len(getcompletion('', name))
+ call assert_true(matchcount >= 0, 'No matches for ' . name)
+ endfor
+
+ call delete('Xtags')
+
+ call assert_fails('call getcompletion("", "burp")', 'E475:')
+endfunc
diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim
new file mode 100644
index 0000000000..78fc81e3d2
--- /dev/null
+++ b/src/nvim/testdir/test_popup.vim
@@ -0,0 +1,30 @@
+" Test for completion menu
+
+function! ComplTest() abort
+ call complete(1, ['source', 'soundfold'])
+ return ''
+endfunction
+
+function! Test() abort
+ call complete(1, ['source', 'soundfold'])
+ return ''
+endfunction
+
+func Test_noinsert_complete()
+ new
+ set completeopt+=noinsert
+ inoremap <F5> <C-R>=ComplTest()<CR>
+ call feedkeys("i\<F5>soun\<CR>\<CR>\<ESC>.", 'tx')
+ call assert_equal('soundfold', getline(1))
+ call assert_equal('soundfold', getline(2))
+ bwipe!
+
+ new
+ inoremap <F5> <C-R>=Test()<CR>
+ call feedkeys("i\<F5>\<CR>\<ESC>", 'tx')
+ call assert_equal('source', getline(1))
+ bwipe!
+
+ set completeopt-=noinsert
+ iunmap <F5>
+endfunc
diff --git a/src/nvim/testdir/test_viml.vim b/src/nvim/testdir/test_viml.vim
index 2d989cdad9..c39c5e6b28 100644
--- a/src/nvim/testdir/test_viml.vim
+++ b/src/nvim/testdir/test_viml.vim
@@ -55,16 +55,26 @@ endfunction
" ExecAsScript - Source a temporary script made from a function. {{{2
"
" Make a temporary script file from the function a:funcname, ":source" it, and
-" delete it afterwards.
+" delete it afterwards. However, if an exception is thrown the file may remain,
+" the caller should call DeleteTheScript() afterwards.
+let s:script_name = ''
function! ExecAsScript(funcname)
" Make a script from the function passed as argument.
- let script = MakeScript(a:funcname)
+ let s:script_name = MakeScript(a:funcname)
" Source and delete the script.
- exec "source" script
- call delete(script)
+ exec "source" s:script_name
+ call delete(s:script_name)
+ let s:script_name = ''
endfunction
+function! DeleteTheScript()
+ if s:script_name
+ call delete(s:script_name)
+ let s:script_name = ''
+ endif
+endfunc
+
com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
@@ -143,6 +153,7 @@ func Test_endwhile_script()
XpathINIT
ExecAsScript T1_F
Xpath 'F'
+ call DeleteTheScript()
try
ExecAsScript T1_G
@@ -152,6 +163,7 @@ func Test_endwhile_script()
Xpath 'x'
endtry
Xpath 'G'
+ call DeleteTheScript()
call assert_equal('abcFhijxG', g:Xpath)
endfunc
@@ -260,6 +272,7 @@ function Test_finish()
XpathINIT
ExecAsScript T4_F
Xpath '5'
+ call DeleteTheScript()
call assert_equal('ab3e3b2c25', g:Xpath)
endfunction
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
new file mode 100644
index 0000000000..83bae967e2
--- /dev/null
+++ b/src/nvim/testdir/test_visual.vim
@@ -0,0 +1,18 @@
+" Tests for Visual mode
+if !has('multi_byte')
+ finish
+endif
+scriptencoding utf-8
+
+if !has('visual')
+ finish
+endif
+
+func Test_block_shift_multibyte()
+ split
+ call setline(1, ['xヹxxx', 'ヹxxx'])
+ exe "normal 1G0l\<C-V>jl>"
+ call assert_equal('x ヹxxx', getline(1))
+ call assert_equal(' ヹxxx', getline(2))
+ q!
+endfunc
diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim
new file mode 100644
index 0000000000..b9e9f45c49
--- /dev/null
+++ b/src/nvim/testdir/test_window_id.vim
@@ -0,0 +1,71 @@
+" Test using the window ID.
+
+func Test_win_getid()
+ edit one
+ let id1 = win_getid()
+ split two
+ let id2 = win_getid()
+ split three
+ let id3 = win_getid()
+ tabnew
+ edit four
+ let id4 = win_getid()
+ split five
+ let id5 = win_getid()
+ tabnext
+
+ wincmd w
+ call assert_equal("two", expand("%"))
+ call assert_equal(id2, win_getid())
+ let nr2 = winnr()
+ wincmd w
+ call assert_equal("one", expand("%"))
+ call assert_equal(id1, win_getid())
+ let nr1 = winnr()
+ wincmd w
+ call assert_equal("three", expand("%"))
+ call assert_equal(id3, win_getid())
+ let nr3 = winnr()
+ tabnext
+ call assert_equal("five", expand("%"))
+ call assert_equal(id5, win_getid())
+ let nr5 = winnr()
+ wincmd w
+ call assert_equal("four", expand("%"))
+ call assert_equal(id4, win_getid())
+ let nr4 = winnr()
+ tabnext
+
+ exe nr1 . "wincmd w"
+ call assert_equal(id1, win_getid())
+ exe nr2 . "wincmd w"
+ call assert_equal(id2, win_getid())
+ exe nr3 . "wincmd w"
+ call assert_equal(id3, win_getid())
+ tabnext
+ exe nr4 . "wincmd w"
+ call assert_equal(id4, win_getid())
+ exe nr5 . "wincmd w"
+ call assert_equal(id5, win_getid())
+
+ call win_gotoid(id2)
+ call assert_equal("two", expand("%"))
+ call win_gotoid(id4)
+ call assert_equal("four", expand("%"))
+ call win_gotoid(id1)
+ call assert_equal("one", expand("%"))
+ call win_gotoid(id5)
+ call assert_equal("five", expand("%"))
+
+ call assert_equal(0, win_id2win(9999))
+ call assert_equal(nr5, win_id2win(id5))
+ call assert_equal(0, win_id2win(id1))
+ tabnext
+ call assert_equal(nr1, win_id2win(id1))
+
+ call assert_equal([0, 0], win_id2tabwin(9999))
+ call assert_equal([1, nr2], win_id2tabwin(id2))
+ call assert_equal([2, nr4], win_id2tabwin(id4))
+
+ only!
+endfunc
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 53551d81cf..aa337f9fae 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -75,29 +75,608 @@ static char *features[] = {
// clang-format off
static int included_patches[] = {
+ // 2200,
+ // 2199,
+ // 2198,
+ // 2197,
+ // 2196,
+ // 2195,
+ // 2194,
+ // 2193,
+ // 2192,
+ // 2191,
+ // 2190,
+ // 2189,
+ // 2188,
+ // 2187,
+ // 2186,
+ // 2185,
+ // 2184,
+ // 2183,
+ // 2182,
+ // 2181,
+ // 2180,
+ // 2179,
+ // 2178,
+ // 2177,
+ // 2176,
+ // 2175,
+ // 2174,
+ // 2173,
+ // 2172,
+ // 2171,
+ // 2170,
+ // 2169,
+ // 2168,
+ // 2167,
+ // 2166,
+ // 2165,
+ // 2164,
+ // 2163,
+ // 2162,
+ // 2161,
+ // 2160,
+ // 2159,
+ // 2158,
+ // 2157,
+ // 2156,
+ // 2155,
+ // 2154,
+ // 2153,
+ // 2152,
+ // 2151,
+ // 2150,
+ // 2149,
+ // 2148,
+ // 2147,
+ // 2146,
+ // 2145,
+ // 2144,
+ // 2143,
+ // 2142,
+ // 2141,
+ // 2140,
+ // 2139,
+ // 2138,
+ // 2137,
+ // 2136,
+ // 2135,
+ // 2134,
+ // 2133,
+ // 2132,
+ // 2131,
+ // 2130,
+ // 2129,
+ // 2128,
+ // 2127,
+ // 2126,
+ // 2125,
+ // 2124,
+ // 2123,
+ // 2122,
+ // 2121,
+ // 2120,
+ // 2119,
+ // 2118,
+ // 2117,
+ // 2116,
+ // 2115,
+ // 2114,
+ // 2113,
+ // 2112,
+ // 2111,
+ // 2110,
+ // 2109,
+ // 2108,
+ // 2107,
+ // 2106,
+ // 2105 NA
+ // 2104,
+ // 2103,
+ // 2102 NA
+ // 2101,
+ // 2100,
+ // 2099,
+ // 2098,
+ // 2097,
+ // 2096,
+ // 2095,
+ // 2094,
+ // 2093,
+ // 2092 NA
+ // 2091 NA
+ // 2090,
+ // 2089 NA
+ // 2088,
+ // 2087,
+ // 2086,
+ // 2085,
+ // 2084,
+ // 2083,
+ // 2082,
+ // 2081,
+ // 2080,
+ // 2079 NA
+ // 2078 NA
+ // 2077,
+ // 2076,
+ // 2075,
+ // 2074,
+ // 2073,
+ // 2072,
+ // 2071,
+ // 2070 NA
+ // 2069,
+ // 2068,
+ // 2067,
+ 2066,
+ // 2065,
+ // 2064,
+ // 2063 NA
+ // 2062,
+ // 2061,
+ // 2060 NA
+ // 2059 NA
+ // 2058,
+ // 2057 NA
+ // 2056 NA
+ // 2055 NA
+ // 2054 NA
+ // 2053 NA
+ // 2052 NA
+ // 2051,
+ // 2050,
+ // 2049,
+ // 2048 NA
+ // 2047,
+ // 2046,
+ // 2045 NA
+ // 2044,
+ // 2043,
+ // 2042 NA
+ // 2041 NA
+ // 2040 NA
+ // 2039 NA
+ // 2038 NA
+ // 2037 NA
+ // 2036,
+ // 2035 NA
+ // 2034 NA
+ // 2033,
+ // 2032 NA
+ // 2031,
+ // 2030 NA
+ // 2029,
+ // 2028,
+ // 2027 NA
+ // 2026 NA
+ // 2025 NA
+ // 2024,
+ // 2023,
+ // 2022,
+ // 2021,
+ // 2020 NA
+ // 2019,
+ // 2018,
+ // 2017,
+ // 2016 NA
+ // 2015,
+ 2014,
+ 2013,
+ 2012,
+ 2011,
+ // 2010,
+ // 2009,
+ // 2008,
+ // 2007,
+ // 2006,
+ // 2005,
+ // 2004 NA
+ // 2003 NA
+ // 2002,
+ // 2001 NA
+ // 2000,
+ // 1999,
+ // 1998 NA
+ // 1997,
+ // 1996,
+ // 1995 NA
+ // 1994,
+ // 1993,
+ // 1992,
+ // 1991,
+ // 1990,
+ // 1989,
+ // 1988 NA
+ // 1987 NA
+ // 1986,
+ // 1985 NA
+ // 1984,
+ // 1983 NA
+ // 1982 NA
+ // 1981,
+ // 1980,
+ // 1979,
+ // 1978,
+ // 1977,
+ // 1976,
+ // 1975,
+ // 1974 NA
1973,
+ // 1972,
+ // 1971,
+ // 1970,
+ // 1969 NA
+ // 1968,
+ // 1967,
+ // 1966,
+ // 1965 NA
+ // 1964,
+ // 1963 NA
+ // 1962,
+ // 1961,
1960,
+ // 1959 NA
+ // 1958 NA
+ // 1957 NA
+ // 1956,
+ // 1955,
+ // 1954,
+ // 1953,
+ // 1952,
+ // 1951 NA
+ // 1950,
+ // 1949,
+ // 1948,
+ // 1947 NA
+ // 1946 NA
+ // 1945,
+ // 1944 NA
+ // 1943 NA
+ // 1942 NA
+ // 1941,
+ // 1940,
+ // 1939 NA
+ // 1938 NA
+ // 1937,
+ // 1936,
+ // 1935 NA
+ // 1934 NA
+ // 1933 NA
+ // 1932 NA
+ // 1931 NA
+ // 1930 NA
+ // 1929 NA
+ // 1928,
+ // 1927 NA
+ // 1926 NA
+ // 1925 NA
+ // 1924 NA
+ // 1923,
+ // 1922 NA
+ // 1921 NA
+ // 1920 NA
+ // 1919 NA
+ // 1918 NA
+ // 1917 NA
+ // 1916 NA
+ // 1915 NA
+ // 1914,
+ // 1913,
+ // 1912,
+ // 1911,
+ // 1910,
+ // 1909,
+ // 1908 NA
+ // 1907,
+ // 1906 NA
+ // 1905,
+ // 1904,
+ // 1903,
+ // 1902 NA
+ // 1901 NA
+ // 1900,
+ // 1899 NA
+ // 1898,
+ // 1897,
+ // 1896,
+ // 1895,
+ // 1894 NA
+ // 1893,
+ // 1892 NA
+ // 1891 NA
+ // 1890 NA
+ // 1889,
+ // 1888,
+ // 1887 NA
+ // 1886 NA
+ // 1885 NA
+ // 1884,
+ // 1883 NA
+ // 1882,
+ // 1881,
+ // 1880 NA
+ // 1879 NA
+ // 1878 NA
+ // 1877 NA
+ // 1876,
+ // 1875,
+ // 1874 NA
+ // 1873 NA
+ // 1872 NA
+ // 1871,
+ // 1870 NA
+ // 1869 NA
+ // 1868,
+ // 1867,
+ // 1866,
+ // 1865 NA
+ // 1864 NA
+ // 1863 NA
+ // 1862,
+ // 1861,
+ // 1860 NA
+ // 1859 NA
+ // 1858 NA
+ // 1857 NA
+ // 1856 NA
+ // 1855 NA
+ // 1854 NA
+ // 1853 NA
+ // 1852 NA
+ // 1851,
+ // 1850 NA
+ // 1849 NA
+ // 1848 NA
+ // 1847,
+ // 1846 NA
+ // 1845 NA
+ // 1844,
+ // 1843 NA
+ // 1842,
+ // 1841,
1840,
+ // 1839,
+ // 1838,
+ // 1837,
+ // 1836,
+ // 1835,
+ // 1834,
+ // 1833,
1832,
1831,
+ // 1830 NA
+ // 1829 NA
+ // 1828 NA
+ // 1827 NA
+ // 1826 NA
+ // 1825 NA
+ // 1824 NA
+ // 1823,
+ // 1822 NA
+ // 1821,
+ // 1820,
+ // 1819 NA
+ // 1818,
+ // 1817 NA
+ // 1816,
+ // 1815,
+ // 1814 NA
+ // 1813,
+ // 1812,
+ // 1811,
+ // 1810 NA
1809,
1808,
+ // 1807 NA
1806,
+ // 1805,
+ // 1804,
+ // 1803 NA
+ // 1802,
+ // 1801 NA
+ // 1800 NA
1799,
+ // 1798 NA
+ // 1797 NA
+ // 1796 NA
+ // 1795 NA
+ // 1794,
+ // 1793,
+ // 1792 NA
+ // 1791 NA
+ // 1790 NA
+ // 1789 NA
+ // 1789 NA
+ // 1788 NA
+ // 1787 NA
+ // 1786 NA
+ // 1785,
+ // 1784 NA
+ // 1783,
+ // 1782,
+ // 1781,
+ // 1780,
+ // 1779,
+ // 1778 NA
+ // 1777 NA
+ // 1776 NA
+ // 1775 NA
+ // 1774 NA
+ // 1773 NA
+ // 1772 NA
+ // 1771 NA
+ // 1770,
+ // 1769,
+ // 1768,
+ // 1767 NA
+ // 1766 NA
+ // 1765,
+ // 1764 NA
+ // 1763,
+ // 1762,
+ // 1761,
+ // 1760 NA
+ // 1759,
+ // 1758,
1757,
+ // 1756 NA
1755,
+ // 1754,
1753,
+ // 1753,
+ // 1752,
+ // 1751,
+ // 1750,
+ // 1749 NA
+ // 1748,
+ // 1747 NA
+ // 1746 NA
+ // 1745 NA
+ // 1744 NA
+ // 1743 NA
+ // 1742,
+ // 1741,
+ // 1740,
+ // 1739,
+ // 1738,
+ // 1737 NA
+ // 1736 NA
+ // 1735,
+ // 1734,
+ // 1733 NA
+ 1732,
+ // 1731,
+ // 1730,
+ // 1729 NA
1728,
+ // 1727,
+ // 1726 NA
+ // 1725 NA
+ // 1724 NA
+ // 1723,
+ // 1722 NA
+ // 1721 NA
+ // 1720,
+ // 1719,
+ // 1718,
+ // 1717 NA
1716,
+ // 1715,
+ // 1714,
+ // 1713 NA
1712,
+ // 1711,
+ // 1710,
+ // 1709 NA
+ // 1708,
+ // 1707,
+ // 1706 NA
+ // 1705 NA
+ // 1704,
+ 1703,
+ // 1702,
+ // 1701,
+ // 1700,
+ // 1699,
+ // 1698 NA
+ // 1697,
+ // 1696,
1695,
+ // 1694 NA
+ // 1693 NA
+ // 1692,
+ // 1691,
+ // 1690 NA
+ // 1689 NA
+ // 1688 NA
+ // 1687 NA
+ // 1686,
+ // 1685,
+ // 1684 NA
+ // 1683 NA
+ 1682,
+ // 1681,
+ // 1680 NA
+ // 1679,
+ // 1678 NA
+ // 1677 NA
+ 1676,
+ 1675,
+ // 1674 NA
+ 1673,
+ // 1672 NA
+ // 1671,
+ // 1670,
+ // 1669 NA
+ // 1668 NA
+ // 1667 NA
+ // 1666 NA
+ // 1665 NA
+ // 1664,
+ 1663,
+ // 1662 NA
+ // 1661 NA
+ // 1660,
+ // 1659 NA
+ // 1658,
+ // 1657 NA
+ // 1656,
+ // 1655 NA
1654,
+ // 1653,
1652,
+ // 1651 NA
+ // 1650,
1649,
+ // 1648,
+ // 1647,
+ // 1646 NA
+ // 1645,
+ // 1644,
1643,
+ // 1642,
1641,
+ // 1640,
+ // 1639,
+ // 1638,
+ // 1637 NA
+ // 1636 NA
+ // 1635 NA
+ // 1634,
+ // 1633 NA
+ // 1632 NA
+ // 1631 NA
+ // 1630,
+ // 1629,
+ // 1628 NA
+ // 1627 NA
+ // 1626 NA
+ // 1625 NA
// 1624 NA
-
+ // 1623 NA
+ // 1622 NA
+ // 1621 NA
+ // 1620,
+ // 1619,
+ // 1618 NA
+ // 1617 NA
+ // 1616 NA
+ // 1615 NA
+ // 1614,
+ // 1613 NA
+ // 1612 NA
+ // 1611 NA
+ // 1610 NA
+ // 1609 NA
+ // 1608,
+ // 1607,
+ // 1606,
+ // 1605,
+ // 1604,
+ 1603,
+ // 1602 NA
+ // 1601 NA
// 1600 NA
// 1599 NA
// 1598 NA
@@ -117,7 +696,6 @@ static int included_patches[] = {
// 1584 NA
// 1583 NA
// 1582,
-
// 1581,
// 1580,
// 1579 NA
@@ -142,7 +720,7 @@ static int included_patches[] = {
// 1560 NA
// 1559,
// 1558,
- // 1557,
+ 1557,
// 1556 NA
// 1555 NA
1554,
@@ -151,9 +729,9 @@ static int included_patches[] = {
1551,
1550,
// 1549,
- // 1548,
+ 1548,
// 1547,
- // 1546,
+ 1546,
// 1545 NA
// 1544 NA
// 1543 NA
@@ -208,7 +786,7 @@ static int included_patches[] = {
// 1494,
// 1493 NA
1492,
- // 1491,
+ 1491,
// 1490 NA
// 1489 NA
// 1488 NA
@@ -243,7 +821,7 @@ static int included_patches[] = {
// 1459 NA
// 1458 NA
// 1457 NA
- // 1456,
+ // 1456 NA
// 1455 NA
// 1454 NA
// 1453 NA
@@ -334,7 +912,7 @@ static int included_patches[] = {
// 1368 NA
// 1367 NA
1366,
- // 1365,
+ 1365,
// 1364 NA
// 1363 NA
// 1362 NA
@@ -394,7 +972,7 @@ static int included_patches[] = {
// 1308 NA
// 1307 NA
// 1306 NA
- // 1305,
+ 1305,
1304,
// 1303 NA
// 1302 NA
@@ -426,7 +1004,7 @@ static int included_patches[] = {
1276,
// 1275 NA
// 1274 NA
- // 1273,
+ // 1273 NA
// 1272 NA
1271,
// 1270 NA
@@ -462,7 +1040,7 @@ static int included_patches[] = {
// 1240 NA
// 1239 NA
// 1238 NA
- // 1237,
+ 1237,
1236,
// 1235 NA
// 1234 NA
@@ -513,14 +1091,14 @@ static int included_patches[] = {
// 1189 NA
// 1188 NA
// 1187 NA
- // 1186,
+ // 1186 NA
// 1185 NA
// 1184 NA
// 1183 NA
// 1182 NA
1181,
1180,
- // 1179,
+ 1179,
1178,
// 1177 NA
// 1176 NA
@@ -552,7 +1130,7 @@ static int included_patches[] = {
1150,
1149,
// 1148 NA
- // 1147,
+ 1147,
// 1146 NA
// 1145 NA
1144,
@@ -625,7 +1203,7 @@ static int included_patches[] = {
// 1077 NA
1076,
1075,
- // 1074 NA,
+ // 1074 NA
// 1073 NA
1072,
1071,
@@ -668,7 +1246,7 @@ static int included_patches[] = {
1034,
// 1033 NA
1032,
- // 1031 NA,
+ // 1031 NA
1030,
1029,
// 1028 NA
@@ -689,15 +1267,15 @@ static int included_patches[] = {
1013,
// 1012 NA
// 1011 NA
- // 1010 NA,
+ // 1010 NA
// 1009 NA
// 1008 NA
1007,
1006,
- // 1005 NA,
- // 1004 NA,
- // 1003 NA,
- // 1002 NA,
+ // 1005 NA
+ // 1004 NA
+ // 1003 NA
+ // 1002 NA
1001,
1000,
// 999 NA
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 1298248f1e..350b54d595 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -3296,8 +3296,11 @@ void tabpage_move(int nr)
tabpage_T *tp;
tabpage_T *tp_dst;
- if (first_tabpage->tp_next == NULL)
+ assert(curtab != NULL);
+
+ if (first_tabpage->tp_next == NULL) {
return;
+ }
for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) {
++n;
@@ -3680,6 +3683,8 @@ win_T *buf_jump_open_tab(buf_T *buf)
return NULL;
}
+static int last_win_id = 0;
+
/*
* Allocate a window structure and link it in the window list when "hidden" is
* FALSE.
@@ -3692,6 +3697,7 @@ static win_T *win_alloc(win_T *after, int hidden)
win_T *new_wp = xcalloc(1, sizeof(win_T));
handle_register_window(new_wp);
win_alloc_lines(new_wp);
+ new_wp->w_id = ++last_win_id;
/* init w: variables */
new_wp->w_vars = dict_alloc();
@@ -5671,3 +5677,93 @@ static bool frame_check_width(frame_T *topfrp, int width)
}
return true;
}
+
+int win_getid(typval_T *argvars)
+{
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ return curwin->w_id;
+ }
+ int winnr = get_tv_number(&argvars[0]);
+ win_T *wp;
+ if (winnr > 0) {
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ wp = firstwin;
+ } else {
+ tabpage_T *tp;
+ int tabnr = get_tv_number(&argvars[1]);
+ for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
+ if (--tabnr == 0) {
+ break;
+ }
+ }
+ if (tp == NULL) {
+ return -1;
+ }
+ wp = tp->tp_firstwin;
+ }
+ for ( ; wp != NULL; wp = wp->w_next) {
+ if (--winnr == 0) {
+ return wp->w_id;
+ }
+ }
+ }
+ return 0;
+}
+
+int win_gotoid(typval_T *argvars)
+{
+ win_T *wp;
+ tabpage_T *tp;
+ int id = get_tv_number(&argvars[0]);
+
+ for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
+ for (wp = tp == curtab ? firstwin : tp->tp_firstwin;
+ wp != NULL; wp = wp->w_next) {
+ if (wp->w_id == id) {
+ goto_tabpage_win(tp, wp);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+void win_id2tabwin(typval_T *argvars, list_T *list)
+{
+ win_T *wp;
+ tabpage_T *tp;
+ int winnr = 1;
+ int tabnr = 1;
+ int id = get_tv_number(&argvars[0]);
+
+ for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
+ for (wp = tp == curtab ? firstwin : tp->tp_firstwin;
+ wp != NULL; wp = wp->w_next) {
+ if (wp->w_id == id) {
+ list_append_number(list, tabnr);
+ list_append_number(list, winnr);
+ return;
+ }
+ winnr++;
+ }
+ tabnr++;
+ winnr = 1;
+ }
+ list_append_number(list, 0);
+ list_append_number(list, 0);
+}
+
+int win_id2win(typval_T *argvars)
+{
+ win_T *wp;
+ int nr = 1;
+ int id = get_tv_number(&argvars[0]);
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next) {
+ if (wp->w_id == id) {
+ return nr;
+ }
+ nr++;
+ }
+ return 0;
+}