aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
33 files changed, 592 insertions, 225 deletions
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)) {