aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/CMakeLists.txt5
-rw-r--r--config/config.h.in1
-rw-r--r--runtime/autoload/health/provider.vim13
-rw-r--r--runtime/autoload/provider/clipboard.vim4
-rw-r--r--runtime/doc/eval.txt22
-rw-r--r--runtime/optwin.vim3
-rw-r--r--snap/snapcraft.yaml41
-rw-r--r--src/nvim/api/ui.c4
-rw-r--r--src/nvim/buffer.c15
-rw-r--r--src/nvim/diff.c5
-rw-r--r--src/nvim/edit.c16
-rw-r--r--src/nvim/eval.c16
-rw-r--r--src/nvim/event/loop.c3
-rw-r--r--src/nvim/ex_getln.c15
-rw-r--r--src/nvim/fileio.c16
-rw-r--r--src/nvim/macros.h14
-rw-r--r--src/nvim/mouse.c117
-rw-r--r--src/nvim/move.c9
-rw-r--r--src/nvim/normal.c59
-rw-r--r--src/nvim/ops.c45
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/os/shell.c9
-rw-r--r--src/nvim/path.c8
-rw-r--r--src/nvim/popupmnu.c2
-rw-r--r--src/nvim/search.c34
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/syntax.c5
-rw-r--r--src/nvim/testdir/Makefile5
-rw-r--r--src/nvim/testdir/shared.vim8
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_findfile.vim25
-rw-r--r--src/nvim/testdir/test_normal.vim2
-rw-r--r--src/nvim/testdir/test_number.vim254
-rw-r--r--src/nvim/testdir/test_options.vim11
-rw-r--r--src/nvim/testdir/test_syntax.vim10
-rw-r--r--src/nvim/tui/tui.c27
-rw-r--r--src/nvim/ui.c10
-rw-r--r--src/nvim/ui_bridge.c3
-rw-r--r--src/nvim/undo.c8
-rw-r--r--src/nvim/version.c57
-rw-r--r--test/functional/core/job_spec.lua3
-rw-r--r--test/functional/eval/buf_functions_spec.lua5
-rw-r--r--test/functional/eval/fnamemodify_spec.lua39
-rw-r--r--test/functional/eval/has_spec.lua6
-rw-r--r--test/functional/eval/hostname_spec.lua5
-rw-r--r--test/functional/eval/system_spec.lua1
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua3
-rw-r--r--test/functional/ex_cmds/mkview_spec.lua2
-rw-r--r--test/functional/helpers.lua6
-rw-r--r--test/functional/legacy/003_cindent_spec.lua3
-rw-r--r--test/functional/legacy/008_autocommands_spec.lua10
-rw-r--r--test/functional/legacy/089_number_relnumber_findfile_spec.lua116
-rw-r--r--test/functional/legacy/096_location_list_spec.lua14
-rw-r--r--test/functional/legacy/fnamemodify_spec.lua22
-rw-r--r--test/functional/normal/langmap_spec.lua280
-rw-r--r--test/functional/plugin/shada_spec.lua11
-rw-r--r--test/functional/ui/mouse_spec.lua439
-rw-r--r--test/functional/ui/searchhl_spec.lua3
58 files changed, 1360 insertions, 514 deletions
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt
index c3643db205..63cb3cc0d6 100644
--- a/config/CMakeLists.txt
+++ b/config/CMakeLists.txt
@@ -12,6 +12,11 @@ check_type_size("size_t" SIZEOF_SIZE_T)
check_type_size("long long" SIZEOF_LONG_LONG)
check_type_size("void *" SIZEOF_VOID_PTR)
+if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft")
+ # Windows Subsystem for Linux
+ set(HAVE_WSL 1)
+endif()
+
check_symbol_exists(_NSGetEnviron crt_externs.h HAVE__NSGETENVIRON)
# Headers
diff --git a/config/config.h.in b/config/config.h.in
index 410d9cc825..106013425d 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -48,6 +48,7 @@
#cmakedefine HAVE_UTIME_H
#cmakedefine HAVE_UTIMES
#cmakedefine HAVE_WORKING_LIBINTL
+#cmakedefine HAVE_WSL
#cmakedefine UNIX
#cmakedefine USE_FNAME_CASE
#cmakedefine HAVE_SYS_UIO_H
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index d1239db605..ae106095f9 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -13,6 +13,12 @@ function! s:normalize_path(s) abort
return substitute(substitute(a:s, '\', '/', 'g'), '/\./\|/\+', '/', 'g')
endfunction
+" Returns TRUE if `cmd` exits with success, else FALSE.
+function! s:cmd_ok(cmd) abort
+ call system(a:cmd)
+ return v:shell_error == 0
+endfunction
+
" Simple version comparison.
function! s:version_cmp(a, b) abort
let a = split(a:a, '\.', 0)
@@ -120,6 +126,13 @@ endfunction
function! s:check_clipboard() abort
call health#report_start('Clipboard (optional)')
+ if !empty($TMUX) && executable('tmux') && executable('pbcopy') && !s:cmd_ok('pbcopy')
+ let tmux_version = matchstr(system('tmux -V'), '\d\+\.\d\+')
+ call health#report_error('pbcopy does not work with tmux version: '.tmux_version,
+ \ ['Install tmux 2.6+. https://superuser.com/q/231130',
+ \ 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233'])
+ endif
+
let clipboard_tool = provider#clipboard#Executable()
if exists('g:clipboard') && empty(clipboard_tool)
call health#report_error(
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index e5a6e4748a..9e2e6046f8 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -26,7 +26,7 @@ let s:selections = { '*': s:selection, '+': copy(s:selection) }
function! s:try_cmd(cmd, ...) abort
let argv = split(a:cmd, " ")
- let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1)
+ let out = systemlist(argv, (a:0 ? a:1 : ['']), 1)
if v:shell_error
if !exists('s:did_error_try_cmd')
echohl WarningMsg
@@ -64,7 +64,7 @@ function! provider#clipboard#Executable() abort
let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null })
let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0)
return get(g:clipboard, 'name', 'g:clipboard')
- elseif has('mac') && executable('pbcopy')
+ elseif has('mac') && executable('pbcopy') && s:cmd_ok('pbcopy')
let s:copy['+'] = 'pbcopy'
let s:paste['+'] = 'pbpaste'
let s:copy['*'] = s:copy['+']
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 8771604c89..767fc133d8 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -7610,12 +7610,21 @@ synconcealed({lnum}, {col}) *synconcealed()*
is 1, the second item contains the text which will be
displayed in place of the concealed text, depending on the
current setting of 'conceallevel' and 'listchars'.
- 3. The third and final item in the list is a unique number
- representing the specific syntax region matched. This
- allows detection of the beginning of a new concealable
- region if there are two consecutive regions with the same
- replacement character. For an example use see
- $VIMRUNTIME/syntax/2html.vim .
+ 3. The third and final item in the list is a number
+ representing the specific syntax region matched in the
+ line. When the character is not concealed the value is
+ zero. This allows detection of the beginning of a new
+ concealable region if there are two consecutive regions
+ with the same replacement character. For an example, if
+ the text is "123456" and both "23" and "45" are concealed
+ and replace by the character "X", then:
+ call returns ~
+ synconcealed(lnum, 1) [0, '', 0]
+ synconcealed(lnum, 2) [1, 'X', 1]
+ synconcealed(lnum, 3) [1, 'X', 1]
+ synconcealed(lnum, 4) [1, 'X', 2]
+ synconcealed(lnum, 5) [1, 'X', 2]
+ synconcealed(lnum, 6) [0, '', 0]
synstack({lnum}, {col}) *synstack()*
@@ -8445,6 +8454,7 @@ win32 Windows version of Vim (32 or 64 bit).
winaltkeys Compiled with 'winaltkeys' option.
windows Compiled with support for more than one window.
writebackup Compiled with 'writebackup' default on.
+wsl WSL (Windows Subsystem for Linux) version of Vim.
*string-match*
Matching a pattern in a String
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 8f04e495e5..13d0db0390 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1087,6 +1087,9 @@ if has("quickfix")
call <SID>OptionG("gp", &gp)
call append("$", "grepformat\tlist of formats for output of 'grepprg'")
call <SID>OptionG("gfm", &gfm)
+ call append("$", "makeencoding\tencoding of the \":make\" and \":grep\" output")
+ call append("$", "\t(global or local to buffer)")
+ call <SID>OptionG("menc", &menc)
endif
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
new file mode 100644
index 0000000000..81ffb9adf3
--- /dev/null
+++ b/snap/snapcraft.yaml
@@ -0,0 +1,41 @@
+name: neovim
+version: git
+summary: Vim-fork focused on extensibility and agility.
+description: |
+ Neovim is a project that seeks to aggressively refactor Vim in order to:
+
+ Simplify maintenance and encourage contributions
+ Split the work between multiple developers
+ Enable the implementation of new/modern user interfaces without any modifications to the core source
+ Improve extensibility with a new plugin architecture
+ For lots more details, see the wiki!
+confinement: classic
+
+apps:
+ neovim:
+ command: usr/local/bin/nvim
+ plugs: [network, network-bind, x11]
+ environment:
+ HOME: /home/$USER
+ VIM: $SNAP/usr/local/share/nvim/runtime
+
+parts:
+ neovim:
+ source: .
+ plugin: make
+ make-parameters:
+ - CMAKE_BUILD_TYPE=Release
+ build-packages:
+ - ninja-build
+ - libtool
+ - libtool-bin
+ - autoconf
+ - automake
+ - cmake
+ - g++
+ - pkg-config
+ - unzip
+ snap:
+ - usr/local/bin
+ - usr/local/share/nvim
+ - -usr/local/share/man
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 35508fde6b..760c95eb5b 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -43,10 +43,10 @@ void remote_ui_disconnect(uint64_t channel_id)
return;
}
UIData *data = ui->data;
- // destroy pending screen updates
- api_free_array(data->buffer);
+ api_free_array(data->buffer); // Destroy pending screen updates.
pmap_del(uint64_t)(connected_uis, channel_id);
xfree(ui->data);
+ ui->data = NULL; // Flag UI as "stopped".
ui_detach_impl(ui);
xfree(ui);
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6f0c78fde4..e5da5b835b 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -129,7 +129,7 @@ read_buffer(
if (read_stdin) {
// Set or reset 'modified' before executing autocommands, so that
// it can be changed there.
- if (!readonlymode && !bufempty()) {
+ if (!readonlymode && !BUFEMPTY()) {
changed();
} else if (retval != FAIL) {
unchanged(curbuf, false);
@@ -1616,7 +1616,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
&& curbuf != NULL
&& curbuf->b_ffname == NULL
&& curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || bufempty())) {
+ && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) {
buf = curbuf;
/* It's like this buffer is deleted. Watch out for autocommands that
* change curbuf! If that happens, allocate a new buffer anyway. */
@@ -1872,7 +1872,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
// current buffer isn't empty: open new tab or window
if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
- && !bufempty()) {
+ && !BUFEMPTY()) {
if (swb_flags & SWB_NEWTAB) {
tabpage_new();
} else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
@@ -4462,11 +4462,12 @@ do_arg_all (
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
- /* ":drop all" should re-use an empty window to avoid "--remote-tab"
- * leaving an empty tab page when executed locally. */
- if (keep_tabs && bufempty() && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed)
+ // ":drop all" should re-use an empty window to avoid "--remote-tab"
+ // leaving an empty tab page when executed locally.
+ if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1
+ && curbuf->b_ffname == NULL && !curbuf->b_changed) {
use_firstwin = TRUE;
+ }
for (i = 0; i < count && i < opened_len && !got_int; ++i) {
if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index a32ac645e3..c2ab57a59b 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -941,9 +941,6 @@ void ex_diffpatch(exarg_T *eap)
}
#endif
- // patch probably has written over the screen
- redraw_later(CLEAR);
-
// Delete any .orig or .rej file created.
STRCPY(buf, tmp_new);
STRCAT(buf, ".orig");
@@ -2270,7 +2267,7 @@ void ex_diffgetput(exarg_T *eap)
}
}
- buf_empty = bufempty();
+ buf_empty = BUFEMPTY();
added = 0;
for (i = 0; i < count; ++i) {
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 649357c7ea..e67ac7d49f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -6907,7 +6907,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
if (try_match && *look == keytyped) {
return true;
}
- look++;
+ if (*look != NUL) {
+ look++;
+ }
}
/*
@@ -7473,13 +7475,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
int oldState;
int cpc[MAX_MCO]; /* composing characters */
- /*
- * can't delete anything in an empty file
- * can't backup past first character in buffer
- * can't backup past starting point unless 'backspace' > 1
- * can backup to a previous line if 'backspace' == 0
- */
- if (bufempty()
+ // can't delete anything in an empty file
+ // can't backup past first character in buffer
+ // can't backup past starting point unless 'backspace' > 1
+ // can backup to a previous line if 'backspace' == 0
+ if (BUFEMPTY()
|| (!revins_on
&& ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0)
|| (!can_bs(BS_START)
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 07a9e9286d..1d483eee18 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1279,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc,
char *call_func_retstr(const char *const func, const int argc,
const char_u *const *const argv,
const bool safe)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
+ FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
typval_T rettv;
// All arguments are passed as strings, no conversion to number.
@@ -10671,6 +10671,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"windows",
"winaltkeys",
"writebackup",
+#if defined(HAVE_WSL)
+ "wsl",
+#endif
"nvim",
};
@@ -10730,6 +10733,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = true;
}
+ if (STRICMP(name, "ruby") == 0 && n == true) {
+ char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true);
+ if (rubyhost) {
+ if (*rubyhost == NUL) {
+ // Invalid rubyhost executable. Gem is probably not installed.
+ n = false;
+ }
+ xfree(rubyhost);
+ }
+ }
+
rettv->vval.v_number = n;
}
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index 55ef0261d9..d92464f17b 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -33,6 +33,9 @@ void loop_init(Loop *loop, void *data)
loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag
}
+/// Processes one `Loop.uv` event (at most).
+/// Processes all `Loop.fast_events` events.
+///
/// @returns true if `ms` timeout was reached
bool loop_poll_events(Loop *loop, int ms)
{
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a2f8994403..ba51f18518 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1870,8 +1870,7 @@ static int command_line_changed(CommandLineState *s)
end_pos = curwin->w_cursor; // shutup gcc 4
}
-
- // Disable 'hlsearch' highlighting if the pattern matches8.0.1304
+ // Disable 'hlsearch' highlighting if the pattern matches
// everything. Avoids a flash when typing "foo\|".
if (empty_pattern(ccline.cmdbuff)) {
SET_NO_HLSEARCH(true);
@@ -4109,12 +4108,12 @@ static int showmatches(expand_T *xp, int wildmenu)
msg_start(); /* prepare for paging */
}
- if (got_int)
- got_int = FALSE; /* only int. the completion, not the cmd line */
- else if (wildmenu)
- win_redr_status_matches(xp, num_files, files_found, 0, showtail);
- else {
- /* find the length of the longest file name */
+ if (got_int) {
+ got_int = false; // only int. the completion, not the cmd line
+ } else if (wildmenu) {
+ win_redr_status_matches(xp, num_files, files_found, -1, showtail);
+ } else {
+ // find the length of the longest file name
maxlen = 0;
for (i = 0; i < num_files; ++i) {
if (!showtail && (xp->xp_context == EXPAND_FILES
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 1f4cd22754..44d74c92cd 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5088,14 +5088,12 @@ void buf_reload(buf_T *buf, int orig_mode)
flags |= READ_KEEP_UNDO;
}
- /*
- * To behave like when a new file is edited (matters for
- * BufReadPost autocommands) we first need to delete the current
- * buffer contents. But if reading the file fails we should keep
- * the old contents. Can't use memory only, the file might be
- * too big. Use a hidden buffer to move the buffer contents to.
- */
- if (bufempty() || saved == FAIL) {
+ // To behave like when a new file is edited (matters for
+ // BufReadPost autocommands) we first need to delete the current
+ // buffer contents. But if reading the file fails we should keep
+ // the old contents. Can't use memory only, the file might be
+ // too big. Use a hidden buffer to move the buffer contents to.
+ if (BUFEMPTY() || saved == FAIL) {
savebuf = NULL;
} else {
// Allocate a buffer without putting it in the buffer list.
@@ -5128,7 +5126,7 @@ void buf_reload(buf_T *buf, int orig_mode)
if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) {
// Put the text back from the save buffer. First
// delete any lines that readfile() added.
- while (!bufempty()) {
+ while (!BUFEMPTY()) {
if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) {
break;
}
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index a98c1e05a0..61834c6499 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -28,15 +28,11 @@
/// @return `s, sizeof(s) - 1`
#define S_LEN(s) (s), (sizeof(s) - 1)
-/*
- * lineempty() - return TRUE if the line is empty
- */
-#define lineempty(p) (*ml_get(p) == NUL)
+/// LINEEMPTY() - return TRUE if the line is empty
+#define LINEEMPTY(p) (*ml_get(p) == NUL)
-/*
- * bufempty() - return TRUE if the current buffer is empty
- */
-#define bufempty() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \
+/// BUFEMPTY() - return TRUE if the current buffer is empty
+#define BUFEMPTY() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \
NUL)
/*
@@ -75,7 +71,7 @@
do { \
if (*p_langmap \
&& (condition) \
- && (p_lrm || KeyTyped) \
+ && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \
&& !KeyStuffed \
&& (c) >= 0) \
{ \
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index d908a022f1..6f636f643a 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -608,8 +608,7 @@ 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.
+/// Adjusts the clicked column position when 'conceallevel' > 0
static int mouse_adjust_click(win_T *wp, int row, int col)
{
if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0
@@ -617,64 +616,102 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
return col;
}
- int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum));
- int vend = getviscol2(end, 0);
+ // `col` is the position within the current line that is highlighted by the
+ // cursor without consideration for concealed characters. The current line is
+ // scanned *up to* `col`, nudging it left or right when concealed characters
+ // are encountered.
+ //
+ // chartabsize() is used to keep track of the virtual column position relative
+ // to the line's bytes. For example: if col == 9 and the line starts with a
+ // tab that's 8 columns wide, we would want the cursor to be highlighting the
+ // second byte, not the ninth.
+
+ linenr_T lnum = wp->w_cursor.lnum;
+ char_u *line = ml_get(lnum);
+ char_u *ptr = line;
+ char_u *ptr_end = line;
+ char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
- if (col >= vend) {
- return col;
+ // Find the offset where scanning should begin.
+ int offset = wp->w_leftcol;
+ if (row > 0) {
+ offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) -
+ wp->w_leftcol + wp->w_skipcol);
}
- int i = wp->w_leftcol;
+ int vcol;
+
+ if (offset) {
+ // Skip everything up to an offset since nvim takes care of displaying the
+ // correct portion of the line when horizontally scrolling.
+ // When 'wrap' is enabled, only the row (of the wrapped line) needs to be
+ // checked for concealed characters.
+ vcol = 0;
+ while (vcol < offset && *ptr != NUL) {
+ vcol += chartabsize(ptr, vcol);
+ ptr += utfc_ptr2len(ptr);
+ }
- if (row > 0) {
- i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp)
- - wp->w_leftcol) + wp->w_skipcol;
+ ptr_row_offset = ptr;
}
- int start_col = i;
- int matchid;
- int last_matchid;
- int bcol = end - (vend - col);
+ // Align `ptr_end` with `col`
+ vcol = offset;
+ ptr_end = ptr_row_offset;
+ while (vcol < col && *ptr_end != NUL) {
+ vcol += chartabsize(ptr_end, vcol);
+ ptr_end += utfc_ptr2len(ptr_end);
+ }
- while (i < bcol) {
- matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i);
+ int matchid;
+ int prev_matchid;
+ int nudge = 0;
+ int cwidth = 0;
+
+ vcol = offset;
+
+#define incr() nudge++; ptr_end += utfc_ptr2len(ptr_end)
+#define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end)
+
+ while (ptr < ptr_end && *ptr != NUL) {
+ cwidth = chartabsize(ptr, vcol);
+ vcol += cwidth;
+ if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
+ // A tab will "absorb" any previous adjustments.
+ cwidth = MIN(cwidth, nudge);
+ while (cwidth > 0) {
+ decr();
+ cwidth--;
+ }
+ }
+ matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
if (matchid != 0) {
if (wp->w_p_cole == 3) {
- bcol++;
+ incr();
} 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))) {
+ if (!(row > 0 && ptr == ptr_row_offset)
+ && (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--;
+ decr();
}
- last_matchid = matchid;
+ prev_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);
+ while (prev_matchid == matchid && *ptr != NUL) {
+ incr();
+ ptr += utfc_ptr2len(ptr);
+ matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line));
+ }
continue;
}
}
- i++;
+ ptr += utfc_ptr2len(ptr);
}
- return getviscol2(bcol, 0);
+ return col + nudge;
}
-
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 134ffcd7dc..6548d351a6 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -155,12 +155,11 @@ void update_topline(void)
old_topline = curwin->w_topline;
old_topfill = curwin->w_topfill;
- /*
- * If the buffer is empty, always set topline to 1.
- */
- if (bufempty()) { /* special case - file is empty */
- if (curwin->w_topline != 1)
+ // If the buffer is empty, always set topline to 1.
+ if (BUFEMPTY()) { // special case - file is empty
+ if (curwin->w_topline != 1) {
redraw_later(NOT_VALID);
+ }
curwin->w_topline = 1;
curwin->w_botline = 2;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index ab20be7246..47044a6072 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -5031,26 +5031,21 @@ static void nv_right(cmdarg_T *cap)
if ((!PAST_LINE && oneright() == false)
|| (PAST_LINE && *get_cursor_pos_ptr() == NUL)
) {
- /*
- * <Space> wraps to next line if 'whichwrap' has 's'.
- * 'l' wraps to next line if 'whichwrap' has 'l'.
- * CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
- */
- if ( ((cap->cmdchar == ' '
- && vim_strchr(p_ww, 's') != NULL)
- || (cap->cmdchar == 'l'
- && vim_strchr(p_ww, 'l') != NULL)
- || (cap->cmdchar == K_RIGHT
- && vim_strchr(p_ww, '>') != NULL))
- && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- /* When deleting we also count the NL as a character.
- * Set cap->oap->inclusive when last char in the line is
- * included, move to next line after that */
- if ( cap->oap->op_type != OP_NOP
- && !cap->oap->inclusive
- && !lineempty(curwin->w_cursor.lnum))
+ // <Space> wraps to next line if 'whichwrap' has 's'.
+ // 'l' wraps to next line if 'whichwrap' has 'l'.
+ // CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
+ if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
+ || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
+ || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ // When deleting we also count the NL as a character.
+ // Set cap->oap->inclusive when last char in the line is
+ // included, move to next line after that
+ if (cap->oap->op_type != OP_NOP
+ && !cap->oap->inclusive
+ && !LINEEMPTY(curwin->w_cursor.lnum)) {
cap->oap->inclusive = true;
- else {
+ } else {
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
@@ -5060,12 +5055,14 @@ static void nv_right(cmdarg_T *cap)
continue;
}
if (cap->oap->op_type == OP_NOP) {
- /* Only beep and flush if not moved at all */
- if (n == cap->count1)
+ // Only beep and flush if not moved at all
+ if (n == cap->count1) {
beep_flush();
+ }
} else {
- if (!lineempty(curwin->w_cursor.lnum))
+ if (!LINEEMPTY(curwin->w_cursor.lnum)) {
cap->oap->inclusive = true;
+ }
}
break;
} else if (PAST_LINE) {
@@ -5123,13 +5120,12 @@ static void nv_left(cmdarg_T *cap)
coladvance((colnr_T)MAXCOL);
curwin->w_set_curswant = true;
- /* When the NL before the first char has to be deleted we
- * put the cursor on the NUL after the previous line.
- * This is a very special case, be careful!
- * Don't adjust op_end now, otherwise it won't work. */
- if ( (cap->oap->op_type == OP_DELETE
- || cap->oap->op_type == OP_CHANGE)
- && !lineempty(curwin->w_cursor.lnum)) {
+ // When the NL before the first char has to be deleted we
+ // put the cursor on the NUL after the previous line.
+ // This is a very special case, be careful!
+ // Don't adjust op_end now, otherwise it won't work.
+ if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE)
+ && !LINEEMPTY(curwin->w_cursor.lnum)) {
char_u *cp = get_cursor_pos_ptr();
if (*cp != NUL) {
@@ -6098,10 +6094,11 @@ static void n_swapchar(cmdarg_T *cap)
pos_T startpos;
int did_change = 0;
- if (checkclearopq(cap->oap))
+ if (checkclearopq(cap->oap)) {
return;
+ }
- if (lineempty(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
+ if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
clearopbeep(cap->oap);
return;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 3a338b1417..665a102253 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2052,10 +2052,11 @@ void op_insert(oparg_T *oap, long count1)
curwin->w_cursor = oap->end;
check_cursor_col();
- /* Works just like an 'i'nsert on the next character. */
- if (!lineempty(curwin->w_cursor.lnum)
- && oap->start_vcol != oap->end_vcol)
+ // Works just like an 'i'nsert on the next character.
+ if (!LINEEMPTY(curwin->w_cursor.lnum)
+ && oap->start_vcol != oap->end_vcol) {
inc_cursor();
+ }
}
}
@@ -2180,9 +2181,10 @@ int op_change(oparg_T *oap)
} else if (op_delete(oap) == FAIL)
return FALSE;
- if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum)
- && !virtual_op)
+ if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
+ && !virtual_op) {
inc_cursor();
+ }
// check for still on same line (<CR> in inserted text meaningless)
// skip blank lines too
@@ -2855,25 +2857,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
} else if (y_type == kMTLineWise) {
lnum = curwin->w_cursor.lnum;
- /* Correct line number for closed fold. Don't move the cursor yet,
- * u_save() uses it. */
- if (dir == BACKWARD)
+ // Correct line number for closed fold. Don't move the cursor yet,
+ // u_save() uses it.
+ if (dir == BACKWARD) {
(void)hasFolding(lnum, &lnum, NULL);
- else
+ } else {
(void)hasFolding(lnum, NULL, &lnum);
- if (dir == FORWARD)
- ++lnum;
- /* In an empty buffer the empty line is going to be replaced, include
- * it in the saved lines. */
- if ((bufempty() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL)
+ }
+ if (dir == FORWARD) {
+ lnum++;
+ }
+ // In an empty buffer the empty line is going to be replaced, include
+ // it in the saved lines.
+ if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) {
goto end;
- if (dir == FORWARD)
+ }
+ if (dir == FORWARD) {
curwin->w_cursor.lnum = lnum - 1;
- else
+ } else {
curwin->w_cursor.lnum = lnum;
- curbuf->b_op_start = curwin->w_cursor; /* for mark_adjust() */
- } else if (u_save_cursor() == FAIL)
+ }
+ curbuf->b_op_start = curwin->w_cursor; // for mark_adjust()
+ } else if (u_save_cursor() == FAIL) {
goto end;
+ }
yanklen = (int)STRLEN(y_array[0]);
@@ -3997,7 +4004,7 @@ format_lines (
&& (do_second_indent || do_number_indent)
&& prev_is_end_par
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (do_second_indent && !lineempty(curwin->w_cursor.lnum + 1)) {
+ if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
if (leader_len == 0 && next_leader_len == 0) {
/* no comment found */
second_indent =
diff --git a/src/nvim/option.c b/src/nvim/option.c
index fa2e6b169b..aa1f5f3fe7 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1059,7 +1059,7 @@ void set_init_3(void)
xfree(p);
}
- if (bufempty()) {
+ if (BUFEMPTY()) {
int idx_ffs = findoption_len(S_LEN("ffs"));
// Apply the first entry of 'fileformats' to the initial buffer.
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index e32c6e05d2..c205e4b3af 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -189,6 +189,7 @@ static int do_os_system(char **argv,
{
out_data_decide_throttle(0); // Initialize throttle decider.
out_data_ring(NULL, 0); // Initialize output ring-buffer.
+ bool has_input = (input != NULL && input[0] != '\0');
// the output buffer
DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
@@ -212,7 +213,7 @@ static int do_os_system(char **argv,
MultiQueue *events = multiqueue_new_child(main_loop.events);
proc->events = events;
proc->argv = argv;
- int status = process_spawn(proc, input != NULL, true, true);
+ int status = process_spawn(proc, has_input, true, true);
if (status) {
loop_poll_events(&main_loop, 0);
// Failed, probably 'shell' is not executable.
@@ -231,7 +232,7 @@ static int do_os_system(char **argv,
// deal with stream events as fast a possible. It prevents closing the
// streams while there's still data in the OS buffer (due to the process
// exiting before all data is read).
- if (input != NULL) {
+ if (has_input) {
wstream_init(&proc->in, 0);
}
rstream_init(&proc->out, 0);
@@ -240,8 +241,8 @@ static int do_os_system(char **argv,
rstream_start(&proc->err, data_cb, &buf);
// write the input, if any
- if (input) {
- WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL);
+ if (has_input) {
+ WBuffer *input_buffer = wstream_new_buffer((char *)input, len, 1, NULL);
if (!wstream_write(&proc->in, input_buffer)) {
// couldn't write, stop the process and tell the user about it
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 09cede8805..21ac064c30 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2202,7 +2202,13 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf,
// expand it if forced or not an absolute path
if (force || !path_is_absolute_path(fname)) {
- if ((p = vim_strrchr(fname, PATHSEP)) != NULL) {
+ p = vim_strrchr(fname, '/');
+#ifdef WIN32
+ if (p == NULL) {
+ p = vim_strrchr(fname, '\\');
+ }
+#endif
+ if (p != NULL) {
// relative to root
if (p == fname) {
// only one path component
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 2462975c9b..bf79e4b84f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -593,7 +593,7 @@ static int pum_set_selected(int n, int repeat)
&& (curbuf->b_p_bt[2] == 'f')
&& (curbuf->b_p_bh[0] == 'w')) {
// Already a "wipeout" buffer, make it empty.
- while (!bufempty()) {
+ while (!BUFEMPTY()) {
ml_delete((linenr_T)1, FALSE);
}
} else {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 9ae5754f80..bbcac45369 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -314,8 +314,7 @@ void free_search_patterns(void)
/// It's similar but different from save_search_patterns() and
/// restore_search_patterns(), because the search pattern must be restored when
/// cancelling incremental searching even if it's called inside user functions.
- void
-save_last_search_pattern(void)
+void save_last_search_pattern(void)
{
saved_last_search_spat = spats[RE_SEARCH];
if (spats[RE_SEARCH].pat != NULL) {
@@ -325,8 +324,7 @@ save_last_search_pattern(void)
saved_no_hlsearch = no_hlsearch;
}
- void
-restore_last_search_pattern(void)
+void restore_last_search_pattern(void)
{
xfree(spats[RE_SEARCH].pat);
spats[RE_SEARCH] = saved_last_search_spat;
@@ -335,8 +333,7 @@ restore_last_search_pattern(void)
SET_NO_HLSEARCH(saved_no_hlsearch);
}
- char_u *
-last_search_pattern(void)
+char_u *last_search_pattern(void)
{
return spats[RE_SEARCH].pat;
}
@@ -2263,10 +2260,11 @@ int findsent(int dir, long count)
break;
found_dot = TRUE;
}
- if (decl(&pos) == -1)
+ if (decl(&pos) == -1) {
break;
- /* when going forward: Stop in front of empty line */
- if (lineempty(pos.lnum) && dir == FORWARD) {
+ }
+ // when going forward: Stop in front of empty line
+ if (LINEEMPTY(pos.lnum) && dir == FORWARD) {
incl(&pos);
goto found;
}
@@ -2570,10 +2568,12 @@ int bck_word(long count, int bigword, int stop)
*/
while (cls() == 0) {
if (curwin->w_cursor.col == 0
- && lineempty(curwin->w_cursor.lnum))
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
goto finished;
- if (dec_cursor() == -1) /* hit start of file, stop here */
+ }
+ if (dec_cursor() == -1) { // hit start of file, stop here
return OK;
+ }
}
/*
@@ -2637,10 +2637,12 @@ int end_word(long count, int bigword, int stop, int empty)
*/
while (cls() == 0) {
if (empty && curwin->w_cursor.col == 0
- && lineempty(curwin->w_cursor.lnum))
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
goto finished;
- if (inc_cursor() == -1) /* hit end of file, stop here */
+ }
+ if (inc_cursor() == -1) { // hit end of file, stop here
return FAIL;
+ }
}
/*
@@ -2693,10 +2695,12 @@ bckend_word (
* Move backward to end of the previous word
*/
while (cls() == 0) {
- if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
+ if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
break;
- if ((i = dec_cursor()) == -1 || (eol && i == 1))
+ }
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
return OK;
+ }
}
}
return OK;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 38392509a5..34eb2fdf1b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -7147,7 +7147,7 @@ void ex_spelldump(exarg_T *eap)
set_option_value("spl", dummy, (char *)spl, OPT_LOCAL);
xfree(spl);
- if (!bufempty()) {
+ if (!BUFEMPTY()) {
return;
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 05bc6c9d96..40479e05dd 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -881,7 +881,8 @@ static void syn_start_line(void)
}
next_match_idx = -1;
- ++current_line_id;
+ current_line_id++;
+ next_seqnr = 1;
}
/*
@@ -1599,6 +1600,7 @@ get_syntax_attr (
current_id = 0;
current_trans_id = 0;
current_flags = 0;
+ current_seqnr = 0;
return 0;
}
@@ -2042,6 +2044,7 @@ syn_current_attr (
current_id = 0;
current_trans_id = 0;
current_flags = 0;
+ current_seqnr = 0;
if (cur_si != NULL) {
for (int idx = current_state.ga_len - 1; idx >= 0; --idx) {
sip = &CUR_STATE(idx);
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 2dc42be652..bd9842ea19 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -7,12 +7,11 @@ ifeq ($(OS),Windows_NT)
else
NVIM_PRG ?= ../../../build/bin/nvim
endif
-TMPDIR ?= Xtest-tmpdir
SCRIPTSOURCE := ../../../runtime
export SHELL := sh
export NVIM_PRG := $(NVIM_PRG)
-export TMPDIR
+export TMPDIR := $(abspath ../../../Xtest-tmpdir)
SCRIPTS_DEFAULT = \
test14.out \
@@ -53,6 +52,7 @@ NEW_TESTS ?= \
test_diffmode.res \
test_farsi.res \
test_filter_map.res \
+ test_findfile.res \
test_fnameescape.res \
test_fold.res \
test_ga.res \
@@ -77,6 +77,7 @@ NEW_TESTS ?= \
test_mksession_utf8.res \
test_nested_function.res \
test_normal.res \
+ test_number.res \
test_options.res \
test_profile.res \
test_quickfix.res \
diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim
index d8e2d1d718..4925b04a82 100644
--- a/src/nvim/testdir/shared.vim
+++ b/src/nvim/testdir/shared.vim
@@ -9,8 +9,12 @@ func! Fix_truncated_tmpfile(fname)
if $TMPDIR ==# ''
throw '$TMPDIR is empty'
endif
- if a:fname !~# $TMPDIR
- throw '$TMPDIR not in fname: '.a:fname
+ let tmpdir_tail = fnamemodify(substitute($TMPDIR, '[\/]\+$', '', 'g'), ':t')
+ if tmpdir_tail ==# ''
+ throw 'empty tmpdir_tail'
+ endif
+ if a:fname !~# tmpdir_tail
+ throw printf('$TMPDIR (%s) not in fname: %s', tmpdir_tail, a:fname)
endif
let last2segments = matchstr(a:fname, '[\/][^\/]\+[\/][^\/]\+$')
return $TMPDIR.last2segments
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 6f572e9b79..6df5aae677 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -12,6 +12,7 @@ source test_expr_utf8.vim
source test_feedkeys.vim
source test_filter_cmd.vim
source test_filter_map.vim
+source test_findfile.vim
source test_float_func.vim
source test_functions.vim
source test_ga.vim
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
new file mode 100644
index 0000000000..d9a89801ea
--- /dev/null
+++ b/src/nvim/testdir/test_findfile.vim
@@ -0,0 +1,25 @@
+" Test for findfile()
+"
+func Test_findfile()
+ new
+ let cwd=getcwd()
+ cd ..
+
+ " Tests may be run from a shadow directory, so an extra cd needs to be done to
+ " get above src/
+ if fnamemodify(getcwd(), ':t') != 'src'
+ cd ../..
+ else
+ cd ..
+ endif
+ set ssl
+
+ call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*'))
+ exe "cd" cwd
+ cd ..
+ call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*'))
+ call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir'))
+
+ exe "cd" cwd
+ q!
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index c28d76e66f..307f62e17a 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1075,7 +1075,6 @@ func! Test_normal19_z_spell()
if !has("spell") || !has('syntax')
return
endif
- " let $TMPDIR=fnamemodify($TMPDIR, ':.')
new
call append(0, ['1 good', '2 goood', '3 goood'])
set spell spellfile=./Xspellfile.add spelllang=en
@@ -1122,7 +1121,6 @@ func! Test_normal19_z_spell()
" Test for zG
let a=execute('unsilent norm! V$zG')
call assert_match("Word '2 goood' added to .*", a)
- set shortmess=
let fname=matchstr(a, 'to\s\+\zs\f\+$')
let fname=Fix_truncated_tmpfile(fname)
let cnt=readfile(fname)
diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim
new file mode 100644
index 0000000000..59debcea0d
--- /dev/null
+++ b/src/nvim/testdir/test_number.vim
@@ -0,0 +1,254 @@
+" Test for 'number' and 'relativenumber'
+
+source view_util.vim
+
+func! s:screen_lines(start, end) abort
+ return ScreenLines([a:start, a:end], 8)
+endfunc
+
+func! s:compare_lines(expect, actual)
+ call assert_equal(a:expect, a:actual)
+endfunc
+
+func! s:test_windows(h, w) abort
+ call NewWindow(a:h, a:w)
+endfunc
+
+func! s:close_windows() abort
+ call CloseWindow()
+endfunc
+
+func! s:validate_cursor() abort
+ " update skipcol.
+ " wincol():
+ " f_wincol
+ " -> validate_cursor
+ " -> curs_columns
+ call wincol()
+endfunc
+
+func Test_set_options()
+ set nu rnu
+ call assert_equal(1, &nu)
+ call assert_equal(1, &rnu)
+
+ call s:test_windows(10, 20)
+ call assert_equal(1, &nu)
+ call assert_equal(1, &rnu)
+ call s:close_windows()
+
+ set nu& rnu&
+endfunc
+
+func Test_set_global_and_local()
+ " setlocal must NOT reset the other global value
+ set nonu nornu
+ setglobal nu
+ setlocal rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ setglobal rnu
+ setlocal nu
+ call assert_equal(1, &g:rnu)
+
+ " setglobal MUST reset the other global value
+ set nonu nornu
+ setglobal nu
+ setglobal rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ setglobal rnu
+ setglobal nu
+ call assert_equal(1, &g:rnu)
+
+ " set MUST reset the other global value
+ set nonu nornu
+ set nu
+ set rnu
+ call assert_equal(1, &g:nu)
+
+ set nonu nornu
+ set rnu
+ set nu
+ call assert_equal(1, &g:rnu)
+
+ set nu& rnu&
+endfunc
+
+func Test_number()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ setl number
+ let lines = s:screen_lines(1, 4)
+ let expect = [
+\ " 1 abcd",
+\ " 2 klmn",
+\ " 3 uvwx",
+\ " 4 EFGH",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_relativenumber()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ 3
+ setl relativenumber
+ let lines = s:screen_lines(1, 6)
+ let expect = [
+\ " 2 abcd",
+\ " 1 klmn",
+\ " 0 uvwx",
+\ " 1 EFGH",
+\ " 2 OPQR",
+\ " 3 YZ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_number_with_relativenumber()
+ call s:test_windows(10, 20)
+ call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"])
+ 4
+ setl number relativenumber
+ let lines = s:screen_lines(1, 6)
+ let expect = [
+\ " 3 abcd",
+\ " 2 klmn",
+\ " 1 uvwx",
+\ "4 EFGH",
+\ " 1 OPQR",
+\ " 2 YZ ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_number_with_linewrap1()
+ call s:test_windows(3, 20)
+ normal! 61ia
+ setl number wrap
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ "--1 aaaa",
+\ " aaaa",
+\ " aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
+func XTest_number_with_linewrap2()
+ call s:test_windows(3, 20)
+ normal! 61ia
+ setl number wrap
+ call s:validate_cursor()
+ 0
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aaaa",
+\ " aaaa",
+\ " aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
+func XTest_number_with_linewrap3()
+ call s:test_windows(4, 20)
+ normal! 81ia
+ setl number wrap
+ call s:validate_cursor()
+ setl nonumber
+ call s:validate_cursor()
+ let lines = s:screen_lines(1, 4)
+ let expect = [
+\ "aaaaaaaa",
+\ "aaaaaaaa",
+\ "aaaaaaaa",
+\ "a ",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_numberwidth()
+ call s:test_windows(10, 20)
+ call setline(1, repeat(['aaaa'], 10))
+ setl number numberwidth=6
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aa",
+\ " 2 aa",
+\ " 3 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ set relativenumber
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ "1 aa",
+\ " 1 aa",
+\ " 2 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ set nonumber
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 0 aa",
+\ " 1 aa",
+\ " 2 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
+
+func Test_numberwidth_adjusted()
+ call s:test_windows(10, 20)
+ call setline(1, repeat(['aaaa'], 10000))
+ setl number numberwidth=4
+ let lines = s:screen_lines(1, 3)
+ let expect = [
+\ " 1 aa",
+\ " 2 aa",
+\ " 3 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ $
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 9998 aa",
+\ " 9999 aa",
+\ "10000 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ setl relativenumber
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 2 aa",
+\ " 1 aa",
+\ "10000 aa",
+\ ]
+ call s:compare_lines(expect, lines)
+
+ setl nonumber
+ let lines = s:screen_lines(8, 10)
+ let expect = [
+\ " 2 aaaa",
+\ " 1 aaaa",
+\ " 0 aaaa",
+\ ]
+ call s:compare_lines(expect, lines)
+ call s:close_windows()
+endfunc
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index d1536b5c43..3c1b0050b5 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -119,6 +119,13 @@ func Check_dir_option(name)
call assert_fails("set " . a:name . "=/not.*there", "E474:")
endfunc
+func Test_cinkeys()
+ " This used to cause invalid memory access
+ set cindent cinkeys=0
+ norm a
+ set cindent& cinkeys&
+endfunc
+
func Test_dictionary()
call Check_dir_option('dictionary')
endfunc
@@ -152,8 +159,8 @@ func Test_set_completion()
" Expand directories.
call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match(' ./samples/ ', @:)
- call assert_notmatch(' ./small.vim ', @:)
+ call assert_match('./samples/ ', @:)
+ call assert_notmatch('./small.vim ', @:)
" Expand files and directories.
call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx')
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 366a6ee1e2..b662279c6d 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -356,24 +356,24 @@ func Test_conceal()
set conceallevel=0
call assert_equal('123456 ', ScreenLines(2, 7)[0])
- call assert_equal([[0, ''], [0, ''], [0, ''], [0, ''], [0, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]'))
+ call assert_equal([[0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
set conceallevel=1
call assert_equal('1X 6 ', ScreenLines(2, 7)[0])
- call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, ' '], [1, ' '], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]'))
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, ' ', 2], [1, ' ', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
set conceallevel=1
set listchars=conceal:Y
- call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, 'Y'], [1, 'Y'], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]'))
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, 'Y', 2], [1, 'Y', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
call assert_equal('1XY6 ', ScreenLines(2, 7)[0])
set conceallevel=2
call assert_match('1X6 ', ScreenLines(2, 7)[0])
- call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, ''], [1, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]'))
+ call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
set conceallevel=3
call assert_match('16 ', ScreenLines(2, 7)[0])
- call assert_equal([[0, ''], [1, ''], [1, ''], [1, ''], [1, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]'))
+ call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)'))
syn clear
set conceallevel&
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index f3383eb006..70e19e1d93 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -69,7 +69,6 @@ typedef struct {
typedef struct {
UIBridgeData *bridge;
Loop *loop;
- bool stop;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
size_t bufpos;
@@ -124,7 +123,7 @@ static bool cursor_style_enabled = false;
UI *tui_start(void)
{
- UI *ui = xcalloc(1, sizeof(UI));
+ UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop().
ui->stop = tui_stop;
ui->rgb = p_tgc;
ui->resize = tui_resize;
@@ -324,11 +323,11 @@ static void tui_terminal_stop(UI *ui)
static void tui_stop(UI *ui)
{
tui_terminal_stop(ui);
- TUIData *data = ui->data;
- data->stop = true;
+ // Flag UI as "stopped". Needed by tui_scheduler (called from main thread).
+ ui->data = NULL;
}
-// Main function of the TUI thread
+/// Main function of the TUI thread.
static void tui_main(UIBridgeData *bridge, UI *ui)
{
Loop tui_loop;
@@ -349,7 +348,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
#endif
term_input_init(&data->input, &tui_loop);
tui_terminal_start(ui);
- data->stop = false;
// Allow main thread to continue, we are ready to handle UI callbacks.
CONTINUE(bridge);
@@ -358,17 +356,17 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
event_create(show_termcap_event, 1, data->ut));
// "Active" loop: first ~100 ms of startup.
- for (size_t ms = 0; ms < 100 && !data->stop;) {
+ for (size_t ms = 0; ms < 100 && !ui_is_stopped(ui);) {
ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1);
}
- if (!data->stop) {
+ if (!ui_is_stopped(ui)) {
tui_terminal_after_startup(ui);
// Tickle `main_loop` with a dummy event, else the initial "focus-gained"
// terminal response may not get processed until user hits a key.
loop_schedule_deferred(&main_loop, event_create(tui_dummy_event, 0));
}
// "Passive" (I/O-driven) loop: TUI thread "main loop".
- while (!data->stop) {
+ while (!ui_is_stopped(ui)) {
loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed
}
@@ -380,16 +378,19 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
loop_close(&tui_loop, false);
kv_destroy(data->invalid_regions);
xfree(data);
- xfree(ui);
}
static void tui_dummy_event(void **argv)
{
}
+/// Handoff point between the main (ui_bridge) thread and the TUI thread.
static void tui_scheduler(Event event, void *d)
{
UI *ui = d;
+ if (ui_is_stopped(ui)) {
+ return; // tui_stop was handled, teardown underway.
+ }
TUIData *data = ui->data;
loop_schedule(data->loop, event); // `tui_loop` local to tui_main().
}
@@ -1812,6 +1813,12 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value,
if (value != NULL && strequal(stty_erase, value)) {
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
}
+ } else if (strequal(name, "key_mouse")) {
+ DLOG("libtermkey:kmous=%s", value);
+ // If key_mouse is found, libtermkey uses its terminfo driver (driver-ti.c)
+ // for mouse input, which by accident only supports X10 protocol.
+ // Force libtermkey to fallback to its CSI driver (driver-csi.c). #7948
+ return NULL;
}
return value;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 81da88c54a..8aec923538 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -143,6 +143,12 @@ void ui_builtin_stop(void)
UI_CALL(stop);
}
+/// Returns true if UI `ui` is stopped.
+bool ui_is_stopped(UI *ui)
+{
+ return ui->data == NULL;
+}
+
bool ui_rgb_attached(void)
{
for (size_t i = 0; i < ui_count; i++) {
@@ -404,7 +410,7 @@ void ui_start_highlight(int attr_code)
{
current_attr_code = attr_code;
- if (!ui_count) {
+ if (!ui_active()) {
return;
}
@@ -415,7 +421,7 @@ void ui_stop_highlight(void)
{
current_attr_code = HL_NORMAL;
- if (!ui_count) {
+ if (!ui_active()) {
return;
}
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 0a69cf0ecb..16dd42ebaa 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -118,11 +118,12 @@ static void ui_bridge_stop(UI *b)
if (stopped) {
break;
}
- loop_poll_events(&main_loop, 10);
+ loop_poll_events(&main_loop, 10); // Process one event.
}
uv_thread_join(&bridge->ui_thread);
uv_mutex_destroy(&bridge->mutex);
uv_cond_destroy(&bridge->cond);
+ xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922
ui_detach_impl(b);
xfree(b);
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index b902f82f31..35857510fc 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2268,12 +2268,14 @@ static void u_undoredo(int undo)
curhead->uh_entry = newlist;
curhead->uh_flags = new_flags;
- if ((old_flags & UH_EMPTYBUF) && bufempty())
+ if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) {
curbuf->b_ml.ml_flags |= ML_EMPTY;
- if (old_flags & UH_CHANGED)
+ }
+ if (old_flags & UH_CHANGED) {
changed();
- else
+ } else {
unchanged(curbuf, FALSE);
+ }
/*
* restore marks from before undo/redo
diff --git a/src/nvim/version.c b/src/nvim/version.c
index e627022ea6..c95be072cf 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -823,27 +823,27 @@ static const int included_patches[] = {
// 677,
// 676,
// 675,
- // 674,
- // 673,
- // 672,
+ 674,
+ 673,
+ 672,
// 671,
// 670,
// 669,
- // 668,
- // 667,
- // 666,
- // 665,
- // 664,
+ 668,
+ 667,
+ 666,
+ 665,
+ 664,
// 663,
- // 662,
- // 661,
- // 660,
- // 659,
+ 662,
+ 661,
+ 660,
+ 659,
658,
657,
- // 656,
- // 655,
- // 654,
+ 656,
+ 655,
+ 654,
// 653,
652,
// 651,
@@ -898,7 +898,7 @@ static const int included_patches[] = {
// 602,
601,
// 600,
- // 599,
+ 599,
// 598,
597,
// 596,
@@ -969,7 +969,7 @@ static const int included_patches[] = {
// 531,
// 530,
// 529,
- // 528,
+ 528,
// 527,
// 526,
// 525,
@@ -1049,13 +1049,13 @@ static const int included_patches[] = {
// 451,
// 450,
// 449,
- // 448,
+ 448,
// 447,
// 446,
// 445,
- // 444,
- // 443,
- // 442,
+ 444,
+ 443,
+ 442,
// 441,
// 440,
// 439,
@@ -1064,14 +1064,14 @@ static const int included_patches[] = {
// 436,
// 435,
// 434,
- // 433,
+ 433,
// 432,
- // 431,
+ 431,
// 430,
// 429,
// 428,
- // 427,
- // 426,
+ 427,
+ 426,
// 425,
// 424,
423,
@@ -1138,8 +1138,8 @@ static const int included_patches[] = {
// 362,
// 361,
360,
- // 359,
- // 358,
+ 359,
+ 358,
// 357,
// 356,
// 355,
@@ -1498,7 +1498,6 @@ static const int included_patches[] = {
2,
1,
0,
-
};
// clang-format on
@@ -1722,7 +1721,7 @@ static void version_msg(char *s)
/// Show the intro message when not editing a file.
void maybe_intro_message(void)
{
- if (bufempty()
+ if (BUFEMPTY()
&& (curbuf->b_fname == NULL)
&& (firstwin->w_next == NULL)
&& (vim_strchr(p_shm, SHM_INTRO) == NULL)) {
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index e957650c88..a02d36c939 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -9,6 +9,7 @@ local command = helpers.command
local wait = helpers.wait
local iswin = helpers.iswin
local get_pathsep = helpers.get_pathsep
+local pathroot = helpers.pathroot
local nvim_set = helpers.nvim_set
local expect_twostreams = helpers.expect_twostreams
local Screen = require('test.functional.ui.screen')
@@ -63,7 +64,7 @@ describe('jobs', function()
nvim('command', "let j = jobstart('pwd', g:job_opts)")
end
eq({'notification', 'stdout',
- {0, {(iswin() and [[C:\]] or '/'), ''}}}, next_msg())
+ {0, {pathroot(), ''}}}, next_msg())
eq({'notification', 'stdout', {0, {''}}}, next_msg())
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua
index db50874c53..7de58766b9 100644
--- a/test/functional/eval/buf_functions_spec.lua
+++ b/test/functional/eval/buf_functions_spec.lua
@@ -14,6 +14,7 @@ local curbufmeths = helpers.curbufmeths
local curwinmeths = helpers.curwinmeths
local curtabmeths = helpers.curtabmeths
local get_pathsep = helpers.get_pathsep
+local rmdir = helpers.rmdir
local fname = 'Xtest-functional-eval-buf_functions'
local fname2 = fname .. '.2'
@@ -61,7 +62,7 @@ describe('bufname() function', function()
lfs.mkdir(dirname)
end)
after_each(function()
- lfs.rmdir(dirname)
+ rmdir(dirname)
end)
it('returns expected buffer name', function()
eq('', funcs.bufname('%')) -- Buffer has no name yet
@@ -143,7 +144,7 @@ describe('bufwinnr() function', function()
lfs.mkdir(dirname)
end)
after_each(function()
- lfs.rmdir(dirname)
+ rmdir(dirname)
end)
it('returns expected window number', function()
eq(1, funcs.bufwinnr('%'))
diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua
new file mode 100644
index 0000000000..fe6b50a544
--- /dev/null
+++ b/test/functional/eval/fnamemodify_spec.lua
@@ -0,0 +1,39 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eq = helpers.eq
+local iswin = helpers.iswin
+local fnamemodify = helpers.funcs.fnamemodify
+local command = helpers.command
+local write_file = helpers.write_file
+
+describe('fnamemodify()', function()
+ setup(function()
+ write_file('Xtest-fnamemodify.txt', [[foobar]])
+ end)
+
+ before_each(clear)
+
+ teardown(function()
+ os.remove('Xtest-fnamemodify.txt')
+ end)
+
+ it('works', function()
+ local root = helpers.pathroot()
+ eq(root, fnamemodify([[/]], ':p:h'))
+ eq(root, fnamemodify([[/]], ':p'))
+ if iswin() then
+ eq(root, fnamemodify([[\]], ':p:h'))
+ eq(root, fnamemodify([[\]], ':p'))
+ command('set shellslash')
+ root = string.sub(root, 1, -2)..'/'
+ eq(root, fnamemodify([[\]], ':p:h'))
+ eq(root, fnamemodify([[\]], ':p'))
+ eq(root, fnamemodify([[/]], ':p:h'))
+ eq(root, fnamemodify([[/]], ':p'))
+ end
+ end)
+
+ it(':8 works', function()
+ eq('Xtest-fnamemodify.txt', fnamemodify([[Xtest-fnamemodify.txt]], ':8'))
+ end)
+end)
diff --git a/test/functional/eval/has_spec.lua b/test/functional/eval/has_spec.lua
index 78c4e08fde..a3af2d1a20 100644
--- a/test/functional/eval/has_spec.lua
+++ b/test/functional/eval/has_spec.lua
@@ -57,4 +57,10 @@ describe('has()', function()
eq(0, funcs.has("unnamedplus"))
end
end)
+
+ it('"wsl"', function()
+ if 1 == funcs.has('win32') or 1 == funcs.has('mac') then
+ eq(0, funcs.has('wsl'))
+ end
+ end)
end)
diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua
index 6d5b64b929..6112cf64e3 100644
--- a/test/functional/eval/hostname_spec.lua
+++ b/test/functional/eval/hostname_spec.lua
@@ -1,7 +1,9 @@
local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
local ok = helpers.ok
local call = helpers.call
local clear = helpers.clear
+local iswin = helpers.iswin
describe('hostname()', function()
before_each(clear)
@@ -11,7 +13,8 @@ describe('hostname()', function()
ok(string.len(actual) > 0)
if call('executable', 'hostname') == 1 then
local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
- helpers.eq(expected, actual)
+ eq((iswin() and expected:upper() or expected),
+ (iswin() and actual:upper() or actual))
end
end)
end)
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index 77e7424452..7fe79d4351 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -260,6 +260,7 @@ describe('system()', function()
end)
it('works with an empty string', function()
eq("test\n", eval('system("echo test", "")'))
+ eq(2, eval("1+1")) -- Still alive?
end)
end)
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index 5d658f10bb..a5b327095e 100644
--- a/test/functional/ex_cmds/mksession_spec.lua
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -6,6 +6,7 @@ local command = helpers.command
local get_pathsep = helpers.get_pathsep
local eq = helpers.eq
local funcs = helpers.funcs
+local rmdir = helpers.rmdir
local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec'
@@ -20,7 +21,7 @@ describe(':mksession', function()
after_each(function()
os.remove(session_file)
- lfs.rmdir(tab_dir)
+ rmdir(tab_dir)
end)
it('restores tab-local working directories', function()
diff --git a/test/functional/ex_cmds/mkview_spec.lua b/test/functional/ex_cmds/mkview_spec.lua
index 97a49dbbd5..fef8065b2e 100644
--- a/test/functional/ex_cmds/mkview_spec.lua
+++ b/test/functional/ex_cmds/mkview_spec.lua
@@ -24,7 +24,7 @@ describe(':mkview', function()
after_each(function()
-- Remove any views created in the view directory
rmdir(view_dir)
- lfs.rmdir(local_dir)
+ rmdir(local_dir)
end)
it('viewoption curdir restores local current directory', function()
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index dfc4694272..f0e47481da 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -603,6 +603,11 @@ local function get_pathsep()
return funcs.fnamemodify('.', ':p'):sub(-1)
end
+local function pathroot()
+ local pathsep = package.config:sub(1,1)
+ return iswin() and (nvim_dir:sub(1,2)..pathsep) or '/'
+end
+
-- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS.
-- Useful for communicating with child instances.
local function new_pipename()
@@ -736,6 +741,7 @@ local module = {
meth_pcall = meth_pcall,
NIL = mpack.NIL,
get_pathsep = get_pathsep,
+ pathroot = pathroot,
missing_provider = missing_provider,
alter_slashes = alter_slashes,
hexdump = hexdump,
diff --git a/test/functional/legacy/003_cindent_spec.lua b/test/functional/legacy/003_cindent_spec.lua
index 13726050b2..1cede8a7d7 100644
--- a/test/functional/legacy/003_cindent_spec.lua
+++ b/test/functional/legacy/003_cindent_spec.lua
@@ -1957,7 +1957,8 @@ describe('cindent', function()
}
]=])
- feed_command('set tw=0 wm=60 columns=80 noai fo=croq')
+ feed_command('set tw=0 noai fo=croq')
+ feed_command('let &wm = &columns - 20')
feed_command('/serious/e')
feed('a about life, the universe, and the rest<esc>')
diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua
index 7474f1e068..453638ce45 100644
--- a/test/functional/legacy/008_autocommands_spec.lua
+++ b/test/functional/legacy/008_autocommands_spec.lua
@@ -5,9 +5,10 @@ local helpers = require('test.functional.helpers')(after_each)
local feed, source = helpers.feed, helpers.source
local clear, feed_command, expect, eq, eval = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.eval
local write_file, wait, dedent = helpers.write_file, helpers.wait, helpers.dedent
-local io = require('io')
+local read_file = helpers.read_file
describe('autocommands that delete and unload buffers:', function()
+ local test_file = 'Xtest-008_autocommands.out'
local text1 = dedent([[
start of Xxx1
test
@@ -18,7 +19,7 @@ describe('autocommands that delete and unload buffers:', function()
write_file('Xxx2', text2..'\n')
end)
teardown(function()
- os.remove('test.out')
+ os.remove(test_file)
os.remove('Xxx1')
os.remove('Xxx2')
end)
@@ -65,7 +66,8 @@ describe('autocommands that delete and unload buffers:', function()
endwhile
endfunc
func WriteToOut()
- edit! test.out
+ edit! ]]..test_file..[[
+
$put ='VimLeave done'
write
endfunc
@@ -86,6 +88,6 @@ describe('autocommands that delete and unload buffers:', function()
feed_command('q')
wait()
eq('VimLeave done',
- string.match(io.open('test.out', 'r'):read('*all'), "^%s*(.-)%s*$"))
+ string.match(read_file(test_file), "^%s*(.-)%s*$"))
end)
end)
diff --git a/test/functional/legacy/089_number_relnumber_findfile_spec.lua b/test/functional/legacy/089_number_relnumber_findfile_spec.lua
deleted file mode 100644
index 6708fd50b7..0000000000
--- a/test/functional/legacy/089_number_relnumber_findfile_spec.lua
+++ /dev/null
@@ -1,116 +0,0 @@
--- - Some tests for setting 'number' and 'relativenumber'
--- This is not all that useful now that the options are no longer reset when
--- setting the other.
-
-local helpers = require('test.functional.helpers')(after_each)
-local feed = helpers.feed
-local clear, expect, source = helpers.clear, helpers.expect, helpers.source
-
-describe("setting 'number' and 'relativenumber'", function()
- setup(clear)
-
- it('is working', function()
- source([[
- set hidden nu rnu
- redir @a | set nu? | set rnu? | redir END
- e! xx
- redir @b | set nu? | set rnu? | redir END
- e! #
- $put ='results:'
- $put a
- $put b
-
- set nonu nornu
- setglobal nu
- setlocal rnu
- redir @c | setglobal nu? | redir END
- set nonu nornu
- setglobal rnu
- setlocal nu
- redir @d | setglobal rnu? | redir END
- $put =':setlocal must NOT reset the other global value'
- $put c
- $put d
-
- set nonu nornu
- setglobal nu
- setglobal rnu
- redir @e | setglobal nu? | redir END
- set nonu nornu
- setglobal rnu
- setglobal nu
- redir @f | setglobal rnu? | redir END
- $put =':setglobal MUST reset the other global value'
- $put e
- $put f
-
- set nonu nornu
- set nu
- set rnu
- redir @g | setglobal nu? | redir END
- set nonu nornu
- set rnu
- set nu
- redir @h | setglobal rnu? | redir END
- $put =':set MUST reset the other global value'
- $put g
- $put h
- ]])
-
- -- Remove empty line
- feed('ggdd')
-
- -- Assert buffer contents.
- expect([[
- results:
-
- number
- relativenumber
-
- number
- relativenumber
- :setlocal must NOT reset the other global value
-
- number
-
- relativenumber
- :setglobal MUST reset the other global value
-
- number
-
- relativenumber
- :set MUST reset the other global value
-
- number
-
- relativenumber]])
- end)
-end)
-
--- - Some tests for findfile() function
-describe('findfile', function()
- setup(clear)
-
- it('is working', function()
- -- Assume test is being run from project root
- source([[
- $put ='Testing findfile'
- $put =''
- set ssl
- $put =findfile('vim.c','src/nvim/ap*')
- cd src/nvim
- $put =findfile('vim.c','ap*')
- $put =findfile('vim.c','api')
- ]])
-
- -- Remove empty line
- feed('ggdd')
-
- expect([[
- Testing findfile
-
- src/nvim/api/vim.c
- api/vim.c
- api/vim.c]])
- end)
-end)
diff --git a/test/functional/legacy/096_location_list_spec.lua b/test/functional/legacy/096_location_list_spec.lua
index 85c4fe0ec4..b21a2085f6 100644
--- a/test/functional/legacy/096_location_list_spec.lua
+++ b/test/functional/legacy/096_location_list_spec.lua
@@ -11,10 +11,10 @@ local source = helpers.source
local clear, command, expect = helpers.clear, helpers.command, helpers.expect
describe('location list', function()
+ local test_file = 'Xtest-096_location_list.out'
setup(clear)
-
teardown(function()
- os.remove('test.out')
+ os.remove(test_file)
end)
it('is working', function()
@@ -70,9 +70,9 @@ describe('location list', function()
endfor
]])
- -- Set up the result buffer "test.out".
+ -- Set up the result buffer.
command('enew')
- command('w! test.out')
+ command('w! '..test_file)
command('b 1')
-- Test A.
@@ -99,7 +99,7 @@ describe('location list', function()
command([[let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')]])
command('wincmd n')
command('wincmd K')
- command('b test.out')
+ command('b '..test_file)
-- Prepare test output and write it to the result buffer.
command([[let fileName = substitute(fileName, '\\', '/', 'g')]])
@@ -132,7 +132,7 @@ describe('location list', function()
command('let numberOfWindowsOpen = winnr("$")')
command('wincmd n')
command('wincmd K')
- command('b test.out')
+ command('b '..test_file)
-- Prepare test output and write it to the result buffer.
command('call append(line("$"), "Test B:")')
@@ -170,7 +170,7 @@ describe('location list', function()
command('let bufferName = expand("%")')
command('wincmd n')
command('wincmd K')
- command('b test.out')
+ command('b '..test_file)
-- Prepare test output and write it to the result buffer.
command([[let bufferName = substitute(bufferName, '\\', '/', 'g')]])
diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua
index d8ecbfe058..7e859bf0cf 100644
--- a/test/functional/legacy/fnamemodify_spec.lua
+++ b/test/functional/legacy/fnamemodify_spec.lua
@@ -4,8 +4,6 @@ local helpers = require('test.functional.helpers')(after_each)
local clear, source = helpers.clear, helpers.source
local call, eq, nvim = helpers.call, helpers.eq, helpers.meths
-if helpers.pending_win32(pending) then return end
-
local function expected_empty()
eq({}, nvim.get_vvar('errors'))
end
@@ -16,17 +14,21 @@ describe('filename modifiers', function()
source([=[
func Test_fnamemodify()
- let tmpdir = resolve('/tmp')
+ if has('win32')
+ set shellslash
+ else
+ set shell=sh
+ endif
+ let tmpdir = resolve($TMPDIR)
+ call assert_true(isdirectory(tmpdir))
execute 'cd '. tmpdir
- set shell=sh
- set shellslash
let $HOME=fnamemodify('.', ':p:h:h:h')
call assert_equal('/', fnamemodify('.', ':p')[-1:])
- call assert_equal('p', fnamemodify('.', ':p:h')[-1:])
+ call assert_equal(tmpdir[strchars(tmpdir) - 1], fnamemodify('.', ':p:h')[-1:])
call assert_equal('t', fnamemodify('test.out', ':p')[-1:])
call assert_equal('test.out', fnamemodify('test.out', ':.'))
call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.'))
- call assert_equal('test.out', fnamemodify('test.out', ':~'))
+ call assert_equal(fnamemodify(tmpdir, ':~').'/test.out', fnamemodify('test.out', ':~'))
call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~'))
call assert_equal('a', fnamemodify('../testdir/a', ':t'))
call assert_equal('', fnamemodify('.', ':p:t'))
@@ -53,8 +55,10 @@ describe('filename modifiers', function()
quit
call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S'))
- set shell=tcsh
- call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S'))
+ if executable('tcsh')
+ set shell=tcsh
+ call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S'))
+ endif
endfunc
func Test_expand()
diff --git a/test/functional/normal/langmap_spec.lua b/test/functional/normal/langmap_spec.lua
new file mode 100644
index 0000000000..e4349a22e7
--- /dev/null
+++ b/test/functional/normal/langmap_spec.lua
@@ -0,0 +1,280 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq, neq, call = helpers.eq, helpers.neq, helpers.call
+local eval, feed, clear = helpers.eval, helpers.feed, helpers.clear
+local command, insert, expect = helpers.command, helpers.insert, helpers.expect
+local feed_command = helpers.feed_command
+local curwin = helpers.curwin
+
+describe("'langmap'", function()
+ before_each(function()
+ clear()
+ insert('iii www')
+ command('set langmap=iw,wi')
+ feed('gg0')
+ end)
+
+ it("converts keys in normal mode", function()
+ feed('ix')
+ expect('iii ww')
+ feed('whello<esc>')
+ expect('iii helloww')
+ end)
+ it("gives characters that are mapped by :nmap.", function()
+ command('map i 0x')
+ feed('w')
+ expect('ii www')
+ end)
+ describe("'langnoremap' option.", function()
+ before_each(function()
+ command('nmapclear')
+ end)
+ it("'langnoremap' is by default ON", function()
+ eq(eval('&langnoremap'), 1)
+ end)
+ it("Results of maps are not converted when 'langnoremap' ON.",
+ function()
+ command('nmap x i')
+ feed('xdl<esc>')
+ expect('dliii www')
+ end)
+ it("applies when deciding whether to map recursively", function()
+ command('nmap l i')
+ command('nmap w j')
+ feed('ll')
+ expect('liii www')
+ end)
+ it("does not stop applying 'langmap' on first character of a mapping",
+ function()
+ command('1t1')
+ command('1t1')
+ command('goto 1')
+ command('nmap w j')
+ feed('iiahello')
+ expect([[
+ iii www
+ iii www
+ ihelloii www]])
+ end)
+ it("Results of maps are converted when 'langnoremap' OFF.",
+ function()
+ command('set nolangnoremap')
+ command('nmap x i')
+ feed('xdl<esc>')
+ expect('iii ww')
+ end)
+ end)
+ -- e.g. CTRL-W_j , mj , 'j and "jp
+ it('conversions are applied to keys in middle of command',
+ function()
+ -- Works in middle of window command
+ feed('<C-w>s')
+ local origwin = curwin()
+ feed('<C-w>i')
+ neq(curwin(), origwin)
+ -- Works when setting a mark
+ feed('yy3p3gg0mwgg0mi')
+ eq(call('getpos', "'i"), {0, 3, 1, 0})
+ eq(call('getpos', "'w"), {0, 1, 1, 0})
+ feed('3dd')
+ -- Works when moving to a mark
+ feed("'i")
+ eq(call('getpos', '.'), {0, 1, 1, 0})
+ -- Works when selecting a register
+ feed('qillqqwhhq')
+ eq(eval('@i'), 'hh')
+ eq(eval('@w'), 'll')
+ feed('a<C-r>i<esc>')
+ expect('illii www')
+ feed('"ip')
+ expect('illllii www')
+ -- Works with i_CTRL-O
+ feed('0a<C-O>ihi<esc>')
+ expect('illllii hiwww')
+ end)
+
+ describe('exceptions', function()
+ -- All "command characters" that 'langmap' does not apply to.
+ -- These tests consist of those places where some subset of ASCII
+ -- characters define certain commands, yet 'langmap' is not applied to
+ -- them.
+ -- n.b. I think these shouldn't be exceptions.
+ it(':s///c confirmation', function()
+ command('set langmap=yn,ny')
+ feed('qa')
+ feed_command('s/i/w/gc')
+ feed('yynq')
+ expect('wwi www')
+ feed('u@a')
+ expect('wwi www')
+ eq(eval('@a'), ':s/i/w/gc\ryyn')
+ end)
+ it('insert-mode CTRL-G', function()
+ command('set langmap=jk,kj')
+ command('d')
+ insert([[
+ hello
+ hello
+ hello]])
+ expect([[
+ hello
+ hello
+ hello]])
+ feed('qa')
+ feed('gg3|ahello<C-G>jx<esc>')
+ feed('q')
+ expect([[
+ helhellolo
+ helxlo
+ hello]])
+ eq(eval('@a'), 'gg3|ahellojx')
+ end)
+ it('command-line CTRL-\\', function()
+ command('set langmap=en,ne')
+ feed(':<C-\\>e\'hello\'\r<C-B>put ="<C-E>"<CR>')
+ expect([[
+ iii www
+ hello]])
+ end)
+ it('command-line CTRL-R', function()
+ helpers.source([[
+ let i_value = 0
+ let j_value = 0
+ call setreg('i', 'i_value')
+ call setreg('j', 'j_value')
+ set langmap=ij,ji
+ ]])
+ feed(':let <C-R>i=1<CR>')
+ eq(eval('i_value'), 1)
+ eq(eval('j_value'), 0)
+ end)
+ -- it('-- More -- prompt', function()
+ -- -- The 'b' 'j' 'd' 'f' commands at the -- More -- prompt
+ -- end)
+ it('ask yes/no after backwards range', function()
+ command('set langmap=yn,ny')
+ feed('dd')
+ insert([[
+ hello
+ there
+ these
+ are
+ some
+ lines
+ ]])
+ feed_command('4,2d')
+ feed('n')
+ expect([[
+ hello
+ there
+ these
+ are
+ some
+ lines
+ ]])
+ end)
+ it('prompt for number', function()
+ command('set langmap=12,21')
+ helpers.source([[
+ let gotten_one = 0
+ function Map()
+ let answer = inputlist(['a', '1.', '2.', '3.'])
+ if answer == 1
+ let g:gotten_one = 1
+ endif
+ endfunction
+ nnoremap x :call Map()<CR>
+ ]])
+ feed('x1<CR>')
+ eq(eval('gotten_one'), 1)
+ command('let g:gotten_one = 0')
+ feed_command('call Map()')
+ feed('1<CR>')
+ eq(eval('gotten_one'), 1)
+ end)
+ end)
+ it('conversions are not applied during setreg()',
+ function()
+ call('setreg', 'i', 'ww')
+ eq(eval('@i'), 'ww')
+ end)
+ it('conversions not applied in insert mode', function()
+ feed('aiiiwww')
+ expect('iiiiwwwii www')
+ end)
+ it('conversions not applied in search mode', function()
+ feed('/iii<cr>x')
+ expect('ii www')
+ end)
+ it('conversions not applied in cmdline mode', function()
+ feed(':call append(1, "iii")<cr>')
+ expect([[
+ iii www
+ iii]])
+ end)
+
+ local function testrecording(command_string, expect_string, setup_function)
+ if setup_function then setup_function() end
+ feed('qa' .. command_string .. 'q')
+ expect(expect_string)
+ eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
+ eval('@a'))
+ if setup_function then setup_function() end
+ -- n.b. may need nvim_replace_termcodes() here.
+ feed('@a')
+ expect(expect_string)
+ end
+
+ local function local_setup()
+ -- Can't use `insert` as it uses `i` and we've swapped the meaning of that
+ -- with the `langmap` setting.
+ command('%d')
+ command("put ='hello'")
+ command('1d')
+ end
+
+ it('does not affect recording special keys', function()
+ testrecording('A<BS><esc>', 'hell', local_setup)
+ testrecording('>><lt><lt>', 'hello', local_setup)
+ command('nnoremap \\ x')
+ testrecording('\\', 'ello', local_setup)
+ testrecording('A<C-V><BS><esc>', 'hello<BS>', local_setup)
+ end)
+ pending('Translates modified keys correctly', function()
+ command('nnoremap <M-i> x')
+ command('nnoremap <M-w> l')
+ testrecording('<M-w>', 'ello', local_setup)
+ testrecording('<M-i>x', 'hllo', local_setup)
+ end)
+ pending('handles multi-byte characters', function()
+ command('set langmap=ïx')
+ testrecording('ï', 'ello', local_setup)
+ -- The test below checks that what's recorded is correct.
+ -- It doesn't check the behaviour, as in order to cause some behaviour we
+ -- need to map the multi-byte character, and there is a known bug
+ -- preventing this from working (see the test below).
+ command('set langmap=xï')
+ testrecording('x', 'hello', local_setup)
+ end)
+ pending('handles multibyte mappings', function()
+ -- See this vim issue for the problem, may as well add a test.
+ -- https://github.com/vim/vim/issues/297
+ command('set langmap=ïx')
+ command('nnoremap x diw')
+ testrecording('ï', '', local_setup)
+ command('set nolangnoremap')
+ command('set langmap=xï')
+ command('nnoremap ï ix<esc>')
+ testrecording('x', 'xhello', local_setup)
+ end)
+ -- This test is to ensure the behaviour doesn't change from what's already
+ -- around. I (hardenedapple) personally think this behaviour should be
+ -- changed.
+ it('treats control modified keys as characters', function()
+ command('nnoremap <C-w> iw<esc>')
+ command('nnoremap <C-i> ii<esc>')
+ testrecording('<C-w>', 'whello', local_setup)
+ testrecording('<C-i>', 'ihello', local_setup)
+ end)
+
+end)
diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua
index 5a064a759f..5a5b4df1ef 100644
--- a/test/functional/plugin/shada_spec.lua
+++ b/test/functional/plugin/shada_spec.lua
@@ -3,6 +3,7 @@ local eq, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf =
helpers.eq, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec,
helpers.funcs, helpers.feed, helpers.curbuf
local neq = helpers.neq
+local read_file = helpers.read_file
local mpack = require('mpack')
@@ -2152,9 +2153,7 @@ describe('plugin/shada.vim', function()
end)
local shada_eq = function(expected, fname_)
- local fd = io.open(fname_)
- local mpack_result = fd:read('*a')
- fd:close()
+ local mpack_result = read_file(fname_)
mpack_eq(expected, mpack_result)
end
@@ -2278,7 +2277,7 @@ describe('plugin/shada.vim', function()
' + f file name ["foo"]',
' + l line number 2',
' + c column -200',
- }, eol) .. eol, io.open(fname .. '.tst'):read('*a'))
+ }, eol) .. eol, read_file(fname .. '.tst'))
shada_eq({{
timestamp=0,
type=8,
@@ -2326,7 +2325,7 @@ describe('plugin/shada.vim', function()
'Jump with timestamp ' .. epoch .. ':',
' % Key________ Description Value',
' + n name \'A\'',
- }, eol) .. eol, io.open(fname .. '.tst'):read('*a'))
+ }, eol) .. eol, read_file(fname .. '.tst'))
shada_eq({{
timestamp=0,
type=8,
@@ -2383,7 +2382,7 @@ describe('plugin/shada.vim', function()
' + f file name ["foo"]',
' + l line number 2',
' + c column -200',
- }, eol) .. eol, io.open(fname .. '.tst'):read('*a'))
+ }, eol) .. eol, read_file(fname .. '.tst'))
shada_eq({{
timestamp=0,
type=8,
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 3fdedeb073..13820af3b8 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -750,17 +750,19 @@ describe('ui/mouse/input', function()
feed_command('set concealcursor=n')
feed_command('set nowrap')
- feed_command('syntax match NonText "\\<amet\\>" conceal')
- feed_command('syntax match NonText "\\cs\\|g." conceal cchar=X')
- feed_command('syntax match NonText "\\%(lo\\|cl\\)." conceal')
- feed_command('syntax match NonText "Lo" conceal cchar=Y')
+ feed_command('set shiftwidth=2 tabstop=4 list listchars=tab:>-')
+ feed_command('syntax match NonText "\\*" conceal')
+ feed_command('syntax match NonText "cats" conceal cchar=X')
+ feed_command('syntax match NonText "x" conceal cchar=>')
+ -- First column is there to retain the tabs.
insert([[
- Lorem ipsum dolor sit amet, consetetur sadipscing elitr.
- Stet clita kasd gubergren, no sea takimata sanctus est.
+ |Section *t1*
+ | *t2* *t3* *t4*
+ |x 私は猫が大好き *cats* ✨🐈✨
]])
- feed('gg')
+ feed('gg<c-v>Gxgg')
end)
it('(level 1) click on non-wrapped lines', function()
@@ -768,93 +770,138 @@ describe('ui/mouse/input', function()
feed('<esc><LeftMouse><0,0>')
screen:expect([[
- {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con|
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no|
+ ^Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
feed('<esc><LeftMouse><1,0>')
screen:expect([[
- {c:Y}^rem ip{c:X}um do{c: } {c:X}it {c: }, con|
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no|
+ S^ection{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
|
{0:~ }|
{0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }^t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
{0:~ }|
|
]])
- feed('<esc><LeftMouse><15,0>')
+ feed('<esc><LeftMouse><21,1>')
screen:expect([[
- {c:Y}rem ip{c:X}um do{c: } {c:^X}it {c: }, con|
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no|
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t^3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
|
{0:~ }|
{0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:^>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
{0:~ }|
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><7,2>')
screen:expect([[
- {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con|
- {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en, no|
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は^猫が大好き{0:>---}{c: X } {0:>}|
|
{0:~ }|
{0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: ^X } {0:>}|
+ |
+ {0:~ }|
{0:~ }|
|
]])
+
end) -- level 1 - non wrapped
it('(level 1) click on wrapped lines', function()
feed_command('let &conceallevel=1', 'let &wrap=1', 'echo')
- feed('<esc><LeftMouse><0,0>')
+ feed('<esc><LeftMouse><24,1>')
screen:expect([[
- {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: } |
- , con{c:X}etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c:^ }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ ^t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><6,1>')
+ feed('<esc><LeftMouse><8,3>')
screen:expect([[
- {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } |
- , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫^が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><21,3>')
screen:expect([[
- {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } |
- , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: ^X} |
+ {c: } ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,3>')
+ feed('<esc><LeftMouse><4,4>')
screen:expect([[
- {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } |
- , con{c:X}etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨^🐈✨ |
+ |
|
]])
end) -- level 1 - wrapped
@@ -863,46 +910,68 @@ describe('ui/mouse/input', function()
it('(level 2) click on non-wrapped lines', function()
feed_command('let &conceallevel=2', 'echo')
- feed('<esc><LeftMouse><0,0>')
+ feed('<esc><LeftMouse><20,0>')
screen:expect([[
- {c:^Y}rem ip{c:X}um do {c:X}it , con{c:X}e|
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no |
+ Section{0:>>--->--->---}^t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
- feed('<esc><LeftMouse><1,0>')
+ feed('<esc><LeftMouse><14,1>')
screen:expect([[
- {c:Y}^rem ip{c:X}um do {c:X}it , con{c:X}e|
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
- feed('<esc><LeftMouse><15,0>')
+ feed('<esc><LeftMouse><18,1>')
screen:expect([[
- {c:Y}rem ip{c:X}um do {c:X}^it , con{c:X}e|
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
|
{0:~ }|
{0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>') -- Weirdness
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
{0:~ }|
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><8,2>')
screen:expect([[
- {c:Y}rem ip{c:X}um do {c:X}it , con{c:X}e|
- {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en, no |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}|
|
{0:~ }|
{0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><20,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}|
+ |
+ {0:~ }|
{0:~ }|
|
]])
@@ -911,47 +980,108 @@ describe('ui/mouse/input', function()
it('(level 2) click on wrapped lines', function()
feed_command('let &conceallevel=2', 'let &wrap=1', 'echo')
- feed('<esc><LeftMouse><0,0>')
+ feed('<esc><LeftMouse><20,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}^t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><18,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ -- NOTE: The click would ideally be on the 't' in 't4', but wrapping
+ -- caused the invisible '*' right before 't4' to remain on the previous
+ -- screen line. This is being treated as expected because fixing this is
+ -- out of scope for mouse clicks. Should the wrapping behavior of
+ -- concealed characters change in the future, this case should be
+ -- reevaluated.
+ feed('<esc><LeftMouse><0,2>')
screen:expect([[
- {c:^Y}rem ip{c:X}um do {c:X}it |
- , con{c:X}etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 ^ |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><6,1>')
+ feed('<esc><LeftMouse><1,2>')
screen:expect([[
- {c:Y}rem ip{c:X}um do {c:X}it |
- , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t^4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><0,3>')
screen:expect([[
- {c:Y}rem ip{c:X}um do {c:X}it |
- , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:^>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,3>')
+ feed('<esc><LeftMouse><20,3>')
screen:expect([[
- {c:Y}rem ip{c:X}um do {c:X}it |
- , con{c:X}etetur {c:X}adip{c:X}cin{c:X} |
- elitr. |
- {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en |
- , no {c:X}ea takimata {c:X}anctu{c:X}|
- e{c:X}t. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:^X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ^✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><5,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈^✨ |
+ |
|
]])
end) -- level 2 - wrapped
@@ -960,47 +1090,47 @@ describe('ui/mouse/input', function()
it('(level 3) click on non-wrapped lines', function()
feed_command('let &conceallevel=3', 'echo')
- feed('<esc><LeftMouse><0,0>')
+ feed('<esc><LeftMouse><0,2>')
screen:expect([[
- ^rem ipum do it , conetetu|
- tet ta kad beren, no ea t|
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ ^ 私は猫が大好き{0:>----} ✨🐈|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
- feed('<esc><LeftMouse><1,0>')
+ feed('<esc><LeftMouse><1,2>')
screen:expect([[
- r^em ipum do it , conetetu|
- tet ta kad beren, no ea t|
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ ^私は猫が大好き{0:>----} ✨🐈|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
- feed('<esc><LeftMouse><15,0>')
+ feed('<esc><LeftMouse><13,2>')
screen:expect([[
- rem ipum do it ^, conetetu|
- tet ta kad beren, no ea t|
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ 私は猫が大好^き{0:>----} ✨🐈|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><20,2>')
screen:expect([[
- rem ipum do it , conetetu|
- tet ta kad bere^n, no ea t|
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ 私は猫が大好き{0:>----}^ ✨🐈|
|
{0:~ }|
{0:~ }|
- {0:~ }|
|
]])
end) -- level 3 - non wrapped
@@ -1008,49 +1138,94 @@ describe('ui/mouse/input', function()
it('(level 3) click on wrapped lines', function()
feed_command('let &conceallevel=3', 'let &wrap=1', 'echo')
- feed('<esc><LeftMouse><0,0>')
+ feed('<esc><LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><18,1>')
screen:expect([[
- ^rem ipum do it |
- , conetetur adipcin |
- elitr. |
- tet ta kad beren |
- , no ea takimata anctu |
- et. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><6,1>')
+ feed('<esc><LeftMouse><1,2>')
screen:expect([[
- rem ipum do it |
- , cone^tetur adipcin |
- elitr. |
- tet ta kad beren |
- , no ea takimata anctu |
- et. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t^4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,1>')
+ feed('<esc><LeftMouse><0,3>')
screen:expect([[
- rem ipum do it |
- , conetetur adi^pcin |
- elitr. |
- tet ta kad beren |
- , no ea takimata anctu |
- et. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ ^ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
|
]])
- feed('<esc><LeftMouse><15,3>')
+ feed('<esc><LeftMouse><20,3>')
screen:expect([[
- rem ipum do it |
- , conetetur adipcin |
- elitr. |
- tet ta kad bere^n |
- , no ea takimata anctu |
- et. |
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----}^ |
+ ✨🐈✨ |
+ |
|
]])
+
+ feed('<esc><LeftMouse><1,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ^✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><3,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨^🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><5,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈^✨ |
+ |
+ |
+ ]])
+
end) -- level 3 - wrapped
end)
end)
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index b15a577585..9f273e8dc9 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local feed_command = helpers.feed_command
+local eq = helpers.eq
+local eval = helpers.eval
describe('search highlighting', function()
local screen
@@ -186,6 +188,7 @@ describe('search highlighting', function()
{1:~ }|
|
]])
+ eq('lit', eval('@/'))
-- cancelling inc search restores the hl state
feed(':noh<cr>')