aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c5
-rw-r--r--src/nvim/eval.c65
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/mbyte.c4
-rw-r--r--src/nvim/mbyte.h1
-rw-r--r--src/nvim/quickfix.c12
-rw-r--r--src/nvim/screen.c86
-rw-r--r--src/nvim/tag.c46
-rw-r--r--src/nvim/testdir/Makefile1
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_autocmd.vim45
-rw-r--r--src/nvim/testdir/test_functions.vim23
-rw-r--r--src/nvim/testdir/test_join.vim35
-rw-r--r--src/nvim/testdir/test_stat.vim93
-rw-r--r--src/nvim/testdir/test_tagjump.vim28
-rw-r--r--src/nvim/testdir/test_taglist.vim7
-rw-r--r--src/nvim/testdir/test_user_func.vim96
18 files changed, 471 insertions, 86 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 3fadcc75bf..00d472b4c8 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -296,6 +296,11 @@ open_buffer (
}
save_file_ff(curbuf); // keep this fileformat
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
+ curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
+
/* require "!" to overwrite the file, because it wasn't read completely */
if (aborting())
curbuf->b_flags |= BF_READERR;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 22cb544f54..86f57ee5a2 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -9874,7 +9874,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
# endif
# ifdef S_ISSOCK
else if (S_ISSOCK(mode))
- t = "fifo";
+ t = "socket";
# endif
else
t = "other";
@@ -17218,6 +17218,69 @@ error:
return;
}
+// "trim({expr})" function
+static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ char buf1[NUMBUFLEN];
+ char buf2[NUMBUFLEN];
+ const char_u *head = (const char_u *)tv_get_string_buf_chk(&argvars[0], buf1);
+ const char_u *mask = NULL;
+ const char_u *tail;
+ const char_u *prev;
+ const char_u *p;
+ int c1;
+
+ rettv->v_type = VAR_STRING;
+ if (head == NULL) {
+ rettv->vval.v_string = NULL;
+ return;
+ }
+
+ if (argvars[1].v_type == VAR_STRING) {
+ mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2);
+ }
+
+ while (*head != NUL) {
+ c1 = PTR2CHAR(head);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == PTR2CHAR(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ MB_PTR_ADV(head);
+ }
+
+ for (tail = head + STRLEN(head); tail > head; tail = prev) {
+ prev = tail;
+ MB_PTR_BACK(head, prev);
+ c1 = PTR2CHAR(prev);
+ if (mask == NULL) {
+ if (c1 > ' ' && c1 != 0xa0) {
+ break;
+ }
+ } else {
+ for (p = mask; *p != NUL; MB_PTR_ADV(p)) {
+ if (c1 == PTR2CHAR(p)) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ }
+ rettv->vval.v_string = vim_strnsave(head, (int)(tail - head));
+}
+
/*
* "type(expr)" function
*/
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 801d2cc468..23959f348a 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -323,6 +323,7 @@ return {
tolower={args=1},
toupper={args=1},
tr={args=3},
+ trim={args={1,2}},
trunc={args=1, func="float_op_wrapper", data="&trunc"},
type={args=1},
undofile={args=1},
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 78fac5acf8..8b650d0d5b 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -4892,10 +4892,12 @@ buf_check_timestamp (
)) {
retval = 1;
- /* set b_mtime to stop further warnings (e.g., when executing
- * FileChangedShell autocmd) */
+ // set b_mtime to stop further warnings (e.g., when executing
+ // FileChangedShell autocmd)
if (!file_info_ok) {
- buf->b_mtime = 0;
+ // When 'autoread' is set we'll check the file again to see if it
+ // re-appears.
+ buf->b_mtime = buf->b_p_ar;
buf->b_orig_size = 0;
buf->b_orig_mode = 0;
} else {
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 15fe51cad1..94bf7fb985 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1829,8 +1829,8 @@ const char *mb_unescape(const char **const pp)
*/
bool mb_lefthalve(int row, int col)
{
- return (*mb_off2cells)(LineOffset[row] + col,
- LineOffset[row] + screen_Columns) > 1;
+ return utf_off2cells(LineOffset[row] + col,
+ LineOffset[row] + screen_Columns) > 1;
}
/*
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index a21c08c7fe..5f48e1783e 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -53,7 +53,6 @@ enum { MAX_MCO = 6 };
#define mb_ptr2cells utf_ptr2cells
#define mb_ptr2cells_len utf_ptr2cells_len
#define mb_char2cells utf_char2cells
-#define mb_off2cells utf_off2cells
#define mb_head_off utf_head_off
/// Flags for vimconv_T
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 4308c4e87e..a19e98725a 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -4541,11 +4541,6 @@ void ex_cbuffer(exarg_T *eap)
qf_info_T *qi = &ql_info;
const char *au_name = NULL;
- if (eap->cmdidx == CMD_lbuffer || eap->cmdidx == CMD_lgetbuffer
- || eap->cmdidx == CMD_laddbuffer) {
- qi = ll_get_or_alloc_list(curwin);
- }
-
switch (eap->cmdidx) {
case CMD_cbuffer:
au_name = "cbuffer";
@@ -4576,6 +4571,13 @@ void ex_cbuffer(exarg_T *eap)
}
}
+ // Must come after autocommands.
+ if (eap->cmdidx == CMD_lbuffer
+ || eap->cmdidx == CMD_lgetbuffer
+ || eap->cmdidx == CMD_laddbuffer) {
+ qi = ll_get_or_alloc_list(curwin);
+ }
+
if (*eap->arg == NUL)
buf = curbuf;
else if (*skipwhite(skipdigits(eap->arg)) == NUL)
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index bcfef89cc2..ec48bf5dcf 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -4232,26 +4232,19 @@ win_line (
/* Remember that the line wraps, used for modeless copy. */
LineWraps[screen_row - 1] = TRUE;
- /*
- * Special trick to make copy/paste of wrapped lines work with
- * xterm/screen: write an extra character beyond the end of
- * the line. This will work with all terminal types
- * (regardless of the xn,am settings).
- * Only do this if the cursor is on the current line
- * (something has been written in it).
- * Don't do this for double-width characters.
- * Don't do this for a window not at the right screen border.
- */
- if (!(has_mbyte
- && ((*mb_off2cells)(LineOffset[screen_row],
- LineOffset[screen_row] + screen_Columns)
- == 2
- || (*mb_off2cells)(LineOffset[screen_row - 1]
- + (int)Columns - 2,
- LineOffset[screen_row] + screen_Columns)
- == 2))
- ) {
- ui_add_linewrap(screen_row-1);
+ // Special trick to make copy/paste of wrapped lines work with
+ // xterm/screen: write an extra character beyond the end of
+ // the line. This will work with all terminal types
+ // (regardless of the xn,am settings).
+ // Only do this if the cursor is on the current line
+ // (something has been written in it).
+ // Don't do this for double-width characters.
+ // Don't do this for a window not at the right screen border.
+ if (utf_off2cells(LineOffset[screen_row],
+ LineOffset[screen_row] + screen_Columns) != 2
+ && utf_off2cells(LineOffset[screen_row - 1] + (int)Columns - 2,
+ LineOffset[screen_row] + screen_Columns) != 2) {
+ ui_add_linewrap(screen_row - 1);
}
}
@@ -4304,7 +4297,7 @@ static int char_needs_redraw(int off_from, int off_to, int cols)
return (cols > 0
&& ((schar_cmp(ScreenLines[off_from], ScreenLines[off_to])
|| ScreenAttrs[off_from] != ScreenAttrs[off_to]
- || ((*mb_off2cells)(off_from, off_from + cols) > 1
+ || (utf_off2cells(off_from, off_from + cols) > 1
&& schar_cmp(ScreenLines[off_from + 1],
ScreenLines[off_to + 1])))
|| p_wd < 0));
@@ -4330,15 +4323,11 @@ static void screen_line(int row, int coloff, int endcol,
unsigned max_off_to;
int col = 0;
int hl;
- int force = FALSE; /* force update rest of the line */
- int redraw_this /* bool: does character need redraw? */
- ;
- int redraw_next; /* redraw_this for next character */
- int clear_next = FALSE;
- int char_cells; /* 1: normal char */
- /* 2: occupies two display cells */
-# define CHAR_CELLS char_cells
-
+ bool redraw_this; // Does character need redraw?
+ bool redraw_next; // redraw_this for next character
+ bool clear_next = false;
+ int char_cells; // 1: normal char
+ // 2: occupies two display cells
int start_dirty = -1, end_dirty = 0;
/* Check for illegal row and col, just in case. */
@@ -4383,15 +4372,14 @@ static void screen_line(int row, int coloff, int endcol,
redraw_next = char_needs_redraw(off_from, off_to, endcol - col);
while (col < endcol) {
- if (has_mbyte && (col + 1 < endcol))
- char_cells = (*mb_off2cells)(off_from, max_off_from);
- else
- char_cells = 1;
-
+ char_cells = 1;
+ if (col + 1 < endcol) {
+ char_cells = utf_off2cells(off_from, max_off_from);
+ }
redraw_this = redraw_next;
- redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS,
- off_to + CHAR_CELLS, endcol - col - CHAR_CELLS);
-
+ redraw_next = char_needs_redraw(off_from + char_cells,
+ off_to + char_cells,
+ endcol - col - char_cells);
if (redraw_this) {
if (start_dirty == -1) {
@@ -4403,12 +4391,12 @@ static void screen_line(int row, int coloff, int endcol,
// the right halve of the old character.
// Also required when writing the right halve of a double-width
// char over the left halve of an existing one
- if (has_mbyte && col + char_cells == endcol
+ if (col + char_cells == endcol
&& ((char_cells == 1
- && (*mb_off2cells)(off_to, max_off_to) > 1)
+ && utf_off2cells(off_to, max_off_to) > 1)
|| (char_cells == 2
- && (*mb_off2cells)(off_to, max_off_to) == 1
- && (*mb_off2cells)(off_to + 1, max_off_to) > 1))) {
+ && utf_off2cells(off_to, max_off_to) == 1
+ && utf_off2cells(off_to + 1, max_off_to) > 1))) {
clear_next = true;
}
@@ -4425,9 +4413,9 @@ static void screen_line(int row, int coloff, int endcol,
}
}
- off_to += CHAR_CELLS;
- off_from += CHAR_CELLS;
- col += CHAR_CELLS;
+ off_to += char_cells;
+ off_from += char_cells;
+ col += char_cells;
}
if (clear_next) {
@@ -5396,15 +5384,15 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr)
// character with a one-cell character, need to clear the next
// cell. Also when overwriting the left halve of a two-cell char
// with the right halve of a two-cell char. Do this only once
- // (mb_off2cells() may return 2 on the right halve).
+ // (utf8_off2cells() may return 2 on the right halve).
if (clear_next_cell) {
clear_next_cell = false;
} else if ((len < 0 ? ptr[mbyte_blen] == NUL
: ptr + mbyte_blen >= text + len)
- && ((mbyte_cells == 1 && (*mb_off2cells)(off, max_off) > 1)
+ && ((mbyte_cells == 1 && utf_off2cells(off, max_off) > 1)
|| (mbyte_cells == 2
- && (*mb_off2cells)(off, max_off) == 1
- && (*mb_off2cells)(off + 1, max_off) > 1))) {
+ && utf_off2cells(off, max_off) == 1
+ && utf_off2cells(off + 1, max_off) > 1))) {
clear_next_cell = true;
}
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index c09a13edb1..2a980af2a2 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -952,12 +952,12 @@ void do_tags(exarg_T *eap)
continue;
msg_putchar('\n');
- sprintf((char *)IObuff, "%c%2d %2d %-15s %5ld ",
- i == tagstackidx ? '>' : ' ',
- i + 1,
- tagstack[i].cur_match + 1,
- tagstack[i].tagname,
- tagstack[i].fmark.mark.lnum);
+ vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5ld ",
+ i == tagstackidx ? '>' : ' ',
+ i + 1,
+ tagstack[i].cur_match + 1,
+ tagstack[i].tagname,
+ tagstack[i].fmark.mark.lnum);
msg_outtrans(IObuff);
msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
@@ -2217,6 +2217,16 @@ static bool test_for_static(tagptrs_T *tagp)
return FALSE;
}
+// Returns the length of a matching tag line.
+static size_t matching_line_len(const char_u *const lbuf)
+{
+ const char_u *p = lbuf + 1;
+
+ // does the same thing as parse_match()
+ p += STRLEN(p) + 1;
+ return (p - lbuf) + STRLEN(p);
+}
+
/*
* Parse a line from a matching tag. Does not change the line itself.
*
@@ -2300,11 +2310,10 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
*
* returns OK for success, NOTAGFILE when file not found, FAIL otherwise.
*/
-static int
-jumpto_tag (
- char_u *lbuf, /* line from the tags file for this tag */
- int forceit, /* :ta with ! */
- int keep_help /* keep help flag (FALSE for cscope) */
+static int jumpto_tag(
+ const char_u *lbuf_arg, // line from the tags file for this tag
+ int forceit, // :ta with !
+ int keep_help // keep help flag (FALSE for cscope)
)
{
int save_secure;
@@ -2312,7 +2321,6 @@ jumpto_tag (
bool save_p_ws;
int save_p_scs, save_p_ic;
linenr_T save_lnum;
- int csave = 0;
char_u *str;
char_u *pbuf; /* search pattern buffer */
char_u *pbuf_end;
@@ -2327,6 +2335,9 @@ jumpto_tag (
char_u *full_fname = NULL;
int old_KeyTyped = KeyTyped; /* getting the file may reset it */
const int l_g_do_tagpreview = g_do_tagpreview;
+ const size_t len = matching_line_len(lbuf_arg) + 1;
+ char_u *lbuf = xmalloc(len);
+ memmove(lbuf, lbuf_arg, len);
pbuf = xmalloc(LSIZE);
@@ -2336,8 +2347,7 @@ jumpto_tag (
goto erret;
}
- /* truncate the file name, so it can be used as a string */
- csave = *tagp.fname_end;
+ // truncate the file name, so it can be used as a string
*tagp.fname_end = NUL;
fname = tagp.fname;
@@ -2447,7 +2457,10 @@ jumpto_tag (
else
keep_help_flag = curbuf->b_help;
}
+
if (getfile_result == GETFILE_UNUSED) {
+ // Careful: getfile() may trigger autocommands and call jumpto_tag()
+ // recursively.
getfile_result = getfile(0, fname, NULL, true, (linenr_T)0, forceit);
}
keep_help_flag = false;
@@ -2605,9 +2618,8 @@ jumpto_tag (
}
erret:
- g_do_tagpreview = 0; /* For next time */
- if (tagp.fname_end != NULL)
- *tagp.fname_end = csave;
+ g_do_tagpreview = 0; // For next time
+ xfree(lbuf);
xfree(pbuf);
xfree(tofree_fname);
xfree(full_fname);
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 361db47fc7..1e3dc04049 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -114,6 +114,7 @@ NEW_TESTS ?= \
test_timers.res \
test_undo.res \
test_usercommands.res \
+ test_user_func.res \
test_vimscript.res \
test_visual.res \
test_winbuf_close.res \
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 238dbe8d90..ace4b377cb 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -19,6 +19,7 @@ source test_functions.vim
source test_ga.vim
source test_global.vim
source test_goto.vim
+source test_join.vim
source test_jumps.vim
source test_fileformat.vim
source test_filetype.vim
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index e4ab3ccea8..b1502eff89 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1,5 +1,7 @@
" Tests for autocommands
+source shared.vim
+
func! s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
if bufloaded(bnr) && bufnr('%') != bnr
@@ -248,6 +250,23 @@ func Test_augroup_warning()
au! VimEnter
endfunc
+func Test_BufReadCmdHelp()
+ " This used to cause access to free memory
+ au BufReadCmd * e +h
+ help
+
+ au! BufReadCmd
+endfunc
+
+func Test_BufReadCmdHelpJump()
+ " This used to cause access to free memory
+ au BufReadCmd * e +h{
+ " } to fix highlighting
+ call assert_fails('help', 'E434:')
+
+ au! BufReadCmd
+endfunc
+
func Test_augroup_deleted()
" This caused a crash before E936 was introduced
augroup x
@@ -1165,6 +1184,13 @@ func Test_nocatch_wipe_dummy_buffer()
au!
endfunc
+func Test_wipe_cbuffer()
+ sv x
+ au * * bw
+ lb
+ au!
+endfunc
+
" Test TextChangedI and TextChangedP
func Test_ChangedP()
" Nvim does not support test_override().
@@ -1245,3 +1271,22 @@ func Test_TextChangedI_with_setline()
call test_override('starting', 0)
bwipe!
endfunc
+
+func Test_Changed_FirstTime()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+ " Prepare file for TextChanged event.
+ call writefile([''], 'Xchanged.txt')
+ let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
+ call assert_equal('running', term_getstatus(buf))
+ " It's only adding autocmd, so that no event occurs.
+ call term_sendkeys(buf, ":au! TextChanged <buffer> call writefile(['No'], 'Xchanged.txt')\<cr>")
+ call term_sendkeys(buf, "\<C-\\>\<C-N>:qa!\<cr>")
+ call WaitFor({-> term_getstatus(buf) == 'finished'})
+ call assert_equal([''], readfile('Xchanged.txt'))
+
+ " clean up
+ call delete('Xchanged.txt')
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 6d0a6b9d5e..e2a035b0b2 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -926,3 +926,26 @@ func Test_redo_in_nested_functions()
delfunc Operator
delfunc Apply
endfunc
+
+func Test_trim()
+ call assert_equal("Testing", trim(" \t\r\r\x0BTesting \t\n\r\n\t\x0B\x0B"))
+ call assert_equal("Testing", trim(" \t \r\r\n\n\x0BTesting \t\n\r\n\t\x0B\x0B"))
+ call assert_equal("RESERVE", trim("xyz \twwRESERVEzyww \t\t", " wxyz\t"))
+ call assert_equal("wRE \tSERVEzyww", trim("wRE \tSERVEzyww"))
+ call assert_equal("abcd\t xxxx tail", trim(" \tabcd\t xxxx tail"))
+ call assert_equal("\tabcd\t xxxx tail", trim(" \tabcd\t xxxx tail", " "))
+ call assert_equal(" \tabcd\t xxxx tail", trim(" \tabcd\t xxxx tail", "abx"))
+ call assert_equal("RESERVE", trim("你RESERVE好", "你好"))
+ call assert_equal("您R E SER V E早", trim("你好您R E SER V E早好你你", "你好"))
+ call assert_equal("你好您R E SER V E早好你你", trim(" \n\r\r 你好您R E SER V E早好你你 \t \x0B", ))
+ call assert_equal("您R E SER V E早好你你 \t \x0B", trim(" 你好您R E SER V E早好你你 \t \x0B", " 你好"))
+ call assert_equal("您R E SER V E早好你你 \t \x0B", trim(" tteesstttt你好您R E SER V E早好你你 \t \x0B ttestt", " 你好tes"))
+ call assert_equal("您R E SER V E早好你你 \t \x0B", trim(" tteesstttt你好您R E SER V E早好你你 \t \x0B ttestt", " 你你你好好好tttsses"))
+ call assert_equal("留下", trim("这些些不要这些留下这些", "这些不要"))
+ call assert_equal("", trim("", ""))
+ call assert_equal("a", trim("a", ""))
+ call assert_equal("", trim("", "a"))
+
+ let chars = join(map(range(1, 0x20) + [0xa0], {n -> nr2char(n)}), '')
+ call assert_equal("x", trim(chars . "x" . chars))
+endfunc
diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim
new file mode 100644
index 0000000000..1c97414164
--- /dev/null
+++ b/src/nvim/testdir/test_join.vim
@@ -0,0 +1,35 @@
+" Test for joining lines.
+
+func Test_join_with_count()
+ new
+ call setline(1, ['one', 'two', 'three', 'four'])
+ normal J
+ call assert_equal('one two', getline(1))
+ %del
+ call setline(1, ['one', 'two', 'three', 'four'])
+ normal 10J
+ call assert_equal('one two three four', getline(1))
+ quit!
+endfunc
+
+" Tests for setting the '[,'] marks when joining lines.
+func Test_join_marks()
+ enew
+ call append(0, [
+ \ "\t\tO sodales, ludite, vos qui",
+ \ "attamen consulite per voster honur. Tua pulchra " .
+ \ "facies me fay planszer milies",
+ \ "",
+ \ "This line.",
+ \ "Should be joined with the next line",
+ \ "and with this line"])
+
+ normal gg0gqj
+ call assert_equal([0, 1, 1, 0], getpos("'["))
+ call assert_equal([0, 2, 1, 0], getpos("']"))
+
+ /^This line/;'}-join
+ call assert_equal([0, 4, 11, 0], getpos("'["))
+ call assert_equal([0, 4, 67, 0], getpos("']"))
+ enew!
+endfunc
diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim
index 0a09130b0c..c276df0a92 100644
--- a/src/nvim/testdir/test_stat.vim
+++ b/src/nvim/testdir/test_stat.vim
@@ -43,7 +43,16 @@ func Test_existent_directory()
call assert_equal(0, getfsize(dname))
call assert_equal('dir', getftype(dname))
- call assert_equal('rwx', getfperm(dname)[0:2])
+ call assert_equal(has('win32') ? 'rw-' : 'rwx', getfperm(dname)[0:2])
+endfunc
+
+func SleepForTimestamp()
+ " FAT has a granularity of 2 seconds, otherwise it's usually 1 second
+ if has('win32')
+ sleep 2
+ else
+ sleep 2
+ endif
endfunc
func Test_checktime()
@@ -53,12 +62,7 @@ func Test_checktime()
call writefile(fl, fname)
set autoread
exec 'e' fname
- " FAT has a granularity of 2 seconds, otherwise it's usually 1 second
- if has('win32')
- sleep 2
- else
- sleep 2
- endif
+ call SleepForTimestamp()
let fl = readfile(fname)
let fl[0] .= ' - checktime'
call writefile(fl, fname)
@@ -68,6 +72,46 @@ func Test_checktime()
call delete(fname)
endfunc
+func Test_autoread_file_deleted()
+ new Xautoread
+ set autoread
+ call setline(1, 'original')
+ w!
+
+ call SleepForTimestamp()
+ if has('win32')
+ silent !echo changed > Xautoread
+ else
+ silent !echo 'changed' > Xautoread
+ endif
+ checktime
+ call assert_equal('changed', trim(getline(1)))
+
+ call SleepForTimestamp()
+ messages clear
+ if has('win32')
+ silent !del Xautoread
+ else
+ silent !rm Xautoread
+ endif
+ checktime
+ call assert_match('E211:', execute('messages'))
+ call assert_equal('changed', trim(getline(1)))
+
+ call SleepForTimestamp()
+ if has('win32')
+ silent !echo recreated > Xautoread
+ else
+ silent !echo 'recreated' > Xautoread
+ endif
+ checktime
+ call assert_equal('recreated', trim(getline(1)))
+
+ call delete('Xautoread')
+ bwipe!
+endfunc
+
+
func Test_nonexistent_file()
let fname = 'Xtest.tmp'
@@ -78,6 +122,41 @@ func Test_nonexistent_file()
call assert_equal('', getfperm(fname))
endfunc
+func Test_getftype()
+ call assert_equal('file', getftype(v:progpath))
+ call assert_equal('dir', getftype('.'))
+
+ if !has('unix')
+ return
+ endif
+
+ silent !ln -s Xfile Xlink
+ call assert_equal('link', getftype('Xlink'))
+ call delete('Xlink')
+
+ if executable('mkfifo')
+ silent !mkfifo Xfifo
+ call assert_equal('fifo', getftype('Xfifo'))
+ call delete('Xfifo')
+ endif
+
+ for cdevfile in systemlist('find /dev -type c -maxdepth 2 2>/dev/null')
+ call assert_equal('cdev', getftype(cdevfile))
+ endfor
+
+ for bdevfile in systemlist('find /dev -type b -maxdepth 2 2>/dev/null')
+ call assert_equal('bdev', getftype(bdevfile))
+ endfor
+
+ " The /run/ directory typically contains socket files.
+ " If it does not, test won't fail but will not test socket files.
+ for socketfile in systemlist('find /run -type s -maxdepth 2 2>/dev/null')
+ call assert_equal('socket', getftype(socketfile))
+ endfor
+
+ " TODO: file type 'other' is not tested. How can we test it?
+endfunc
+
func Test_win32_symlink_dir()
" On Windows, non-admin users cannot create symlinks.
" So we use an existing symlink for this test.
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 268a153077..f9bd8b5246 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -230,4 +230,32 @@ func Test_tag_file_encoding()
call delete('Xtags1')
endfunc
+func Test_tagjump_etags()
+ if !has('emacs_tags')
+ return
+ endif
+ call writefile([
+ \ "void foo() {}",
+ \ "int main(int argc, char **argv)",
+ \ "{",
+ \ "\tfoo();",
+ \ "\treturn 0;",
+ \ "}",
+ \ ], 'Xmain.c')
+
+ call writefile([
+ \ "\x0c",
+ \ "Xmain.c,64",
+ \ "void foo() {}\x7ffoo\x011,0",
+ \ "int main(int argc, char **argv)\x7fmain\x012,14",
+ \ ], 'Xtags')
+ set tags=Xtags
+ ta foo
+ call assert_equal('void foo() {}', getline('.'))
+
+ call delete('Xtags')
+ call delete('Xmain.c')
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim
index 2d1557ebd9..3ad2025915 100644
--- a/src/nvim/testdir/test_taglist.vim
+++ b/src/nvim/testdir/test_taglist.vim
@@ -1,4 +1,4 @@
-" test 'taglist' function
+" test 'taglist' function and :tags command
func Test_taglist()
call writefile([
@@ -56,3 +56,8 @@ func Test_taglist_ctags_etags()
call delete('Xtags')
endfunc
+
+func Test_tags_too_long()
+ call assert_fails('tag ' . repeat('x', 1020), 'E426')
+ tags
+endfunc
diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim
new file mode 100644
index 0000000000..e7a3701386
--- /dev/null
+++ b/src/nvim/testdir/test_user_func.vim
@@ -0,0 +1,96 @@
+" Test for user functions.
+" Also test an <expr> mapping calling a function.
+" Also test that a builtin function cannot be replaced.
+" Also test for regression when calling arbitrary expression.
+
+func Table(title, ...)
+ let ret = a:title
+ let idx = 1
+ while idx <= a:0
+ exe "let ret = ret . a:" . idx
+ let idx = idx + 1
+ endwhile
+ return ret
+endfunc
+
+func Compute(n1, n2, divname)
+ if a:n2 == 0
+ return "fail"
+ endif
+ exe "let g:" . a:divname . " = ". a:n1 / a:n2
+ return "ok"
+endfunc
+
+func Expr1()
+ silent! normal! v
+ return "111"
+endfunc
+
+func Expr2()
+ call search('XX', 'b')
+ return "222"
+endfunc
+
+func ListItem()
+ let g:counter += 1
+ return g:counter . '. '
+endfunc
+
+func ListReset()
+ let g:counter = 0
+ return ''
+endfunc
+
+func FuncWithRef(a)
+ unlet g:FuncRef
+ return a:a
+endfunc
+
+func Test_user_func()
+ let g:FuncRef=function("FuncWithRef")
+ let g:counter = 0
+ inoremap <expr> ( ListItem()
+ inoremap <expr> [ ListReset()
+ imap <expr> + Expr1()
+ imap <expr> * Expr2()
+ let g:retval = "nop"
+
+ call assert_equal('xxx4asdf', Table("xxx", 4, "asdf"))
+ call assert_equal('fail', Compute(45, 0, "retval"))
+ call assert_equal('nop', g:retval)
+ call assert_equal('ok', Compute(45, 5, "retval"))
+ call assert_equal(9, g:retval)
+ call assert_equal(333, g:FuncRef(333))
+
+ enew
+
+ normal oXX+-XX
+ call assert_equal('XX111-XX', getline('.'))
+ normal o---*---
+ call assert_equal('---222---', getline('.'))
+ normal o(one
+ call assert_equal('1. one', getline('.'))
+ normal o(two
+ call assert_equal('2. two', getline('.'))
+ normal o[(one again
+ call assert_equal('1. one again', getline('.'))
+
+ call assert_equal(3, max([1, 2, 3]))
+ call assert_fails("call extend(g:, {'max': function('min')})", 'E704')
+ call assert_equal(3, max([1, 2, 3]))
+
+ " Regression: the first line below used to throw ?E110: Missing ')'?
+ " Second is here just to prove that this line is correct when not skipping
+ " rhs of &&.
+ call assert_equal(0, (0 && (function('tr'))(1, 2, 3)))
+ call assert_equal(1, (1 && (function('tr'))(1, 2, 3)))
+
+ delfunc Table
+ delfunc Compute
+ delfunc Expr1
+ delfunc Expr2
+ delfunc ListItem
+ delfunc ListReset
+ unlet g:retval g:counter
+ enew!
+endfunc