aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c46
-rw-r--r--src/nvim/api/vim.c24
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/buffer.h49
-rw-r--r--src/nvim/edit.c36
-rw-r--r--src/nvim/eval.c26
-rw-r--r--src/nvim/ex_cmds.c12
-rw-r--r--src/nvim/ex_cmds2.c15
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/ex_getln.c4
-rw-r--r--src/nvim/fileio.c253
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/highlight.c7
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/mark.c23
-rw-r--r--src/nvim/menu.c20
-rw-r--r--src/nvim/misc1.c7
-rw-r--r--src/nvim/normal.c11
-rw-r--r--src/nvim/ops.c31
-rw-r--r--src/nvim/option_defs.h9
-rw-r--r--src/nvim/os/fs.c6
-rw-r--r--src/nvim/pos.h1
-rw-r--r--src/nvim/quickfix.c5
-rw-r--r--src/nvim/regexp.c175
-rw-r--r--src/nvim/regexp_defs.h8
-rw-r--r--src/nvim/regexp_nfa.c80
-rw-r--r--src/nvim/screen.c8
-rw-r--r--src/nvim/search.c67
-rw-r--r--src/nvim/spell.c5
-rw-r--r--src/nvim/syntax.c3
-rw-r--r--src/nvim/tag.c25
-rw-r--r--src/nvim/terminal.c14
-rw-r--r--src/nvim/testdir/test50.in89
-rw-r--r--src/nvim/testdir/test50.ok14
-rw-r--r--src/nvim/testdir/test_autocmd.vim10
-rw-r--r--src/nvim/testdir/test_findfile.vim16
-rw-r--r--src/nvim/testdir/test_functions.vim8
-rw-r--r--src/nvim/testdir/test_gf.vim7
-rw-r--r--src/nvim/testdir/test_hlsearch.vim19
-rw-r--r--src/nvim/testdir/test_marks.vim41
-rw-r--r--src/nvim/testdir/test_profile.vim41
-rw-r--r--src/nvim/testdir/test_regex_char_classes.vim240
-rw-r--r--src/nvim/testdir/test_regexp_latin.vim26
-rw-r--r--src/nvim/testdir/test_statusline.vim2
-rw-r--r--src/nvim/testdir/test_substitute.vim84
-rw-r--r--src/nvim/testdir/test_textformat.vim323
-rw-r--r--src/nvim/testdir/test_true_false.vim5
-rw-r--r--src/nvim/testdir/test_visual.vim103
-rw-r--r--src/nvim/testdir/test_window_cmd.vim128
-rw-r--r--src/nvim/tui/input.c20
-rw-r--r--src/nvim/tui/tui.c2
-rw-r--r--src/nvim/ui_compositor.c1
-rw-r--r--src/nvim/window.c48
53 files changed, 1644 insertions, 565 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 9cd178eaeb..3613a8f8bc 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -380,8 +380,6 @@ void nvim_buf_set_lines(uint64_t channel_id,
}
}
- win_T *save_curwin = NULL;
- tabpage_T *save_curtab = NULL;
size_t new_len = replacement.size;
size_t old_len = (size_t)(end - start);
ptrdiff_t extra = 0; // lines added to text, can be negative
@@ -397,8 +395,8 @@ void nvim_buf_set_lines(uint64_t channel_id,
}
try_start();
- bufref_T save_curbuf = { NULL, 0, 0 };
- switch_to_win_for_buf(buf, &save_curwin, &save_curtab, &save_curbuf);
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, (buf_T *)buf);
if (u_save((linenr_T)(start - 1), (linenr_T)end) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to save undo information");
@@ -465,27 +463,21 @@ void nvim_buf_set_lines(uint64_t channel_id,
// changed range, and move any in the remainder of the buffer.
// Only adjust marks if we managed to switch to a window that holds
// the buffer, otherwise line numbers will be invalid.
- if (save_curbuf.br_buf == NULL) {
- mark_adjust((linenr_T)start,
- (linenr_T)(end - 1),
- MAXLNUM,
- (long)extra,
- false);
- }
+ mark_adjust((linenr_T)start,
+ (linenr_T)(end - 1),
+ MAXLNUM,
+ (long)extra,
+ false);
changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra, true);
- if (save_curbuf.br_buf == NULL) {
- fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
- }
-
end:
for (size_t i = 0; i < new_len; i++) {
xfree(lines[i]);
}
xfree(lines);
- restore_win_for_buf(save_curwin, save_curtab, &save_curbuf);
+ aucmd_restbuf(&aco);
try_end(err);
}
@@ -1109,28 +1101,6 @@ free_exit:
return 0;
}
-// Check if deleting lines made the cursor position invalid.
-// Changed the lines from "lo" to "hi" and added "extra" lines (negative if
-// deleted).
-static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
-{
- if (curwin->w_cursor.lnum >= lo) {
- // Adjust the cursor position if it's in/after the changed
- // lines.
- if (curwin->w_cursor.lnum >= hi) {
- curwin->w_cursor.lnum += extra;
- check_cursor_col();
- } else if (extra < 0) {
- curwin->w_cursor.lnum = lo;
- check_cursor();
- } else {
- check_cursor_col();
- }
- changed_cline_bef_curs();
- }
- invalidate_botline();
-}
-
// Normalizes 0-based indexes to buffer line numbers
static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob)
{
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 94d1697083..a773234ea0 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -15,6 +15,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/buffer.h"
+#include "nvim/api/window.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/lua/executor.h"
@@ -31,6 +32,7 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/fileio.h"
#include "nvim/option.h"
#include "nvim/state.h"
#include "nvim/syntax.h"
@@ -978,11 +980,12 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
return 0;
}
if (scratch) {
- WITH_BUFFER(buf, {
- set_option_value("bh", 0L, "hide", OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
- });
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ set_option_value("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ aucmd_restbuf(&aco);
}
return buf->b_fnum;
}
@@ -995,6 +998,8 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// GUI with the |ui-multigrid| extension. External windows are only supported
/// with multigrid GUIs, and are displayed as separate top-level windows.
///
+/// For a general overview of floats, see |api-floatwin|.
+///
/// Exactly one of `external` and `relative` must be specified.
///
/// @param buffer handle of buffer to be displayed in the window
@@ -1047,7 +1052,6 @@ Window nvim_open_win(Buffer buffer, Boolean enter,
Dictionary options, Error *err)
FUNC_API_SINCE(6)
{
- win_T *old = curwin;
FloatConfig config = FLOAT_CONFIG_INIT;
if (!parse_float_config(options, &config, false, err)) {
return 0;
@@ -1056,11 +1060,11 @@ Window nvim_open_win(Buffer buffer, Boolean enter,
if (!wp) {
return 0;
}
- if (buffer > 0) {
- nvim_set_current_buf(buffer, err);
+ if (enter) {
+ win_enter(wp, false);
}
- if (!enter) {
- win_enter(old, false);
+ if (buffer > 0) {
+ nvim_win_set_buf(wp->handle, buffer, err);
}
return wp->handle;
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6e4e7afeb2..8cb4e32815 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -2446,7 +2446,7 @@ void get_winopts(buf_T *buf)
*/
pos_T *buflist_findfpos(buf_T *buf)
{
- static pos_T no_position = INIT_POS_T(1, 0, 0);
+ static pos_T no_position = { 1, 0, 0 };
wininfo_T *wip = find_wininfo(buf, FALSE);
return (wip == NULL) ? &no_position : &(wip->wi_fpos);
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 79bed049ea..ee3fda5f6d 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -63,35 +63,6 @@ enum bfa_values {
# include "buffer.h.generated.h"
#endif
-// Find a window that contains "buf" and switch to it.
-// If there is no such window, use the current window and change "curbuf".
-// Caller must initialize save_curbuf to NULL.
-// restore_win_for_buf() MUST be called later!
-static inline void switch_to_win_for_buf(buf_T *buf,
- win_T **save_curwinp,
- tabpage_T **save_curtabp,
- bufref_T *save_curbuf)
-{
- win_T *wp;
- tabpage_T *tp;
-
- if (!find_win_for_buf(buf, &wp, &tp)
- || switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL) {
- switch_buffer(save_curbuf, buf);
- }
-}
-
-static inline void restore_win_for_buf(win_T *save_curwin,
- tabpage_T *save_curtab,
- bufref_T *save_curbuf)
-{
- if (save_curbuf->br_buf == NULL) {
- restore_win(save_curwin, save_curtab, true);
- } else {
- restore_buffer(save_curbuf);
- }
-}
-
static inline void buf_set_changedtick(buf_T *const buf,
const varnumber_T changedtick)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE;
@@ -103,6 +74,8 @@ static inline void buf_set_changedtick(buf_T *const buf,
static inline void buf_set_changedtick(buf_T *const buf,
const varnumber_T changedtick)
{
+ typval_T old_val = buf->changedtick_di.di_tv;
+
#ifndef NDEBUG
dictitem_T *const changedtick_di = tv_dict_find(
buf->b_vars, S_LEN("changedtick"));
@@ -116,6 +89,13 @@ static inline void buf_set_changedtick(buf_T *const buf,
assert(changedtick_di == (dictitem_T *)&buf->changedtick_di);
#endif
buf->changedtick_di.di_tv.vval.v_number = changedtick;
+
+ if (tv_dict_is_watched(buf->b_vars)) {
+ tv_dict_watcher_notify(buf->b_vars,
+ (char *)buf->changedtick_di.di_key,
+ &buf->changedtick_di.di_tv,
+ &old_val);
+ }
}
static inline varnumber_T buf_get_changedtick(const buf_T *const buf)
@@ -145,15 +125,4 @@ static inline void buf_inc_changedtick(buf_T *const buf)
buf_set_changedtick(buf, buf_get_changedtick(buf) + 1);
}
-#define WITH_BUFFER(b, code) \
- do { \
- win_T *save_curwin = NULL; \
- tabpage_T *save_curtab = NULL; \
- bufref_T save_curbuf = { NULL, 0, 0 }; \
- switch_to_win_for_buf(b, &save_curwin, &save_curtab, &save_curbuf); \
- code; \
- restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); \
- } while (0)
-
-
#endif // NVIM_BUFFER_H
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 6f0468dbea..f6b5a01915 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -3924,10 +3924,11 @@ static int ins_compl_get_exp(pos_T *ini)
compl_direction, compl_pattern);
} else
found_new_match = searchit(NULL, ins_buf, pos,
- compl_direction,
- compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG,
- RE_LAST, (linenr_T)0, NULL);
- --msg_silent;
+ compl_direction,
+ compl_pattern, 1L,
+ SEARCH_KEEP + SEARCH_NFMSG,
+ RE_LAST, (linenr_T)0, NULL, NULL);
+ msg_silent--;
if (!compl_started || set_match_pos) {
/* set "compl_started" even on fail */
compl_started = TRUE;
@@ -5508,16 +5509,33 @@ internal_format (
/* remember position of blank just before text */
end_col = curwin->w_cursor.col;
- /* find start of sequence of blanks */
+ // find start of sequence of blanks
+ int wcc = 0; // counter for whitespace chars
while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
dec_cursor();
cc = gchar_cursor();
+
+ // Increment count of how many whitespace chars in this
+ // group; we only need to know if it's more than one.
+ if (wcc < 2) {
+ wcc++;
+ }
}
- if (curwin->w_cursor.col == 0 && WHITECHAR(cc))
- break; /* only spaces in front of text */
- /* Don't break until after the comment leader */
- if (curwin->w_cursor.col < leader_len)
+ if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
+ break; // only spaces in front of text
+ }
+
+ // Don't break after a period when 'formatoptions' has 'p' and
+ // there are less than two spaces.
+ if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
+ continue;
+ }
+
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
break;
+ }
+
if (has_format_option(FO_ONE_LETTER)) {
/* do not break after one-letter words */
if (curwin->w_cursor.col == 0)
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d18884ff07..5ef2a8772e 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -10086,10 +10086,23 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
tv_list_append_number(
l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0);
if (getcurpos) {
+ const int save_set_curswant = curwin->w_set_curswant;
+ const colnr_T save_curswant = curwin->w_curswant;
+ const colnr_T save_virtcol = curwin->w_virtcol;
+
update_curswant();
tv_list_append_number(l, (curwin->w_curswant == MAXCOL
? (varnumber_T)MAXCOL
: (varnumber_T)curwin->w_curswant + 1));
+
+ // Do not change "curswant", as it is unexpected that a get
+ // function has a side effect.
+ if (save_set_curswant) {
+ curwin->w_set_curswant = save_set_curswant;
+ curwin->w_curswant = save_curswant;
+ curwin->w_virtcol = save_virtcol;
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
}
}
@@ -10303,6 +10316,8 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow);
+ tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
+ tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol);
@@ -13793,7 +13808,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
pos = save_cursor = curwin->w_cursor;
subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1,
- options, RE_SEARCH, (linenr_T)lnum_stop, &tm);
+ options, RE_SEARCH, (linenr_T)lnum_stop, &tm, NULL);
if (subpatnum != FAIL) {
if (flags & SP_SUBPAT)
retval = subpatnum;
@@ -14295,10 +14310,11 @@ do_searchpair(
pat = pat3;
for (;; ) {
n = searchit(curwin, curbuf, &pos, dir, pat, 1L,
- options, RE_SEARCH, lnum_stop, &tm);
- if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos)))
- /* didn't find it or found the first match again: FAIL */
+ options, RE_SEARCH, lnum_stop, &tm, NULL);
+ if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
+ // didn't find it or found the first match again: FAIL
break;
+ }
if (firstpos.lnum == 0)
firstpos = pos;
@@ -20144,7 +20160,7 @@ void ex_function(exarg_T *eap)
skip_until = vim_strsave((char_u *)".");
}
- // Check for ":python <<EOF", ":lua <<EOF", etc.
+ // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] =='<'
&& ((p[0] == 'p' && p[1] == 'y'
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 2a5793f0d4..4356767cc9 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3432,7 +3432,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
|| lnum <= curwin->w_botline);
lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL);
+ (colnr_T)0, NULL, NULL);
if (nmatch) {
colnr_T copycol;
colnr_T matchcol;
@@ -3951,8 +3951,8 @@ skip:
if (lastone
|| nmatch_tl > 0
|| (nmatch = vim_regexec_multi(&regmatch, curwin,
- curbuf, sub_firstlnum,
- matchcol, NULL)) == 0
+ curbuf, sub_firstlnum,
+ matchcol, NULL, NULL)) == 0
|| regmatch.startpos[0].lnum > 0) {
if (new_start != NULL) {
/*
@@ -4016,7 +4016,7 @@ skip:
}
if (nmatch == -1 && !lastone)
nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
- sub_firstlnum, matchcol, NULL);
+ sub_firstlnum, matchcol, NULL, NULL);
/*
* 5. break if there isn't another match in this line
@@ -4314,7 +4314,7 @@ void ex_global(exarg_T *eap)
if (global_busy) {
lnum = curwin->w_cursor.lnum;
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL);
+ (colnr_T)0, NULL, NULL);
if ((type == 'g' && match) || (type == 'v' && !match)) {
global_exe_one(cmd, lnum);
}
@@ -4323,7 +4323,7 @@ void ex_global(exarg_T *eap)
for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) {
// a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL);
+ (colnr_T)0, NULL, NULL);
if ((type == 'g' && match) || (type == 'v' && !match)) {
ml_setmarked(lnum);
ndone++;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 0ecc389699..4684a1b31d 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1152,6 +1152,21 @@ static void script_dump_profile(FILE *fd)
if (vim_fgets(IObuff, IOSIZE, sfd)) {
break;
}
+ // When a line has been truncated, append NL, taking care
+ // of multi-byte characters .
+ if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
+ int n = IOSIZE - 2;
+
+ // Move to the first byte of this char.
+ // utf_head_off() doesn't work, because it checks
+ // for a truncated character.
+ while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
+ n--;
+ }
+
+ IObuff[n] = NL;
+ IObuff[n + 1] = NUL;
+ }
if (i < si->sn_prl_ga.ga_len
&& (pp = &PRL_ITEM(si, i))->snp_count > 0) {
fprintf(fd, "%5d ", pp->snp_count);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 6b39ad8e87..5b9b4fed12 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3699,7 +3699,7 @@ static linenr_T get_address(exarg_T *eap,
}
searchcmdlen = 0;
if (!do_search(NULL, c, cmd, 1L,
- SEARCH_HIS | SEARCH_MSG, NULL)) {
+ SEARCH_HIS | SEARCH_MSG, NULL, NULL)) {
curwin->w_cursor = pos;
cmd = NULL;
goto error;
@@ -3737,7 +3737,7 @@ static linenr_T get_address(exarg_T *eap,
if (searchit(curwin, curbuf, &pos,
*cmd == '?' ? BACKWARD : FORWARD,
(char_u *)"", 1L, SEARCH_MSG,
- i, (linenr_T)0, NULL) != FAIL)
+ i, (linenr_T)0, NULL, NULL) != FAIL)
lnum = pos.lnum;
else {
cmd = NULL;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a50e18efce..8e6fc5ad4f 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1061,7 +1061,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
s->i = searchit(curwin, curbuf, &t,
next_match ? FORWARD : BACKWARD,
pat, s->count, search_flags,
- RE_SEARCH, 0, NULL);
+ RE_SEARCH, 0, NULL, NULL);
emsg_off--;
ui_busy_stop();
if (s->i) {
@@ -1847,7 +1847,7 @@ static int command_line_changed(CommandLineState *s)
}
s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
search_flags,
- &tm);
+ &tm, NULL);
emsg_off--;
// if interrupted while searching, behave like it failed
if (got_int) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index b9de46efc8..7be4107c94 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -99,8 +99,9 @@
// defined and will have to be executed.
//
typedef struct AutoCmd {
- char_u *cmd; // The command to be executed (NULL
- // when command has been removed)
+ char_u *cmd; // Command to be executed (NULL when
+ // command has been removed)
+ bool once; // "One shot": removed after execution
char nested; // If autocommands nest here
char last; // last command in list
scid_T scriptID; // script ID where defined
@@ -121,20 +122,20 @@ typedef struct AutoPat {
char last; // last pattern for apply_autocmds()
} AutoPat;
-/*
- * struct used to keep status while executing autocommands for an event.
- */
+///
+/// Struct used to keep status while executing autocommands for an event.
+///
typedef struct AutoPatCmd {
- AutoPat *curpat; /* next AutoPat to examine */
- AutoCmd *nextcmd; /* next AutoCmd to execute */
- int group; /* group being used */
- char_u *fname; /* fname to match with */
- char_u *sfname; /* sfname to match with */
- char_u *tail; /* tail of fname */
- event_T event; /* current event */
- int arg_bufnr; /* initially equal to <abuf>, set to zero when
- buf is deleted */
- struct AutoPatCmd *next; /* chain of active apc-s for auto-invalidation*/
+ AutoPat *curpat; // next AutoPat to examine
+ AutoCmd *nextcmd; // next AutoCmd to execute
+ int group; // group being used
+ char_u *fname; // fname to match with
+ char_u *sfname; // sfname to match with
+ char_u *tail; // tail of fname
+ event_T event; // current event
+ int arg_bufnr; // initially equal to <abuf>, set to zero when
+ // buf is deleted
+ struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation
} AutoPatCmd;
#define AUGROUP_DEFAULT -1 /* default autocmd group */
@@ -5563,64 +5564,75 @@ static void show_autocmd(AutoPat *ap, event_T event)
}
}
-/*
- * Mark an autocommand pattern for deletion.
- */
+// Mark an autocommand handler for deletion.
static void au_remove_pat(AutoPat *ap)
{
xfree(ap->pat);
ap->pat = NULL;
ap->buflocal_nr = -1;
- au_need_clean = TRUE;
+ au_need_clean = true;
}
-/*
- * Mark all commands for a pattern for deletion.
- */
+// Mark all commands for a pattern for deletion.
static void au_remove_cmds(AutoPat *ap)
{
- AutoCmd *ac;
-
- for (ac = ap->cmds; ac != NULL; ac = ac->next) {
+ for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
xfree(ac->cmd);
ac->cmd = NULL;
}
- au_need_clean = TRUE;
+ au_need_clean = true;
}
-/*
- * Cleanup autocommands and patterns that have been deleted.
- * This is only done when not executing autocommands.
- */
+// Delete one command from an autocmd pattern.
+static void au_del_cmd(AutoCmd *ac)
+{
+ xfree(ac->cmd);
+ ac->cmd = NULL;
+ au_need_clean = true;
+}
+
+/// Cleanup autocommands and patterns that have been deleted.
+/// This is only done when not executing autocommands.
static void au_cleanup(void)
{
AutoPat *ap, **prev_ap;
AutoCmd *ac, **prev_ac;
event_T event;
- if (autocmd_busy || !au_need_clean)
+ if (autocmd_busy || !au_need_clean) {
return;
+ }
- /* loop over all events */
+ // Loop over all events.
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1)) {
- /* loop over all autocommand patterns */
+ // Loop over all autocommand patterns.
prev_ap = &(first_autopat[(int)event]);
for (ap = *prev_ap; ap != NULL; ap = *prev_ap) {
- /* loop over all commands for this pattern */
+ // Loop over all commands for this pattern.
prev_ac = &(ap->cmds);
+ bool has_cmd = false;
+
for (ac = *prev_ac; ac != NULL; ac = *prev_ac) {
- /* remove the command if the pattern is to be deleted or when
- * the command has been marked for deletion */
+ // Remove the command if the pattern is to be deleted or when
+ // the command has been marked for deletion.
if (ap->pat == NULL || ac->cmd == NULL) {
*prev_ac = ac->next;
xfree(ac->cmd);
xfree(ac);
- } else
+ } else {
+ has_cmd = true;
prev_ac = &(ac->next);
+ }
+ }
+
+ if (ap->pat != NULL && !has_cmd) {
+ // Pattern was not marked for deletion, but all of its commands were.
+ // So mark the pattern for deletion.
+ au_remove_pat(ap);
}
- /* remove the pattern if it has been marked for deletion */
+ // Remove the pattern if it has been marked for deletion.
if (ap->pat == NULL) {
if (ap->next == NULL) {
if (prev_ap == &(first_autopat[(int)event])) {
@@ -5634,12 +5646,13 @@ static void au_cleanup(void)
*prev_ap = ap->next;
vim_regfree(ap->reg_prog);
xfree(ap);
- } else
+ } else {
prev_ap = &(ap->next);
+ }
}
}
- au_need_clean = FALSE;
+ au_need_clean = false;
}
/*
@@ -5674,18 +5687,18 @@ void aubuflocal_remove(buf_T *buf)
au_cleanup();
}
-/*
- * Add an autocmd group name.
- * Return it's ID. Returns AUGROUP_ERROR (< 0) for error.
- */
+// Add an autocmd group name.
+// Return its ID. Returns AUGROUP_ERROR (< 0) for error.
static int au_new_group(char_u *name)
{
int i = au_find_group(name);
- if (i == AUGROUP_ERROR) { /* the group doesn't exist yet, add it */
- /* First try using a free entry. */
- for (i = 0; i < augroups.ga_len; ++i)
- if (AUGROUP_NAME(i) == NULL)
+ if (i == AUGROUP_ERROR) { // the group doesn't exist yet, add it.
+ // First try using a free entry.
+ for (i = 0; i < augroups.ga_len; i++) {
+ if (AUGROUP_NAME(i) == NULL) {
break;
+ }
+ }
if (i == augroups.ga_len) {
ga_grow(&augroups, 1);
}
@@ -5701,9 +5714,7 @@ static int au_new_group(char_u *name)
static void au_del_group(char_u *name)
{
- int i;
-
- i = au_find_group(name);
+ int i = au_find_group(name);
if (i == AUGROUP_ERROR) { // the group doesn't exist
EMSG2(_("E367: No such group: \"%s\""), name);
} else if (i == current_augroup) {
@@ -5760,23 +5771,22 @@ bool au_has_group(const char_u *name)
return au_find_group(name) != AUGROUP_ERROR;
}
-/*
- * ":augroup {name}".
- */
+/// ":augroup {name}".
void do_augroup(char_u *arg, int del_group)
{
if (del_group) {
- if (*arg == NUL)
+ if (*arg == NUL) {
EMSG(_(e_argreq));
- else
+ } else {
au_del_group(arg);
- } else if (STRICMP(arg, "end") == 0) /* ":aug end": back to group 0 */
+ }
+ } else if (STRICMP(arg, "end") == 0) { // ":aug end": back to group 0
current_augroup = AUGROUP_DEFAULT;
- else if (*arg) { /* ":aug xxx": switch to group xxx */
+ } else if (*arg) { // ":aug xxx": switch to group xxx
int i = au_new_group(arg);
if (i != AUGROUP_ERROR)
current_augroup = i;
- } else { /* ":aug": list the group names */
+ } else { // ":aug": list the group names
msg_start();
for (int i = 0; i < augroups.ga_len; ++i) {
if (AUGROUP_NAME(i) != NULL) {
@@ -5957,38 +5967,38 @@ void au_event_restore(char_u *old_ei)
}
}
-/*
- * do_autocmd() -- implements the :autocmd command. Can be used in the
- * following ways:
- *
- * :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
- * will be automatically executed for <event>
- * when editing a file matching <pat>, in
- * the current group.
- * :autocmd <event> <pat> Show the autocommands associated with
- * <event> and <pat>.
- * :autocmd <event> Show the autocommands associated with
- * <event>.
- * :autocmd Show all autocommands.
- * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
- * <event> and <pat>, and add the command
- * <cmd>, for the current group.
- * :autocmd! <event> <pat> Remove all autocommands associated with
- * <event> and <pat> for the current group.
- * :autocmd! <event> Remove all autocommands associated with
- * <event> for the current group.
- * :autocmd! Remove ALL autocommands for the current
- * group.
- *
- * Multiple events and patterns may be given separated by commas. Here are
- * some examples:
- * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
- * :autocmd bufleave * set tw=79 nosmartindent ic infercase
- *
- * :autocmd * *.c show all autocommands for *.c files.
- *
- * Mostly a {group} argument can optionally appear before <event>.
- */
+// Implements :autocmd.
+// Defines an autocmd (does not execute; cf. apply_autocmds_group).
+//
+// Can be used in the following ways:
+//
+// :autocmd <event> <pat> <cmd> Add <cmd> to the list of commands that
+// will be automatically executed for <event>
+// when editing a file matching <pat>, in
+// the current group.
+// :autocmd <event> <pat> Show the autocommands associated with
+// <event> and <pat>.
+// :autocmd <event> Show the autocommands associated with
+// <event>.
+// :autocmd Show all autocommands.
+// :autocmd! <event> <pat> <cmd> Remove all autocommands associated with
+// <event> and <pat>, and add the command
+// <cmd>, for the current group.
+// :autocmd! <event> <pat> Remove all autocommands associated with
+// <event> and <pat> for the current group.
+// :autocmd! <event> Remove all autocommands associated with
+// <event> for the current group.
+// :autocmd! Remove ALL autocommands for the current
+// group.
+//
+// Multiple events and patterns may be given separated by commas. Here are
+// some examples:
+// :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic
+// :autocmd bufleave * set tw=79 nosmartindent ic infercase
+//
+// :autocmd * *.c show all autocommands for *.c files.
+//
+// Mostly a {group} argument can optionally appear before <event>.
void do_autocmd(char_u *arg_in, int forceit)
{
char_u *arg = arg_in;
@@ -5997,6 +6007,7 @@ void do_autocmd(char_u *arg_in, int forceit)
char_u *cmd;
int need_free = false;
int nested = false;
+ bool once = false;
int group;
if (*arg == '|') {
@@ -6046,12 +6057,23 @@ void do_autocmd(char_u *arg_in, int forceit)
}
}
- // Check for "nested" flag.
cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
- && ascii_iswhite(cmd[6])) {
- nested = true;
- cmd = skipwhite(cmd + 6);
+ for (size_t i = 0; i < 2; i++) {
+ if (*cmd != NUL) {
+ // Check for "++once" flag.
+ if (!once && STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) {
+ once = true;
+ cmd = skipwhite(cmd + 6);
+ }
+ // Check for "++nested" flag.
+ if (!nested
+ && ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))
+ // Deprecated form (without "++").
+ || (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])))) {
+ nested = true;
+ cmd = skipwhite(cmd + ('+' == cmd[0] ? 8 : 6));
+ }
+ }
}
// Find the start of the commands.
@@ -6081,7 +6103,8 @@ void do_autocmd(char_u *arg_in, int forceit)
if (*arg == '*' || *arg == NUL || *arg == '|') {
for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1)) {
- if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
+ == FAIL) {
break;
}
}
@@ -6089,7 +6112,8 @@ void do_autocmd(char_u *arg_in, int forceit)
while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
- if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
+ if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group)
+ == FAIL) {
break;
}
}
@@ -6127,14 +6151,15 @@ static int au_get_grouparg(char_u **argp)
return group;
}
-/*
- * do_autocmd() for one event.
- * If *pat == NUL do for all patterns.
- * If *cmd == NUL show entries.
- * If forceit == TRUE delete entries.
- * If group is not AUGROUP_ALL, only use this group.
- */
-static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group)
+// do_autocmd() for one event.
+// Defines an autocmd (does not execute; cf. apply_autocmds_group).
+//
+// If *pat == NUL: do for all patterns.
+// If *cmd == NUL: show entries.
+// If forceit == TRUE: delete entries.
+// If group is not AUGROUP_ALL: only use this group.
+static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested,
+ char_u *cmd, int forceit, int group)
{
AutoPat *ap;
AutoPat **prev_ap;
@@ -6333,6 +6358,7 @@ static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd,
ac->scriptID = current_SID;
ac->next = NULL;
*prev_ac = ac;
+ ac->once = once;
ac->nested = nested;
}
}
@@ -6996,7 +7022,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
patcmd.event = event;
patcmd.arg_bufnr = autocmd_bufnr;
patcmd.next = NULL;
- auto_next_pat(&patcmd, FALSE);
+ auto_next_pat(&patcmd, false);
/* found one, start executing the autocommands */
if (patcmd.curpat != NULL) {
@@ -7020,8 +7046,11 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
}
ap->last = true;
check_lnums(true); // make sure cursor and topline are valid
+
+ // Execute the autocmd. The `getnextac` callback handles iteration.
do_cmdline(NULL, getnextac, (void *)&patcmd,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
+
if (eap != NULL) {
(void)set_cmdarg(NULL, save_cmdarg);
set_vim_var_nr(VV_CMDBANG, save_cmdbang);
@@ -7233,12 +7262,18 @@ char_u *getnextac(int c, void *cookie, int indent)
verbose_leave_scroll();
}
retval = vim_strsave(ac->cmd);
+ // Remove one-shot ("once") autocmd in anticipation of its execution.
+ if (ac->once) {
+ au_del_cmd(ac);
+ }
autocmd_nested = ac->nested;
current_SID = ac->scriptID;
- if (ac->last)
+ if (ac->last) {
acp->nextcmd = NULL;
- else
+ } else {
acp->nextcmd = ac->next;
+ }
+
return retval;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 52c5d65512..004b3252da 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -602,7 +602,7 @@ EXTERN bool can_si INIT(= false);
EXTERN bool can_si_back INIT(= false);
// w_cursor before formatting text.
-EXTERN pos_T saved_cursor INIT(= INIT_POS_T(0, 0, 0));
+EXTERN pos_T saved_cursor INIT(= { 0, 0, 0 });
/*
* Stuff for insert mode.
@@ -789,7 +789,7 @@ EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd
// for CursorMoved event
-EXTERN pos_T last_cursormoved INIT(= INIT_POS_T(0, 0, 0));
+EXTERN pos_T last_cursormoved INIT(= { 0, 0, 0 });
EXTERN int postponed_split INIT(= 0); /* for CTRL-W CTRL-] command */
EXTERN int postponed_split_flags INIT(= 0); /* args for win_split() */
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 4c5fca6d39..3ba02be32d 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -160,14 +160,19 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_needs_update = false;
// determine window specific background set in 'winhighlight'
+ bool float_win = wp->w_floating && !wp->w_float_config.external;
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
wp->w_hl_ids[HLF_INACTIVE], true);
+ } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) {
+ wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
+ wp->w_hl_ids[HLF_NFLOAT], true);
} else if (wp->w_hl_id_normal > 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, true);
} else {
- wp->w_hl_attr_normal = 0;
+ wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
+
if (wp != curwin) {
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 1da33bfea5..746d2c2dfc 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -90,6 +90,7 @@ typedef enum {
, HLF_0 // Whitespace
, HLF_INACTIVE // NormalNC: Normal text in non-current windows
, HLF_MSGSEP // message separator line
+ , HLF_NFLOAT // Floating window
, HLF_COUNT // MUST be the last one
} hlf_T;
@@ -142,6 +143,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_0] = "Whitespace",
[HLF_INACTIVE] = "NormalNC",
[HLF_MSGSEP] = "MsgSeparator",
+ [HLF_NFLOAT] = "NormalFloat",
});
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 05f78c76bc..af404c354b 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -924,7 +924,7 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
int i;
int fnum = curbuf->b_fnum;
linenr_T *lp;
- static pos_T initpos = INIT_POS_T(1, 0, 0);
+ static pos_T initpos = { 1, 0, 0 };
if (line2 < line1 && amount_after == 0L) /* nothing to do */
return;
@@ -1069,19 +1069,24 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2,
{ \
posp->lnum += lnum_amount; \
assert(col_amount > INT_MIN && col_amount <= INT_MAX); \
- if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
+ if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \
posp->col = 0; \
- else \
+ } else if (posp->col < spaces_removed) { \
+ posp->col = (int)col_amount + spaces_removed; \
+ } else { \
posp->col += (colnr_T)col_amount; \
+ } \
} \
}
-/*
- * Adjust marks in line "lnum" at column "mincol" and further: add
- * "lnum_amount" to the line number and add "col_amount" to the column
- * position.
- */
-void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount)
+// Adjust marks in line "lnum" at column "mincol" and further: add
+// "lnum_amount" to the line number and add "col_amount" to the column
+// position.
+// "spaces_removed" is the number of spaces that were removed, matters when the
+// cursor is inside them.
+void mark_col_adjust(
+ linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount,
+ int spaces_removed)
{
int i;
int fnum = curbuf->b_fnum;
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index aea297fce2..523bb20738 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -171,7 +171,7 @@ ex_menu(exarg_T *eap)
if (enable != kNone) {
// Change sensitivity of the menu.
// For the PopUp menu, remove a menu for each mode separately.
- // Careful: menu_nable_recurse() changes menu_path.
+ // Careful: menu_enable_recurse() changes menu_path.
if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus
menu_path = (char_u *)"";
}
@@ -180,11 +180,11 @@ ex_menu(exarg_T *eap)
for (i = 0; i < MENU_INDEX_TIP; ++i)
if (modes & (1 << i)) {
p = popup_mode_name(menu_path, i);
- menu_nable_recurse(root_menu, p, MENU_ALL_MODES, enable);
+ menu_enable_recurse(root_menu, p, MENU_ALL_MODES, enable);
xfree(p);
}
}
- menu_nable_recurse(root_menu, menu_path, modes, enable);
+ menu_enable_recurse(root_menu, menu_path, modes, enable);
} else if (unmenu) {
/*
* Delete menu(s).
@@ -485,7 +485,10 @@ erret:
* Set the (sub)menu with the given name to enabled or disabled.
* Called recursively.
*/
-static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enable)
+static int menu_enable_recurse(vimmenu_T *menu,
+ char_u *name,
+ int modes,
+ int enable)
{
char_u *p;
@@ -503,13 +506,14 @@ static int menu_nable_recurse(vimmenu_T *menu, char_u *name, int modes, int enab
EMSG(_(e_notsubmenu));
return FAIL;
}
- if (menu_nable_recurse(menu->children, p, modes, enable)
- == FAIL)
+ if (menu_enable_recurse(menu->children, p, modes, enable) == FAIL) {
return FAIL;
- } else if (enable)
+ }
+ } else if (enable) {
menu->enabled |= modes;
- else
+ } else {
menu->enabled &= ~modes;
+ }
/*
* When name is empty, we are doing all menu items for the given
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index a8cfc2d700..64a4b8b0b4 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -848,10 +848,11 @@ open_line (
/* Move marks after the line break to the new line. */
if (flags & OPENLINE_MARKFIX)
mark_col_adjust(curwin->w_cursor.lnum,
- curwin->w_cursor.col + less_cols_off,
- 1L, (long)-less_cols);
- } else
+ curwin->w_cursor.col + less_cols_off,
+ 1L, (long)-less_cols, 0);
+ } else {
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
+ }
}
/*
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 49eef72a05..f12abd362f 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3782,9 +3782,10 @@ find_decl (
valid = false;
(void)valid; // Avoid "dead assignment" warning.
t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD,
- pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL);
- if (curwin->w_cursor.lnum >= old_pos.lnum)
- t = false; /* match after start is failure too */
+ pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL);
+ if (curwin->w_cursor.lnum >= old_pos.lnum) {
+ t = false; // match after start is failure too
+ }
if (thisblock && t != false) {
const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1;
@@ -5384,7 +5385,7 @@ static int normal_search(
curwin->w_set_curswant = true;
i = do_search(cap->oap, dir, pat, cap->count1,
- opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL);
+ opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL, NULL);
if (i == 0) {
clearop(cap->oap);
} else {
@@ -5443,7 +5444,7 @@ static void nv_csearch(cmdarg_T *cap)
*/
static void nv_brackets(cmdarg_T *cap)
{
- pos_T new_pos = INIT_POS_T(0, 0, 0);
+ pos_T new_pos = { 0, 0, 0 };
pos_T prev_pos;
pos_T *pos = NULL; /* init for GCC */
pos_T old_pos; /* cursor position before command */
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 99dee939fc..02ec3aad31 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2605,17 +2605,16 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
static bool recursive = false;
if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
- // No autocommand was defined
- // or we yanked from this autocommand.
+ // No autocommand was defined, or we yanked from this autocommand.
return;
}
recursive = true;
- // set v:event to a dictionary with information about the yank
+ // Set the v:event dictionary with information about the yank.
dict_T *dict = get_vim_var_dict(VV_EVENT);
- // the yanked text
+ // The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
@@ -2623,17 +2622,21 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
- // the register type
+ // Register type.
char buf[NUMBUFLEN+2];
format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
tv_dict_add_str(dict, S_LEN("regtype"), buf);
- // name of requested register or the empty string for an unnamed operation.
+ // Name of requested register, or empty string for unnamed operation.
buf[0] = (char)oap->regname;
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("regname"), buf);
- // kind of operation (yank/delete/change)
+ // Motion type: inclusive or exclusive.
+ tv_dict_add_special(dict, S_LEN("inclusive"),
+ oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse);
+
+ // Kind of operation: yank, delete, change).
buf[0] = (char)get_op_char(oap->op_type);
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("operator"), buf);
@@ -3704,10 +3707,16 @@ int do_join(size_t count,
cend -= spaces[t];
memset(cend, ' ', (size_t)(spaces[t]));
}
+
+ // If deleting more spaces than adding, the cursor moves no more than
+ // what is added if it is inside these spaces.
+ const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
+
mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
- (long)(cend - newp + spaces[t] - (curr - curr_start)));
- if (t == 0)
+ (long)(cend - newp - spaces_removed), spaces_removed);
+ if (t == 0) {
break;
+ }
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
if (remove_comments)
curr += comments[t - 1];
@@ -4135,14 +4144,14 @@ format_lines (
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len);
+ (long)-next_leader_len, 0);
} else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
int indent = (int)getwhitecols_curline();
if (indent > 0) {
(void)del_bytes(indent, FALSE, FALSE);
mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent);
+ (colnr_T)0, 0L, (long)-indent, 0);
}
}
curwin->w_cursor.lnum--;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 6dc5e418fc..74047e7cef 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -74,13 +74,14 @@
#define FO_MBYTE_JOIN 'M' /* no space before/after multi-byte char */
#define FO_MBYTE_JOIN2 'B' /* no space between multi-byte chars */
#define FO_ONE_LETTER '1'
-#define FO_WHITE_PAR 'w' /* trailing white space continues paragr. */
-#define FO_AUTO 'a' /* automatic formatting */
-#define FO_REMOVE_COMS 'j' /* remove comment leaders when joining lines */
+#define FO_WHITE_PAR 'w' // trailing white space continues paragr.
+#define FO_AUTO 'a' // automatic formatting
+#define FO_REMOVE_COMS 'j' // remove comment leaders when joining lines
+#define FO_PERIOD_ABBR 'p' // don't break a single space after a period
#define DFLT_FO_VI "vt"
#define DFLT_FO_VIM "tcqj"
-#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */
+#define FO_ALL "tcroq2vlb1mMBn,awjp" // for do_set()
// characters for the p_cpo option:
#define CPO_ALTREAD 'a' // ":read" sets alternate file name
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 7f2ebeec2f..27db675c52 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -288,7 +288,11 @@ static bool is_executable(const char *name)
// a directory.
return (S_ISREG(mode));
#else
- return (S_ISREG(mode) && (S_IXUSR & mode));
+ int r = -1;
+ if (S_ISREG(mode)) {
+ RUN_UV_FS_FUNC(r, uv_fs_access, name, X_OK, NULL);
+ }
+ return (r == 0);
#endif
}
diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index 0a2afd5847..47d253e083 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -24,7 +24,6 @@ typedef struct {
colnr_T coladd;
} pos_T;
-# define INIT_POS_T(l, c, ca) {l, c, ca}
/*
* Same, but without coladd.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index f0c37c0e38..ee1cf2182f 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2128,8 +2128,9 @@ win_found:
save_cursor = curwin->w_cursor;
curwin->w_cursor.lnum = 0;
if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1,
- SEARCH_KEEP, NULL))
+ SEARCH_KEEP, NULL, NULL)) {
curwin->w_cursor = save_cursor;
+ }
}
if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped)
@@ -3758,7 +3759,7 @@ void ex_vimgrep(exarg_T *eap)
++lnum) {
col = 0;
while (vim_regexec_multi(&regmatch, curwin, buf, lnum,
- col, NULL) > 0) {
+ col, NULL, NULL) > 0) {
// Pass the buffer number so that it gets used even for a
// dummy buffer, unless duplicate_name is set, then the
// buffer will be wiped out below.
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 3b3ca29dad..ab1a7c7b3e 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1210,6 +1210,31 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
return p;
}
+/// Return TRUE if the back reference is legal. We must have seen the close
+/// brace.
+/// TODO(vim): Should also check that we don't refer to something repeated
+/// (+*=): what instance of the repetition should we match?
+static int seen_endbrace(int refnum)
+{
+ if (!had_endbrace[refnum]) {
+ char_u *p;
+
+ // Trick: check if "@<=" or "@<!" follows, in which case
+ // the \1 can appear before the referenced match.
+ for (p = regparse; *p != NUL; p++) {
+ if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) {
+ break;
+ }
+ }
+
+ if (*p == NUL) {
+ EMSG(_("E65: Illegal back reference"));
+ rc_did_emsg = true;
+ return false;
+ }
+ }
+ return TRUE;
+}
/*
* bt_regcomp() - compile a regular expression into internal code for the
@@ -1928,22 +1953,8 @@ static char_u *regatom(int *flagp)
int refnum;
refnum = c - Magic('0');
- /*
- * Check if the back reference is legal. We must have seen the
- * close brace.
- * TODO: Should also check that we don't refer to something
- * that is repeated (+*=): what instance of the repetition
- * should we match?
- */
- if (!had_endbrace[refnum]) {
- /* Trick: check if "@<=" or "@<!" follows, in which case
- * the \1 can appear before the referenced match. */
- for (p = regparse; *p != NUL; ++p)
- if (p[0] == '@' && p[1] == '<'
- && (p[2] == '!' || p[2] == '='))
- break;
- if (*p == NUL)
- EMSG_RET_NULL(_("E65: Illegal back reference"));
+ if (!seen_endbrace(refnum)) {
+ return NULL;
}
ret = regnode(BACKREF + refnum);
}
@@ -3297,7 +3308,7 @@ bt_regexec_nl (
rex.reg_icombine = false;
rex.reg_maxcol = 0;
- long r = bt_regexec_both(line, col, NULL);
+ long r = bt_regexec_both(line, col, NULL, NULL);
assert(r <= INT_MAX);
return (int)r;
}
@@ -3357,7 +3368,8 @@ static inline char_u *cstrchr(const char_u *const s, const int c)
/// @return zero if there is no match and number of lines contained in the match
/// otherwise.
static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
- linenr_T lnum, colnr_T col, proftime_T *tm)
+ linenr_T lnum, colnr_T col,
+ proftime_T *tm, int *timed_out)
{
rex.reg_match = NULL;
rex.reg_mmatch = rmp;
@@ -3370,18 +3382,16 @@ static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
rex.reg_icombine = false;
rex.reg_maxcol = rmp->rmm_maxcol;
- return bt_regexec_both(NULL, col, tm);
+ return bt_regexec_both(NULL, col, tm, timed_out);
}
-/*
- * Match a regexp against a string ("line" points to the string) or multiple
- * lines ("line" is NULL, use reg_getline()).
- * Returns 0 for failure, number of lines contained in the match otherwise.
- */
+/// Match a regexp against a string ("line" points to the string) or multiple
+/// lines ("line" is NULL, use reg_getline()).
+/// @return 0 for failure, or number of lines contained in the match.
static long bt_regexec_both(char_u *line,
- colnr_T col, /* column to start looking for match */
- proftime_T *tm /* timeout limit or NULL */
- )
+ colnr_T col, // column to start search
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out) // flag set on timeout or NULL
{
bt_regprog_T *prog;
char_u *s;
@@ -3483,7 +3493,7 @@ static long bt_regexec_both(char_u *line,
&& (utf_fold(prog->regstart) == utf_fold(c)
|| (c < 255 && prog->regstart < 255
&& mb_tolower(prog->regstart) == mb_tolower(c))))) {
- retval = regtry(prog, col);
+ retval = regtry(prog, col, tm, timed_out);
} else {
retval = 0;
}
@@ -3507,9 +3517,10 @@ static long bt_regexec_both(char_u *line,
break;
}
- retval = regtry(prog, col);
- if (retval > 0)
+ retval = regtry(prog, col, tm, timed_out);
+ if (retval > 0) {
break;
+ }
/* if not currently on the first line, get it again */
if (reglnum != 0) {
@@ -3525,8 +3536,12 @@ static long bt_regexec_both(char_u *line,
/* Check for timeout once in a twenty times to avoid overhead. */
if (tm != NULL && ++tm_count == 20) {
tm_count = 0;
- if (profile_passed_limit(*tm))
+ if (profile_passed_limit(*tm)) {
+ if (timed_out != NULL) {
+ *timed_out = true;
+ }
break;
+ }
}
}
}
@@ -3582,11 +3597,12 @@ void unref_extmatch(reg_extmatch_T *em)
}
}
-/*
- * regtry - try match of "prog" with at regline["col"].
- * Returns 0 for failure, number of lines contained in the match otherwise.
- */
-static long regtry(bt_regprog_T *prog, colnr_T col)
+/// Try match of "prog" with at regline["col"].
+/// @returns 0 for failure, or number of lines contained in the match.
+static long regtry(bt_regprog_T *prog,
+ colnr_T col,
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out) // flag set on timeout or NULL
{
reginput = regline + col;
need_clear_subexpr = TRUE;
@@ -3594,8 +3610,9 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
if (prog->reghasz == REX_SET)
need_clear_zsubexpr = TRUE;
- if (regmatch(prog->program + 1) == 0)
+ if (regmatch(prog->program + 1, tm, timed_out) == 0) {
return 0;
+ }
cleanup_subexpr();
if (REG_MULTI) {
@@ -3736,24 +3753,23 @@ static int reg_match_visual(void)
static long bl_minval;
static long bl_maxval;
-/*
- * regmatch - main matching routine
- *
- * Conceptually the strategy is simple: Check to see whether the current node
- * matches, push an item onto the regstack and loop to see whether the rest
- * matches, and then act accordingly. In practice we make some effort to
- * avoid using the regstack, in particular by going through "ordinary" nodes
- * (that don't need to know whether the rest of the match failed) by a nested
- * loop.
- *
- * Returns TRUE when there is a match. Leaves reginput and reglnum just after
- * the last matched character.
- * Returns FALSE when there is no match. Leaves reginput and reglnum in an
- * undefined state!
- */
-static int
-regmatch (
- char_u *scan /* Current node. */
+/// Main matching routine
+///
+/// Conceptually the strategy is simple: Check to see whether the current node
+/// matches, push an item onto the regstack and loop to see whether the rest
+/// matches, and then act accordingly. In practice we make some effort to
+/// avoid using the regstack, in particular by going through "ordinary" nodes
+/// (that don't need to know whether the rest of the match failed) by a nested
+/// loop.
+///
+/// Returns TRUE when there is a match. Leaves reginput and reglnum just after
+/// the last matched character.
+/// Returns FALSE when there is no match. Leaves reginput and reglnum in an
+/// undefined state!
+static int regmatch(
+ char_u *scan, // Current node.
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out // flag set on timeout or NULL
)
{
char_u *next; /* Next node. */
@@ -3761,15 +3777,16 @@ regmatch (
int c;
regitem_T *rp;
int no;
- int status; /* one of the RA_ values: */
-#define RA_FAIL 1 /* something failed, abort */
-#define RA_CONT 2 /* continue in inner loop */
-#define RA_BREAK 3 /* break inner loop */
-#define RA_MATCH 4 /* successful match */
-#define RA_NOMATCH 5 /* didn't match */
-
- /* Make "regstack" and "backpos" empty. They are allocated and freed in
- * bt_regexec_both() to reduce malloc()/free() calls. */
+ int status; // one of the RA_ values:
+ int tm_count = 0;
+#define RA_FAIL 1 // something failed, abort
+#define RA_CONT 2 // continue in inner loop
+#define RA_BREAK 3 // break inner loop
+#define RA_MATCH 4 // successful match
+#define RA_NOMATCH 5 // didn't match
+
+ // Make "regstack" and "backpos" empty. They are allocated and freed in
+ // bt_regexec_both() to reduce malloc()/free() calls.
regstack.ga_len = 0;
backpos.ga_len = 0;
@@ -3797,6 +3814,17 @@ regmatch (
status = RA_FAIL;
break;
}
+ // Check for timeout once in a 100 times to avoid overhead.
+ if (tm != NULL && ++tm_count == 100) {
+ tm_count = 0;
+ if (profile_passed_limit(*tm)) {
+ if (timed_out != NULL) {
+ *timed_out = true;
+ }
+ status = RA_FAIL;
+ break;
+ }
+ }
status = RA_CONT;
#ifdef REGEXP_DEBUG
@@ -7116,6 +7144,8 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags)
}
bt_regengine.expr = expr;
nfa_regengine.expr = expr;
+ // reg_iswordc() uses rex.reg_buf
+ rex.reg_buf = curbuf;
//
// First try the NFA engine, unless backtracking was requested.
@@ -7273,12 +7303,13 @@ int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
/// Return zero if there is no match. Return number of lines contained in the
/// match otherwise.
long vim_regexec_multi(
- regmmatch_T *rmp,
- win_T *win, /* window in which to search or NULL */
- buf_T *buf, /* buffer in which to search */
- linenr_T lnum, /* nr of line to start looking for match */
- colnr_T col, /* column to start looking for match */
- proftime_T *tm /* timeout limit or NULL */
+ regmmatch_T *rmp,
+ win_T *win, // window in which to search or NULL
+ buf_T *buf, // buffer in which to search
+ linenr_T lnum, // nr of line to start looking for match
+ colnr_T col, // column to start looking for match
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out // flag is set when timeout limit reached
)
{
regexec_T rex_save;
@@ -7291,7 +7322,7 @@ long vim_regexec_multi(
rex_in_use = true;
int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
- tm);
+ tm, timed_out);
// NFA engine aborted because it's very slow, use backtracking engine instead.
if (rmp->regprog->re_engine == AUTOMATIC_ENGINE
@@ -7311,7 +7342,7 @@ long vim_regexec_multi(
if (rmp->regprog != NULL) {
result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
- tm);
+ tm, timed_out);
}
xfree(pat);
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index b5d56e07fc..298d82fc6b 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -156,11 +156,11 @@ struct reg_extmatch {
};
struct regengine {
- regprog_T *(*regcomp)(char_u*, int);
+ regprog_T *(*regcomp)(char_u *, int);
void (*regfree)(regprog_T *);
- int (*regexec_nl)(regmatch_T*, char_u*, colnr_T, bool);
- long (*regexec_multi)(regmmatch_T*, win_T*, buf_T*, linenr_T, colnr_T,
- proftime_T*);
+ int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, bool);
+ long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T,
+ proftime_T *, int *);
char_u *expr;
};
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 95030974d8..b935b44291 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -1338,8 +1338,15 @@ static int nfa_regatom(void)
case Magic('7'):
case Magic('8'):
case Magic('9'):
- EMIT(NFA_BACKREF1 + (no_Magic(c) - '1'));
- nfa_has_backref = TRUE;
+ {
+ int refnum = no_Magic(c) - '1';
+
+ if (!seen_endbrace(refnum + 1)) {
+ return FAIL;
+ }
+ EMIT(NFA_BACKREF1 + refnum);
+ nfa_has_backref = true;
+ }
break;
case Magic('z'):
@@ -3568,6 +3575,7 @@ static char *pim_info(nfa_pim_T *pim)
// Used during execution: whether a match has been found.
static int nfa_match;
static proftime_T *nfa_time_limit;
+static int *nfa_timed_out;
static int nfa_time_count;
// Copy postponed invisible match info from "from" to "to".
@@ -4574,7 +4582,9 @@ static bool nfa_re_num_cmp(uintmax_t val, int op, uintmax_t pos)
* "pim" is NULL or contains info about a Postponed Invisible Match (start
* position).
*/
-static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog, regsubs_T *submatch, regsubs_T *m, int **listids)
+static int recursive_regmatch(
+ nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T *prog,
+ regsubs_T *submatch, regsubs_T *m, int **listids, int *listids_len)
{
int save_reginput_col = (int)(reginput - regline);
int save_reglnum = reglnum;
@@ -4657,8 +4667,10 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
if (nfa_ll_index == 1) {
/* Already calling nfa_regmatch() recursively. Save the lastlist[1]
* values and clear them. */
- if (*listids == NULL) {
+ if (*listids == NULL || *listids_len < nstate) {
+ xfree(*listids);
*listids = xmalloc(sizeof(**listids) * nstate);
+ *listids_len = nstate;
}
nfa_save_listids(prog, *listids);
need_restore = TRUE;
@@ -4939,6 +4951,17 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
#undef PTR2LEN
}
+static int nfa_did_time_out(void)
+{
+ if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) {
+ if (nfa_timed_out != NULL) {
+ *nfa_timed_out = true;
+ }
+ return true;
+ }
+ return false;
+}
+
/// Main matching routine.
///
/// Run NFA to determine whether it matches reginput.
@@ -4960,6 +4983,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
nfa_list_T *thislist;
nfa_list_T *nextlist;
int *listids = NULL;
+ int listids_len = 0;
nfa_state_T *add_state;
bool add_here;
int add_count;
@@ -4982,7 +5006,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
#endif
return false;
}
- if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) {
+ if (nfa_did_time_out()) {
#ifdef NFA_REGEXP_DEBUG_LOG
fclose(debug);
#endif
@@ -5095,8 +5119,20 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
if (thislist->n == 0)
break;
- /* compute nextlist */
- for (listidx = 0; listidx < thislist->n; ++listidx) {
+ // compute nextlist
+ for (listidx = 0; listidx < thislist->n; listidx++) {
+ // If the list gets very long there probably is something wrong.
+ // At least allow interrupting with CTRL-C.
+ fast_breakcheck();
+ if (got_int) {
+ break;
+ }
+ if (nfa_time_limit != NULL && ++nfa_time_count == 20) {
+ nfa_time_count = 0;
+ if (nfa_did_time_out()) {
+ break;
+ }
+ }
t = &thislist->t[listidx];
#ifdef NFA_REGEXP_DEBUG_LOG
@@ -5240,7 +5276,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the invisible match, then what
// follows.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -5341,7 +5377,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// First try matching the pattern.
result = recursive_regmatch(t->state, NULL, prog, submatch, m,
- &listids);
+ &listids, &listids_len);
if (result == NFA_TOO_EXPENSIVE) {
nfa_match = result;
goto theend;
@@ -6048,8 +6084,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
fprintf(log_fd, "Postponed recursive nfa_regmatch()\n");
fprintf(log_fd, "\n");
#endif
- result = recursive_regmatch(pim->state, pim,
- prog, submatch, m, &listids);
+ result = recursive_regmatch(pim->state, pim, prog, submatch, m,
+ &listids, &listids_len);
pim->result = result ? NFA_PIM_MATCH : NFA_PIM_NOMATCH;
// for \@! and \@<! it is a match when the result is
// FALSE
@@ -6218,7 +6254,7 @@ nextchar:
// Check for timeout once every twenty times to avoid overhead.
if (nfa_time_limit != NULL && ++nfa_time_count == 20) {
nfa_time_count = 0;
- if (profile_passed_limit(*nfa_time_limit)) {
+ if (nfa_did_time_out()) {
break;
}
}
@@ -6245,7 +6281,10 @@ theend:
// Try match of "prog" with at regline["col"].
// Returns <= 0 for failure, number of lines contained in the match otherwise.
-static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
+static long nfa_regtry(nfa_regprog_T *prog,
+ colnr_T col,
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out) // flag set on timeout or NULL
{
int i;
regsubs_T subs, m;
@@ -6256,6 +6295,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
reginput = regline + col;
nfa_time_limit = tm;
+ nfa_timed_out = timed_out;
nfa_time_count = 0;
#ifdef REGEXP_DEBUG
@@ -6364,10 +6404,12 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
/// @param line String in which to search or NULL
/// @param startcol Column to start looking for match
/// @param tm Timeout limit or NULL
+/// @param timed_out Flag set on timeout or NULL
///
/// @return <= 0 if there is no match and number of lines contained in the
/// match otherwise.
-static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
+static long nfa_regexec_both(char_u *line, colnr_T startcol,
+ proftime_T *tm, int *timed_out)
{
nfa_regprog_T *prog;
long retval = 0L;
@@ -6449,7 +6491,7 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
prog->state[i].lastlist[1] = 0;
}
- retval = nfa_regtry(prog, col, tm);
+ retval = nfa_regtry(prog, col, tm, timed_out);
nfa_regengine.expr = NULL;
@@ -6596,7 +6638,7 @@ nfa_regexec_nl (
rex.reg_ic = rmp->rm_ic;
rex.reg_icombine = false;
rex.reg_maxcol = 0;
- return nfa_regexec_both(line, col, NULL);
+ return nfa_regexec_both(line, col, NULL, NULL);
}
/// Matches a regexp against multiple lines.
@@ -6608,6 +6650,7 @@ nfa_regexec_nl (
/// @param lnum Number of line to start looking for match
/// @param col Column to start looking for match
/// @param tm Timeout limit or NULL
+/// @param timed_out Flag set on timeout or NULL
///
/// @return <= 0 if there is no match and number of lines contained in the match
/// otherwise.
@@ -6634,7 +6677,8 @@ nfa_regexec_nl (
/// @par
/// FIXME if this behavior is not compatible.
static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
- linenr_T lnum, colnr_T col, proftime_T *tm)
+ linenr_T lnum, colnr_T col,
+ proftime_T *tm, int *timed_out)
{
rex.reg_match = NULL;
rex.reg_mmatch = rmp;
@@ -6647,5 +6691,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
rex.reg_icombine = false;
rex.reg_maxcol = rmp->rmm_maxcol;
- return nfa_regexec_both(NULL, col, tm);
+ return nfa_regexec_both(NULL, col, tm, timed_out);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 08bb4e4a52..5255fd2a51 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -5651,13 +5651,15 @@ next_search_hl (
&& cur != NULL
&& shl == &cur->hl
&& cur->match.regprog == cur->hl.rm.regprog);
+ int timed_out = false;
- nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm));
- /* Copy the regprog, in case it got freed and recompiled. */
+ nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
+ &(shl->tm), &timed_out);
+ // Copy the regprog, in case it got freed and recompiled.
if (regprog_is_copy) {
cur->match.regprog = cur->hl.rm.regprog;
}
- if (called_emsg || got_int) {
+ if (called_emsg || got_int || timed_out) {
// Error while handling regexp: stop using this regexp.
if (shl == &search_hl) {
// don't free regprog in the match list, it's a copy
diff --git a/src/nvim/search.c b/src/nvim/search.c
index f6b80d1b79..777ea07a21 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -523,9 +523,10 @@ int searchit(
char_u *pat,
long count,
int options,
- int pat_use, /* which pattern to use when "pat" is empty */
- linenr_T stop_lnum, /* stop after this line number when != 0 */
- proftime_T *tm /* timeout limit or NULL */
+ int pat_use, // which pattern to use when "pat" is empty
+ linenr_T stop_lnum, // stop after this line number when != 0
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out // set when timed out or NULL
)
{
int found;
@@ -620,9 +621,9 @@ int searchit(
// Look for a match somewhere in line "lnum".
colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0;
nmatched = vim_regexec_multi(&regmatch, win, buf,
- lnum, col, tm);
+ lnum, col, tm, timed_out);
// Abort searching on an error (e.g., out of stack).
- if (called_emsg) {
+ if (called_emsg || (timed_out != NULL && *timed_out)) {
break;
}
if (nmatched > 0) {
@@ -686,8 +687,9 @@ int searchit(
}
if (ptr[matchcol] == NUL
- || (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum,
- matchcol, tm)) == 0) {
+ || (nmatched = vim_regexec_multi(&regmatch, win, buf,
+ lnum, matchcol, tm,
+ timed_out)) == 0) {
match_ok = false;
break;
}
@@ -771,7 +773,7 @@ int searchit(
if (ptr[matchcol] == NUL
|| (nmatched = vim_regexec_multi(
&regmatch, win, buf, lnum + matchpos.lnum, matchcol,
- tm)) == 0) {
+ tm, timed_out)) == 0) {
// If the search timed out, we did find a match
// but it might be the wrong one, so that's not
// OK.
@@ -855,30 +857,35 @@ int searchit(
* twice.
*/
if (!p_ws || stop_lnum != 0 || got_int || called_emsg
+ || (timed_out != NULL && timed_out)
|| break_loop
- || found || loop)
+ || found || loop) {
break;
-
- /*
- * If 'wrapscan' is set we continue at the other end of the file.
- * If 'shortmess' does not contain 's', we give a message.
- * This message is also remembered in keep_msg for when the screen
- * is redrawn. The keep_msg is cleared whenever another message is
- * written.
- */
- if (dir == BACKWARD) /* start second loop at the other end */
+ }
+ //
+ // If 'wrapscan' is set we continue at the other end of the file.
+ // If 'shortmess' does not contain 's', we give a message.
+ // This message is also remembered in keep_msg for when the screen
+ // is redrawn. The keep_msg is cleared whenever another message is
+ // written.
+ //
+ if (dir == BACKWARD) { // start second loop at the other end
lnum = buf->b_ml.ml_line_count;
- else
+ } else {
lnum = 1;
- if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
+ }
+ if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) {
give_warning((char_u *)_(dir == BACKWARD
- ? top_bot_msg : bot_top_msg), true);
+ ? top_bot_msg : bot_top_msg), true);
+ }
}
if (got_int || called_emsg
+ || (timed_out != NULL && *timed_out)
|| break_loop
- )
+ ) {
break;
- } while (--count > 0 && found); /* stop after count matches or no match */
+ }
+ } while (--count > 0 && found); // stop after count matches or no match
vim_regfree(regmatch.regprog);
@@ -965,7 +972,8 @@ int do_search(
char_u *pat,
long count,
int options,
- proftime_T *tm /* timeout limit or NULL */
+ proftime_T *tm, // timeout limit or NULL
+ int *timed_out // flag set on timeout or NULL
)
{
pos_T pos; /* position of the last match */
@@ -1195,7 +1203,7 @@ int do_search(
& (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG
+ SEARCH_START
+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))),
- RE_LAST, (linenr_T)0, tm);
+ RE_LAST, (linenr_T)0, tm, timed_out);
if (dircp != NULL)
*dircp = dirc; /* restore second '/' or '?' for normal_cmd() */
@@ -3978,7 +3986,7 @@ current_search(
result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD),
spats[last_idx].pat, i ? count : 1,
- SEARCH_KEEP | flags, RE_SEARCH, 0, NULL);
+ SEARCH_KEEP | flags, RE_SEARCH, 0, NULL, NULL);
/* First search may fail, but then start searching from the
* beginning of the file (cursor might be on the search match)
@@ -4025,7 +4033,7 @@ current_search(
for (int i = 0; i < 2; i++) {
result = searchit(curwin, curbuf, &pos, direction,
spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH,
- 0, NULL);
+ 0, NULL, NULL);
// Search successfull, break out from the loop
if (result) {
break;
@@ -4104,14 +4112,15 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur,
flag = SEARCH_START;
}
if (searchit(curwin, curbuf, &pos, direction, pattern, 1,
- SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) {
+ SEARCH_KEEP + flag, RE_SEARCH, 0, NULL, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position.
called_emsg = false;
do {
regmatch.startpos[0].col++;
nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
- pos.lnum, regmatch.startpos[0].col, NULL);
+ pos.lnum, regmatch.startpos[0].col,
+ NULL, NULL);
if (!nmatched) {
break;
}
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index ff61c2e5de..ec4da88ea1 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3048,9 +3048,10 @@ void ex_spellrepall(exarg_T *eap)
sub_nlines = 0;
curwin->w_cursor.lnum = 0;
while (!got_int) {
- if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL) == 0
- || u_save_cursor() == FAIL)
+ if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL, NULL) == 0
+ || u_save_cursor() == FAIL) {
break;
+ }
// Only replace when the right word isn't there yet. This happens
// when changing "etc" to "etc.".
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index b6b7dfff11..8c3ce823d3 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -2902,7 +2902,7 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
}
rmp->rmm_maxcol = syn_buf->b_p_smc;
- r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
+ r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL);
if (l_syn_time_on) {
pt = profile_end(pt);
@@ -5956,6 +5956,7 @@ static const char *highlight_init_both[] = {
"default link Substitute Search",
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
+ "default link NormalFloat Pmenu",
NULL
};
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 50397d40e6..410b9dfcbd 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -109,7 +109,7 @@ static char_u *tagmatchname = NULL; /* name of last used tag */
* Tag for preview window is remembered separately, to avoid messing up the
* normal tagstack.
*/
-static taggy_T ptag_entry = {NULL, {INIT_POS_T(0, 0, 0), 0, 0, NULL}, 0, 0};
+static taggy_T ptag_entry = { NULL, { { 0, 0, 0 }, 0, 0, NULL }, 0, 0 };
/*
* Jump to tag; handling of tag commands and tag stack
@@ -2508,9 +2508,9 @@ static int jumpto_tag(
save_lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = 0; /* start search before first line */
if (do_search(NULL, pbuf[0], pbuf + 1, (long)1,
- search_options, NULL))
+ search_options, NULL, NULL)) {
retval = OK;
- else {
+ } else {
int found = 1;
int cc;
@@ -2519,23 +2519,22 @@ static int jumpto_tag(
*/
p_ic = TRUE;
if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1,
- search_options, NULL)) {
- /*
- * Failed to find pattern, take a guess: "^func ("
- */
+ search_options, NULL, NULL)) {
+ // Failed to find pattern, take a guess: "^func ("
found = 2;
(void)test_for_static(&tagp);
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname);
+ snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1,
- search_options, NULL)) {
- /* Guess again: "^char * \<func (" */
- sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
- tagp.tagname);
+ search_options, NULL, NULL)) {
+ // Guess again: "^char * \<func ("
+ snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
+ tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1,
- search_options, NULL))
+ search_options, NULL, NULL)) {
found = 0;
+ }
}
*tagp.tagname_end = cc;
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 8b4ad4d3af..ffe650f416 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -1112,11 +1112,15 @@ static void refresh_terminal(Terminal *term)
return;
}
long ml_before = buf->b_ml.ml_line_count;
- WITH_BUFFER(buf, {
- refresh_size(term, buf);
- refresh_scrollback(term, buf);
- refresh_screen(term, buf);
- });
+
+ // refresh_ functions assume the terminal buffer is current
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ refresh_size(term, buf);
+ refresh_scrollback(term, buf);
+ refresh_screen(term, buf);
+ aucmd_restbuf(&aco);
+
long ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
}
diff --git a/src/nvim/testdir/test50.in b/src/nvim/testdir/test50.in
deleted file mode 100644
index 392177b808..0000000000
--- a/src/nvim/testdir/test50.in
+++ /dev/null
@@ -1,89 +0,0 @@
-Test for shortpathname ':8' extension.
-Only for use on Win32 systems!
-
-STARTTEST
-:fun! TestIt(file, bits, expected)
- let res=fnamemodify(a:file,a:bits)
- if a:expected == ''
- echo "'".a:file."'->(".a:bits.")->'".res."'"
- else
- if substitute(res,'/','\\', 'g') != substitute( a:expected, '/','\\', 'g')
- echo "FAILED: '".a:file."'->(".a:bits.")->'".res."'"
- echo "Expected: '".a:expected."'"
- else
- echo "OK"
- endif
- endif
-endfun
-:fun! MakeDir( dirname )
- "exe '!mkdir '.substitute(a:dirname,'/','\\','g')
- call system('mkdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! RMDir( dirname)
- "exe '!rmdir '.substitute(a:dirname,'/','\\','g')
- call system('rmdir '.substitute(a:dirname,'/','\\','g'))
-endfun
-:fun! MakeFile( filename)
- "exe '!copy nul '.substitute(a:filename,'/','\\','g')
- call system('copy nul '.substitute(a:filename,'/','\\','g'))
-endfun
-:fun! TestColonEight()
- redir! >test.out
- " This could change for CygWin to //cygdrive/c
- let dir1='c:/x.x.y'
- if filereadable(dir1) || isdirectory(dir1)
- echo "FATAL: '".dir1."' exists, cannot run test"
- return
- endif
- let file1=dir1.'/zz.y.txt'
- let nofile1=dir1.'/z.y.txt'
- let dir2=dir1.'/VimIsTheGreatestSinceSlicedBread'
- let file2=dir2.'/z.txt'
- let nofile2=dir2.'/zz.txt'
- call MakeDir( dir1 )
- let resdir1 = substitute(fnamemodify(dir1, ':p:8'), '\\$', '', '')
- if resdir1 !~ '\V\^c:/XX\x\x\x\x~1.Y\$'
- echo "FATAL: unexpected short name: " . resdir1
- echo "INFO: please report your OS to vim-dev"
- return
- endif
- let resfile1=resdir1.'/ZZY~1.TXT'
- let resnofile1=resdir1.'/z.y.txt'
- let resdir2=resdir1.'/VIMIST~1'
- let resfile2=resdir2.'/z.txt'
- let resnofile2=resdir2.'/zz.txt'
- call MakeDir( dir2 )
- call MakeFile( file1 )
- call MakeFile( file2 )
- call TestIt(file1, ':p:8', resfile1)
- call TestIt(nofile1, ':p:8', resnofile1)
- call TestIt(file2, ':p:8', resfile2)
- call TestIt(nofile2, ':p:8', resnofile2)
- call TestIt(nofile2, ':p:8:h', fnamemodify(resnofile2,':h'))
- exe 'cd '.dir1
- call TestIt(file1, ':.:8', strpart(resfile1,strlen(resdir1)+1))
- call TestIt(nofile1, ':.:8', strpart(resnofile1,strlen(resdir1)+1))
- call TestIt(file2, ':.:8', strpart(resfile2,strlen(resdir1)+1))
- call TestIt(nofile2, ':.:8', strpart(resnofile2,strlen(resdir1)+1))
- let $HOME=dir1
- call TestIt(file1, ':~:8', '~'.strpart(resfile1,strlen(resdir1)))
- call TestIt(nofile1, ':~:8', '~'.strpart(resnofile1,strlen(resdir1)))
- call TestIt(file2, ':~:8', '~'.strpart(resfile2,strlen(resdir1)))
- call TestIt(nofile2, ':~:8', '~'.strpart(resnofile2,strlen(resdir1)))
- cd c:/
- call delete( file2 )
- call delete( file1 )
- call RMDir( dir2 )
- call RMDir( dir1 )
- echo
- redir END
-endfun
-:let dir = getcwd()
-:call TestColonEight()
-:exe "cd " . dir
-:edit! test.out
-:set ff=dos
-:w
-:qa!
-ENDTEST
-
diff --git a/src/nvim/testdir/test50.ok b/src/nvim/testdir/test50.ok
deleted file mode 100644
index 91ef1d6604..0000000000
--- a/src/nvim/testdir/test50.ok
+++ /dev/null
@@ -1,14 +0,0 @@
-
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
-OK
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index f1fb8e67b9..7a6c9478ca 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1161,23 +1161,23 @@ func Test_TextYankPost()
norm "ayiw
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'a', 'operator': 'y', 'regtype': 'v'},
\g:event)
norm y_
call assert_equal(
- \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'},
+ \{'regcontents': ['foo'], 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'},
\g:event)
call feedkeys("\<C-V>y", 'x')
call assert_equal(
- \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"},
+ \{'regcontents': ['f'], 'inclusive': v:true, 'regname': '', 'operator': 'y', 'regtype': "\x161"},
\g:event)
norm "xciwbar
call assert_equal(
- \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
+ \{'regcontents': ['foo'], 'inclusive': v:true, 'regname': 'x', 'operator': 'c', 'regtype': 'v'},
\g:event)
norm "bdiw
call assert_equal(
- \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
+ \{'regcontents': ['bar'], 'inclusive': v:true, 'regname': 'b', 'operator': 'd', 'regtype': 'v'},
\g:event)
call assert_equal({}, v:event)
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index 78e51ed836..0bae161a8b 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -119,6 +119,14 @@ func Test_findfile()
let &shellslash = save_shellslash
endfunc
+func Test_findfile_error()
+ call assert_fails('call findfile([])', 'E730:')
+ call assert_fails('call findfile("x", [])', 'E730:')
+ call assert_fails('call findfile("x", "", [])', 'E745:')
+ call assert_fails('call findfile("x", "**x")', 'E343:')
+ call assert_fails('call findfile("x", repeat("x", 5000))', 'E854:')
+endfunc
+
" Test finddir({name} [, {path} [, {count}]])
func Test_finddir()
let save_path = &path
@@ -167,3 +175,11 @@ func Test_finddir()
let &path = save_path
let &shellslash = save_shellslash
endfunc
+
+func Test_finddir_error()
+ call assert_fails('call finddir([])', 'E730:')
+ call assert_fails('call finddir("x", [])', 'E730:')
+ call assert_fails('call finddir("x", "", [])', 'E745:')
+ call assert_fails('call finddir("x", "**x")', 'E343:')
+ call assert_fails('call finddir("x", repeat("x", 5000))', 'E854:')
+endfunc
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index bfe13d6b2d..824baffbc9 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -956,8 +956,8 @@ func Test_balloon_show()
endfunc
func Test_shellescape()
- let save_shell = &shell
- set shell=bash
+ let [save_shell, save_shellslash] = [&shell, &shellslash]
+ set shell=bash shellslash
call assert_equal("'text'", shellescape('text'))
call assert_equal("'te\"xt'", shellescape('te"xt'))
call assert_equal("'te'\\''xt'", shellescape("te'xt"))
@@ -971,13 +971,13 @@ func Test_shellescape()
call assert_equal("'te\nxt'", shellescape("te\nxt"))
call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1))
- set shell=tcsh
+ set shell=tcsh shellslash
call assert_equal("'te\\!xt'", shellescape("te!xt"))
call assert_equal("'te\\\\!xt'", shellescape("te!xt", 1))
call assert_equal("'te\\\nxt'", shellescape("te\nxt"))
call assert_equal("'te\\\\\nxt'", shellescape("te\nxt", 1))
- let &shell = save_shell
+ let [&shell, &shellslash] = [save_shell, save_shellslash]
endfunc
func Test_redo_in_nested_functions()
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index c352379697..d233046d2b 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -9,6 +9,7 @@ func Test_gf_url()
\ "third test for URL:\\\\machine.name\\vimtest2c and other text",
\ "fourth test for URL:\\\\machine.name\\tmp\\vimtest2d, and other text",
\ "fifth test for URL://machine.name/tmp?q=vim&opt=yes and other text",
+ \ "sixth test for URL://machine.name:1234?q=vim and other text",
\ ])
call cursor(1,1)
call search("^first")
@@ -20,7 +21,7 @@ func Test_gf_url()
if has("ebcdic")
set isf=@,240-249,/,.,-,_,+,,,$,:,~,\
else
- set isf=@,48-57,/,.,-,_,+,,,$,:,~,\
+ set isf=@,48-57,/,.,-,_,+,,,$,~,\
endif
call search("^third")
call search("name")
@@ -33,6 +34,10 @@ func Test_gf_url()
call search("URL")
call assert_equal("URL://machine.name/tmp?q=vim&opt=yes", expand("<cfile>"))
+ call search("^sixth")
+ call search("URL")
+ call assert_equal("URL://machine.name:1234?q=vim", expand("<cfile>"))
+
set isf&vim
enew!
endfunc
diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim
index 1fc7b04f88..97f6ae7b51 100644
--- a/src/nvim/testdir/test_hlsearch.vim
+++ b/src/nvim/testdir/test_hlsearch.vim
@@ -32,6 +32,25 @@ function! Test_hlsearch()
enew!
endfunction
+func Test_hlsearch_hangs()
+ if !has('reltime') || !has('float')
+ return
+ endif
+
+ " This pattern takes a long time to match, it should timeout.
+ new
+ call setline(1, ['aaa', repeat('abc ', 100000), 'ccc'])
+ let start = reltime()
+ set hlsearch nolazyredraw redrawtime=101
+ let @/ = '\%#=1a*.*X\@<=b*'
+ redraw
+ let elapsed = reltimefloat(reltime(start))
+ call assert_true(elapsed > 0.1)
+ call assert_true(elapsed < 1.0)
+ set nohlsearch redrawtime&
+ bwipe!
+endfunc
+
func Test_hlsearch_eol_highlight()
new
call append(1, repeat([''], 9))
diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim
index 8858cd22b8..7fd115fd68 100644
--- a/src/nvim/testdir/test_marks.vim
+++ b/src/nvim/testdir/test_marks.vim
@@ -136,3 +136,44 @@ func Test_marks_cmd_multibyte()
bwipe!
endfunc
+
+func Test_delmarks()
+ new
+ norm mx
+ norm `x
+ delmarks x
+ call assert_fails('norm `x', 'E20:')
+
+ " Deleting an already deleted mark should not fail.
+ delmarks x
+
+ " Test deleting a range of marks.
+ norm ma
+ norm mb
+ norm mc
+ norm mz
+ delmarks b-z
+ norm `a
+ call assert_fails('norm `b', 'E20:')
+ call assert_fails('norm `c', 'E20:')
+ call assert_fails('norm `z', 'E20:')
+ call assert_fails('delmarks z-b', 'E475:')
+
+ call assert_fails('delmarks', 'E471:')
+ call assert_fails('delmarks /', 'E475:')
+
+ " Test delmarks!
+ norm mx
+ norm `x
+ delmarks!
+ call assert_fails('norm `x', 'E20:')
+ call assert_fails('delmarks! x', 'E474:')
+
+ bwipe!
+endfunc
+
+func Test_mark_error()
+ call assert_fails('mark', 'E471:')
+ call assert_fails('mark xx', 'E488:')
+ call assert_fails('mark _', 'E191:')
+endfunc
diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim
index 4cbd800da5..8996e86b43 100644
--- a/src/nvim/testdir/test_profile.vim
+++ b/src/nvim/testdir/test_profile.vim
@@ -181,3 +181,44 @@ func Test_profile_errors()
call assert_fails("profile pause", 'E750:')
call assert_fails("profile continue", 'E750:')
endfunc
+
+func Test_profile_truncate_mbyte()
+ if !has('multi_byte') || &enc !=# 'utf-8'
+ return
+ endif
+
+ let lines = [
+ \ 'scriptencoding utf-8',
+ \ 'func! Foo()',
+ \ ' return [',
+ \ ' \ "' . join(map(range(0x4E00, 0x4E00 + 340), 'nr2char(v:val)'), '') . '",',
+ \ ' \ "' . join(map(range(0x4F00, 0x4F00 + 340), 'nr2char(v:val)'), '') . '",',
+ \ ' \ ]',
+ \ 'endfunc',
+ \ 'call Foo()',
+ \ ]
+
+ call writefile(lines, 'Xprofile_file.vim')
+ call system(v:progpath
+ \ . ' -es --cmd "set enc=utf-8"'
+ \ . ' -c "profile start Xprofile_file.log"'
+ \ . ' -c "profile file Xprofile_file.vim"'
+ \ . ' -c "so Xprofile_file.vim"'
+ \ . ' -c "qall!"')
+ call assert_equal(0, v:shell_error)
+
+ split Xprofile_file.log
+ if &fenc != ''
+ call assert_equal('utf-8', &fenc)
+ endif
+ /func! Foo()
+ let lnum = line('.')
+ call assert_match('^\s*return \[$', getline(lnum + 1))
+ call assert_match("\u4F52$", getline(lnum + 2))
+ call assert_match("\u5052$", getline(lnum + 3))
+ call assert_match('^\s*\\ \]$', getline(lnum + 4))
+ bwipe!
+
+ call delete('Xprofile_file.vim')
+ call delete('Xprofile_file.log')
+endfunc
diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim
index 2192b5e8fc..7873502943 100644
--- a/src/nvim/testdir/test_regex_char_classes.vim
+++ b/src/nvim/testdir/test_regex_char_classes.vim
@@ -1,5 +1,11 @@
" Tests for regexp with backslash and other special characters inside []
" Also test backslash for hex/octal numbered character.
+"
+if !has('multi_byte')
+ finish
+endif
+
+scriptencoding utf-8
function RunSTest(value, calls, expected)
new
@@ -56,3 +62,237 @@ function Test_s_search()
call RunSTest(" xyz", "s/~/bcd/", " bcd")
call RunSTest(" bcdbcdbcd", "s/~\\+/BB/", " BB")
endfunction
+
+" Test character classes in regexp using regexpengine 0, 1, 2.
+func Test_regex_char_classes()
+ new
+ let save_enc = &encoding
+ set encoding=utf-8
+
+ let input = "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"
+
+ " Format is [cmd_to_run, expected_output]
+ let tests = [
+ \ [':s/\%#=0\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\d//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[0-9]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=1\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=2\D//g',
+ \ "0123456789"],
+ \ [':s/\%#=0[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=1[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=2[^0-9]//g',
+ \ "0123456789"],
+ \ [':s/\%#=0\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\o//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[0-7]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\O//g',
+ \ "01234567"],
+ \ [':s/\%#=1\O//g',
+ \ "01234567"],
+ \ [':s/\%#=2\O//g',
+ \ "01234567"],
+ \ [':s/\%#=0[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=1[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=2[^0-7]//g',
+ \ "01234567"],
+ \ [':s/\%#=0\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\x//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[0-9A-Fa-f]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2\X//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=1[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=2[^0-9A-Fa-f]//g',
+ \ "0123456789ABCDEFabcdef"],
+ \ [':s/\%#=0\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\w//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[0-9A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\W//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-9A-Za-z_]//g',
+ \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\h//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[A-Za-z_]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2\H//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z_]//g',
+ \ "ABCDEFGHIXYZ_abcdefghiwxyz"],
+ \ [':s/\%#=0\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\a//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[A-Za-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2\A//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=1[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=2[^A-Za-z]//g',
+ \ "ABCDEFGHIXYZabcdefghiwxyz"],
+ \ [':s/\%#=0\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\l//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[a-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2\L//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=1[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=2[^a-z]//g',
+ \ "abcdefghiwxyz"],
+ \ [':s/\%#=0\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\u//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[A-Z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2\U//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=1[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=2[^A-Z]//g',
+ \ "ABCDEFGHIXYZ"],
+ \ [':s/\%#=0\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2\%' . line('.') . 'l^\t...//g',
+ \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=1[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=2[0-z]//g',
+ \ "\t\<C-L>\<C-M> !\"#$%&'()#+'-./{|}~\<C-?>\u0080\u0082\u0090\u009bΡ记娱"],
+ \ [':s/\%#=0[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=1[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"],
+ \ [':s/\%#=2[^0-z]//g',
+ \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"]
+ \]
+
+ for [cmd, expected] in tests
+ call append(0, input)
+ call cursor(1, 1)
+ exe cmd
+ call assert_equal(expected, getline(1), cmd)
+ endfor
+
+ let &encoding = save_enc
+ enew!
+ close
+endfunc
diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index eb8f69ef15..0619e9c027 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -47,3 +47,29 @@ func Test_get_equi_class()
s/.*/[[.
call assert_equal(1, search(getline(1)))
endfunc
+
+func Test_rex_init()
+ set noincsearch
+ set re=1
+ new
+ setlocal iskeyword=a-z
+ call setline(1, ['abc', 'ABC'])
+ call assert_equal(1, search('[[:keyword:]]'))
+ new
+ setlocal iskeyword=A-Z
+ call setline(1, ['abc', 'ABC'])
+ call assert_equal(2, search('[[:keyword:]]'))
+ bwipe!
+ bwipe!
+ set re=0
+endfunc
+
+func Test_backref()
+ new
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ call assert_equal(3, search('\%#=1\(e\)\1'))
+ call assert_equal(3, search('\%#=2\(e\)\1'))
+ call assert_fails('call search("\\%#=1\\(e\\1\\)")', 'E65:')
+ call assert_fails('call search("\\%#=2\\(e\\1\\)")', 'E65:')
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index cf85bd58ac..351b119acd 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -223,7 +223,7 @@ func Test_statusline()
set statusline=ab%(cd%q%)de
call assert_match('^abde\s*$', s:get_statusline())
copen
- call assert_match('^abcd\[Quickfix List\1]de\s*$', s:get_statusline())
+ call assert_match('^abcd\[Quickfix List]de\s*$', s:get_statusline())
cclose
" %#: Set highlight group. The name must follow and then a # again.
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index dbd26be089..d02454fbf0 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -322,6 +322,90 @@ func Test_sub_cmd_8()
set titlestring&
endfunc
+" Test %s/\n// which is implemented as a special case to use a
+" more efficient join rather than doing a regular substitution.
+func Test_substitute_join()
+ new
+
+ call setline(1, ["foo\tbar", "bar\<C-H>foo"])
+ let a = execute('%s/\n//')
+ call assert_equal("", a)
+ call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
+ call assert_equal('\n', histget("search", -1))
+
+ call setline(1, ["foo\tbar", "bar\<C-H>foo"])
+ let a = execute('%s/\n//g')
+ call assert_equal("", a)
+ call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
+ call assert_equal('\n', histget("search", -1))
+
+ call setline(1, ["foo\tbar", "bar\<C-H>foo"])
+ let a = execute('%s/\n//p')
+ call assert_equal("\nfoo barbar^Hfoo", a)
+ call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
+ call assert_equal('\n', histget("search", -1))
+
+ call setline(1, ["foo\tbar", "bar\<C-H>foo"])
+ let a = execute('%s/\n//l')
+ call assert_equal("\nfoo^Ibarbar^Hfoo$", a)
+ call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
+ call assert_equal('\n', histget("search", -1))
+
+ call setline(1, ["foo\tbar", "bar\<C-H>foo"])
+ let a = execute('%s/\n//#')
+ call assert_equal("\n 1 foo barbar^Hfoo", a)
+ call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
+ call assert_equal('\n', histget("search", -1))
+
+ bwipe!
+endfunc
+
+func Test_substitute_count()
+ new
+ call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
+ 2
+
+ s/foo/bar/3
+ call assert_equal(['foo foo', 'bar foo', 'bar foo', 'bar foo', 'foo foo'],
+ \ getline(1, '$'))
+
+ call assert_fails('s/foo/bar/0', 'E939:')
+
+ bwipe!
+endfunc
+
+" Test substitute 'n' flag (report number of matches, do not substitute).
+func Test_substitute_flag_n()
+ new
+ let lines = ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo']
+ call setline(1, lines)
+
+ call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/n'))
+ call assert_equal("\n6 matches on 3 lines", execute('2,4s/foo/bar/gn'))
+
+ " c flag (confirm) should be ignored when using n flag.
+ call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/nc'))
+
+ " No substitution should have been done.
+ call assert_equal(lines, getline(1, '$'))
+
+ bwipe!
+endfunc
+
+func Test_substitute_errors()
+ new
+ call setline(1, 'foobar')
+
+ call assert_fails('s/FOO/bar/', 'E486:')
+ call assert_fails('s/foo/bar/@', 'E488:')
+ call assert_fails('s/\(/bar/', 'E476:')
+
+ setl nomodifiable
+ call assert_fails('s/foo/bar/', 'E21:')
+
+ bwipe!
+endfunc
+
" Test for *sub-replace-special* and *sub-replace-expression* on substitute().
func Test_sub_replace_1()
" Run the tests with 'magic' on
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 999566c6ac..13fb50b985 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -163,6 +163,329 @@ func Test_text_format()
\ '# 1 xxxxx',
\ '# foobar'], getline(1, 2))
+ " Test the 'p' flag for 'formatoptions'
+ " First test without the flag: that it will break "Mr. Feynman" at the space
+ normal ggdG
+ setl tw=28 fo=tcq
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+ " Now test with the flag: that it will push the name with the title onto the
+ " next line
+ normal ggdG
+ setl fo+=p
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking,',
+ \ 'Mr. Feynman!'], getline(1, 2))
+ " Ensure that it will still break if two spaces are entered
+ normal ggdG
+ call setline('.', 'Surely you''re joking, Mr. Feynman!')
+ normal gqq
+ call assert_equal([
+ \ 'Surely you''re joking, Mr.',
+ \ 'Feynman!'], getline(1, 2))
+
setl ai& tw& fo& si& comments&
enew!
endfunc
+
+" Tests for :right, :center and :left on text with embedded TAB.
+func Test_format_align()
+ enew!
+ set tw=65
+
+ " :left alignment
+ call append(0, [
+ \ " test for :left",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ "asxxdfa a",
+ \ ])
+ %left
+ call assert_equal([
+ \ "test for :left",
+ \ "a a",
+ \ "fa a",
+ \ "dfa a",
+ \ "sdfa a",
+ \ "asdfa a",
+ \ "xasdfa a",
+ \ "asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :center alignment
+ call append(0, [
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ "asxxdfa a"
+ \ ])
+ %center
+ call assert_equal([
+ \ " test for :center",
+ \ " a a",
+ \ " fa afd asdf",
+ \ " dfa a",
+ \ " sdfa afd asdf",
+ \ " asdfa a",
+ \ " xasdfa asdfasdfasdfasdfasdf",
+ \ " asxxdfa a",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ " :right alignment
+ call append(0, [
+ \ " test for :right",
+ \ " a a",
+ \ " fa a",
+ \ " dfa a",
+ \ " sdfa a",
+ \ " asdfa a",
+ \ " xasdfa a",
+ \ " asxxdfa a",
+ \ " asxa;ofa a",
+ \ " asdfaqwer a",
+ \ " a ax",
+ \ " fa ax",
+ \ " dfa ax",
+ \ " sdfa ax",
+ \ " asdfa ax",
+ \ " xasdfa ax",
+ \ " asxxdfa ax",
+ \ " asxa;ofa ax",
+ \ " asdfaqwer ax",
+ \ " a axx",
+ \ " fa axx",
+ \ " dfa axx",
+ \ " sdfa axx",
+ \ " asdfa axx",
+ \ " xasdfa axx",
+ \ " asxxdfa axx",
+ \ " asxa;ofa axx",
+ \ " asdfaqwer axx",
+ \ " a axxx",
+ \ " fa axxx",
+ \ " dfa axxx",
+ \ " sdfa axxx",
+ \ " asdfa axxx",
+ \ " xasdfa axxx",
+ \ " asxxdfa axxx",
+ \ " asxa;ofa axxx",
+ \ " asdfaqwer axxx",
+ \ " a axxxo",
+ \ " fa axxxo",
+ \ " dfa axxxo",
+ \ " sdfa axxxo",
+ \ " asdfa axxxo",
+ \ " xasdfa axxxo",
+ \ " asxxdfa axxxo",
+ \ " asxa;ofa axxxo",
+ \ " asdfaqwer axxxo",
+ \ " a axxxoi",
+ \ " fa axxxoi",
+ \ " dfa axxxoi",
+ \ " sdfa axxxoi",
+ \ " asdfa axxxoi",
+ \ " xasdfa axxxoi",
+ \ " asxxdfa axxxoi",
+ \ " asxa;ofa axxxoi",
+ \ " asdfaqwer axxxoi",
+ \ " a axxxoik",
+ \ " fa axxxoik",
+ \ " dfa axxxoik",
+ \ " sdfa axxxoik",
+ \ " asdfa axxxoik",
+ \ " xasdfa axxxoik",
+ \ " asxxdfa axxxoik",
+ \ " asxa;ofa axxxoik",
+ \ " asdfaqwer axxxoik",
+ \ " a axxxoike",
+ \ " fa axxxoike",
+ \ " dfa axxxoike",
+ \ " sdfa axxxoike",
+ \ " asdfa axxxoike",
+ \ " xasdfa axxxoike",
+ \ " asxxdfa axxxoike",
+ \ " asxa;ofa axxxoike",
+ \ " asdfaqwer axxxoike",
+ \ " a axxxoikey",
+ \ " fa axxxoikey",
+ \ " dfa axxxoikey",
+ \ " sdfa axxxoikey",
+ \ " asdfa axxxoikey",
+ \ " xasdfa axxxoikey",
+ \ " asxxdfa axxxoikey",
+ \ " asxa;ofa axxxoikey",
+ \ " asdfaqwer axxxoikey",
+ \ ])
+ %right
+ call assert_equal([
+ \ "\t\t\t\t test for :right",
+ \ "\t\t\t\t a a",
+ \ "\t\t\t\t fa a",
+ \ "\t\t\t\t dfa a",
+ \ "\t\t\t\t sdfa a",
+ \ "\t\t\t\t asdfa a",
+ \ "\t\t\t\t xasdfa a",
+ \ "\t\t\t\t asxxdfa a",
+ \ "\t\t\t\t asxa;ofa a",
+ \ "\t\t\t\t asdfaqwer a",
+ \ "\t\t\t\t a ax",
+ \ "\t\t\t\t fa ax",
+ \ "\t\t\t\t dfa ax",
+ \ "\t\t\t\t sdfa ax",
+ \ "\t\t\t\t asdfa ax",
+ \ "\t\t\t\t xasdfa ax",
+ \ "\t\t\t\t asxxdfa ax",
+ \ "\t\t\t\t asxa;ofa ax",
+ \ "\t\t\t\t asdfaqwer ax",
+ \ "\t\t\t\t a axx",
+ \ "\t\t\t\t fa axx",
+ \ "\t\t\t\t dfa axx",
+ \ "\t\t\t\t sdfa axx",
+ \ "\t\t\t\t asdfa axx",
+ \ "\t\t\t\t xasdfa axx",
+ \ "\t\t\t\t asxxdfa axx",
+ \ "\t\t\t\t asxa;ofa axx",
+ \ "\t\t\t\t asdfaqwer axx",
+ \ "\t\t\t\t a axxx",
+ \ "\t\t\t\t fa axxx",
+ \ "\t\t\t\t dfa axxx",
+ \ "\t\t\t\t sdfa axxx",
+ \ "\t\t\t\t asdfa axxx",
+ \ "\t\t\t\t xasdfa axxx",
+ \ "\t\t\t\t asxxdfa axxx",
+ \ "\t\t\t\t asxa;ofa axxx",
+ \ "\t\t\t\t asdfaqwer axxx",
+ \ "\t\t\t\t a axxxo",
+ \ "\t\t\t\t fa axxxo",
+ \ "\t\t\t\t dfa axxxo",
+ \ "\t\t\t\t sdfa axxxo",
+ \ "\t\t\t\t asdfa axxxo",
+ \ "\t\t\t\t xasdfa axxxo",
+ \ "\t\t\t\t asxxdfa axxxo",
+ \ "\t\t\t\t asxa;ofa axxxo",
+ \ "\t\t\t\t asdfaqwer axxxo",
+ \ "\t\t\t\t a axxxoi",
+ \ "\t\t\t\t fa axxxoi",
+ \ "\t\t\t\t dfa axxxoi",
+ \ "\t\t\t\t sdfa axxxoi",
+ \ "\t\t\t\t asdfa axxxoi",
+ \ "\t\t\t\t xasdfa axxxoi",
+ \ "\t\t\t\t asxxdfa axxxoi",
+ \ "\t\t\t\t asxa;ofa axxxoi",
+ \ "\t\t\t\t asdfaqwer axxxoi",
+ \ "\t\t\t\t a axxxoik",
+ \ "\t\t\t\t fa axxxoik",
+ \ "\t\t\t\t dfa axxxoik",
+ \ "\t\t\t\t sdfa axxxoik",
+ \ "\t\t\t\t asdfa axxxoik",
+ \ "\t\t\t\t xasdfa axxxoik",
+ \ "\t\t\t\t asxxdfa axxxoik",
+ \ "\t\t\t\t asxa;ofa axxxoik",
+ \ "\t\t\t\t asdfaqwer axxxoik",
+ \ "\t\t\t\t a axxxoike",
+ \ "\t\t\t\t fa axxxoike",
+ \ "\t\t\t\t dfa axxxoike",
+ \ "\t\t\t\t sdfa axxxoike",
+ \ "\t\t\t\t asdfa axxxoike",
+ \ "\t\t\t\t xasdfa axxxoike",
+ \ "\t\t\t\t asxxdfa axxxoike",
+ \ "\t\t\t\t asxa;ofa axxxoike",
+ \ "\t\t\t\t asdfaqwer axxxoike",
+ \ "\t\t\t\t a axxxoikey",
+ \ "\t\t\t\t fa axxxoikey",
+ \ "\t\t\t\t dfa axxxoikey",
+ \ "\t\t\t\t sdfa axxxoikey",
+ \ "\t\t\t\t asdfa axxxoikey",
+ \ "\t\t\t\t xasdfa axxxoikey",
+ \ "\t\t\t\t asxxdfa axxxoikey",
+ \ "\t\t\t\t asxa;ofa axxxoikey",
+ \ "\t\t\t\t asdfaqwer axxxoikey",
+ \ ""
+ \ ], getline(1, '$'))
+ enew!
+
+ set tw&
+endfunc
+
+" Test formatting a paragraph.
+func Test_format_para()
+ enew!
+ set fo+=tcroql tw=72
+
+ call append(0, [
+ \ "xxxxx xx xxxxxx ",
+ \ "xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx",
+ \ "xx xxxxxxx. xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx",
+ \ "> xxxxxx xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx"
+ \ ])
+ exe "normal /xxxxxxxx$\<CR>"
+ normal 0gq6kk
+ call assert_equal([
+ \ "xxxxx xx xxxxxx xxxxxxx xxxxxxxxx xxx xxxx xxxxx xxxxx xxx xx",
+ \ "xxxxxxxxxxxxxxxxxx xxxxx xxxx, xxxx xxxx xxxx xxxx xxx xx xx xx xxxxxxx.",
+ \ "xxxx xxxx.",
+ \ "",
+ \ "> xx xx, xxxx xxxx xxx xxxx xxx xxxxx xxx xxx xxxxxxx xxx xxxxx xxxxxx",
+ \ "> xxxxxxx: xxxx xxxxxxx, xx xxxxxx xxxx xxxxxxxxxx",
+ \ ""
+ \ ], getline(1, '$'))
+
+ set fo& tw&
+ enew!
+endfunc
+
+" Test undo after ":%s" and formatting.
+func Test_format_undo()
+ enew!
+ map gg :.,.+2s/^/x/<CR>kk:set tw=3<CR>gqq
+
+ call append(0, [
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc"
+ \ ])
+ " undo/redo here to make the next undo only work on the following changes
+ exe "normal i\<C-G>u"
+ call cursor(1,1)
+ normal ggu
+ call assert_equal([
+ \ "aa aa aa aa",
+ \ "bb bb bb bb",
+ \ "cc cc cc cc",
+ \ ""
+ \ ], getline(1, '$'))
+
+ unmap gg
+ set tw&
+ enew!
+endfunc
+
+func Test_format_list_auto()
+ new
+ call setline(1, ['1. abc', '2. def', '3. ghi'])
+ set fo=tan ai bs=2
+ call feedkeys("3G0lli\<BS>\<BS>x\<Esc>", 'tx')
+ call assert_equal('2. defx ghi', getline(2))
+ bwipe!
+ set fo& ai& bs&
+endfunc
diff --git a/src/nvim/testdir/test_true_false.vim b/src/nvim/testdir/test_true_false.vim
index 84aca737ac..4a5d47471d 100644
--- a/src/nvim/testdir/test_true_false.vim
+++ b/src/nvim/testdir/test_true_false.vim
@@ -57,6 +57,9 @@ endfunc
" Test using TRUE or FALSE values for an argument.
func Test_true_false_arg()
+ let shellslash = &shellslash
+ let wildignore = &wildignore
+ set shellslash
call Try_arg_true_false('count(["a", "A"], "a", %v%)', 1, 2)
set wildignore=*.swp
@@ -110,6 +113,8 @@ func Test_true_false_arg()
let here_id = synID(1, 3, 0)
call Try_arg_true_false('synID(1, 3, %v%)', here_id, brackets_id)
bwipe!
+ let &wildignore = wildignore
+ let &shellslash = shellslash
endfunc
function Try_arg_non_zero(expr, false_val, true_val)
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 4a143d665d..74afc72f03 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1,8 +1,4 @@
" Tests for various Visual mode.
-if !has('visual')
- finish
-endif
-
func Test_block_shift_multibyte()
" Uses double-wide character.
@@ -278,9 +274,46 @@ func Test_visual_mode_reset()
set belloff&
endfunc
+func Test_Visual_word_textobject()
+ new
+ call setline(1, ['First sentence. Second sentence.'])
+
+ " When start and end of visual area are identical, 'aw' or 'iw' select
+ " the whole word.
+ norm! 1go2fcvawy
+ call assert_equal('Second ', @")
+ norm! 1go2fcviwy
+ call assert_equal('Second', @")
+
+ " When start and end of visual area are not identical, 'aw' or 'iw'
+ " extend the word in direction of the end of the visual area.
+ norm! 1go2fcvlawy
+ call assert_equal('cond ', @")
+ norm! gv2awy
+ call assert_equal('cond sentence.', @")
+
+ norm! 1go2fcvliwy
+ call assert_equal('cond', @")
+ norm! gv2iwy
+ call assert_equal('cond sentence', @")
+
+ " Extend visual area in opposite direction.
+ norm! 1go2fcvhawy
+ call assert_equal(' Sec', @")
+ norm! gv2awy
+ call assert_equal(' sentence. Sec', @")
+
+ norm! 1go2fcvhiwy
+ call assert_equal('Sec', @")
+ norm! gv2iwy
+ call assert_equal('. Sec', @")
+
+ bwipe!
+endfunc
+
func Test_Visual_sentence_textobject()
new
- call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fouth sentence'])
+ call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fourth sentence'])
" When start and end of visual area are identical, 'as' or 'is' select
" the whole sentence.
@@ -318,3 +351,63 @@ func Test_Visual_sentence_textobject()
bwipe!
endfunc
+
+func Test_curswant_not_changed()
+ new
+ call setline(1, ['one', 'two'])
+ au InsertLeave * call getcurpos()
+ call feedkeys("gg0\<C-V>jI123 \<Esc>j", 'xt')
+ call assert_equal([0, 2, 1, 0, 1], getcurpos())
+
+ bwipe!
+ au! InsertLeave
+endfunc
+
+func Test_Visual_paragraph_textobject()
+ new
+ call setline(1, ['First line.',
+ \ '',
+ \ 'Second line.',
+ \ 'Third line.',
+ \ 'Fourth line.',
+ \ 'Fifth line.',
+ \ '',
+ \ 'Sixth line.'])
+
+ " When start and end of visual area are identical, 'ap' or 'ip' select
+ " the whole paragraph.
+ norm! 4ggvapy
+ call assert_equal("Second line.\nThird line.\nFourth line.\nFifth line.\n\n", @")
+ norm! 4ggvipy
+ call assert_equal("Second line.\nThird line.\nFourth line.\nFifth line.\n", @")
+
+ " When start and end of visual area are not identical, 'ap' or 'ip'
+ " extend the sentence in direction of the end of the visual area.
+ " FIXME: actually, it is not sufficient to have different start and
+ " end of visual selection, the start line and end line have to differ,
+ " which is not consistent with the documentation.
+ norm! 4ggVjapy
+ call assert_equal("Third line.\nFourth line.\nFifth line.\n\n", @")
+ norm! gvapy
+ call assert_equal("Third line.\nFourth line.\nFifth line.\n\nSixth line.\n", @")
+ norm! 4ggVjipy
+ call assert_equal("Third line.\nFourth line.\nFifth line.\n", @")
+ norm! gvipy
+ call assert_equal("Third line.\nFourth line.\nFifth line.\n\n", @")
+ norm! gvipy
+ call assert_equal("Third line.\nFourth line.\nFifth line.\n\nSixth line.\n", @")
+
+ " Extend visual area in opposite direction.
+ norm! 5ggVkapy
+ call assert_equal("\nSecond line.\nThird line.\nFourth line.\n", @")
+ norm! gvapy
+ call assert_equal("First line.\n\nSecond line.\nThird line.\nFourth line.\n", @")
+ norm! 5ggVkipy
+ call assert_equal("Second line.\nThird line.\nFourth line.\n", @")
+ norma gvipy
+ call assert_equal("\nSecond line.\nThird line.\nFourth line.\n", @")
+ norm! gvipy
+ call assert_equal("First line.\n\nSecond line.\nThird line.\nFourth line.\n", @")
+
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index b3ab6957dc..57fb36abb8 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -518,4 +518,132 @@ func Test_winrestcmd()
only
endfunc
+func Test_relative_cursor_position_in_one_line_window()
+ new
+ only
+ call setline(1, range(1, 10000))
+ normal 50%
+ let lnum = getcurpos()[1]
+ split
+ split
+ " make third window take as many lines as possible, other windows will
+ " become one line
+ 3wincmd w
+ for i in range(1, &lines - 6)
+ wincmd +
+ redraw!
+ endfor
+
+ " first and second window should show cursor line
+ let wininfo = getwininfo()
+ call assert_equal(lnum, wininfo[0].topline)
+ call assert_equal(lnum, wininfo[1].topline)
+
+ only!
+ bwipe!
+endfunc
+
+func Test_relative_cursor_position_after_move_and_resize()
+ let so_save = &so
+ set so=0
+ enew
+ call setline(1, range(1, 10000))
+ normal 50%
+ split
+ 1wincmd w
+ " Move cursor to first line in window
+ normal H
+ redraw!
+ " Reduce window height to two lines
+ let height = winheight(0)
+ while winheight(0) > 2
+ wincmd -
+ redraw!
+ endwhile
+ " move cursor to second/last line in window
+ normal j
+ " restore previous height
+ while winheight(0) < height
+ wincmd +
+ redraw!
+ endwhile
+ " make window two lines again
+ while winheight(0) > 2
+ wincmd -
+ redraw!
+ endwhile
+
+ " cursor should be at bottom line
+ let info = getwininfo(win_getid())[0]
+ call assert_equal(info.topline + 1, getcurpos()[1])
+
+ only!
+ bwipe!
+ let &so = so_save
+endfunc
+
+func Test_relative_cursor_position_after_resize()
+ let so_save = &so
+ set so=0
+ enew
+ call setline(1, range(1, 10000))
+ normal 50%
+ split
+ 1wincmd w
+ let winid1 = win_getid()
+ let info = getwininfo(winid1)[0]
+ " Move cursor to second line in window
+ exe "normal " . (info.topline + 1) . "G"
+ redraw!
+ let lnum = getcurpos()[1]
+
+ " Make the window only two lines high, cursor should end up in top line
+ 2wincmd w
+ exe (info.height - 2) . "wincmd +"
+ redraw!
+ let info = getwininfo(winid1)[0]
+ call assert_equal(lnum, info.topline)
+
+ only!
+ bwipe!
+ let &so = so_save
+endfunc
+
+func Test_relative_cursor_second_line_after_resize()
+ let so_save = &so
+ set so=0
+ enew
+ call setline(1, range(1, 10000))
+ normal 50%
+ split
+ 1wincmd w
+ let winid1 = win_getid()
+ let info = getwininfo(winid1)[0]
+
+ " Make the window only two lines high
+ 2wincmd _
+
+ " Move cursor to second line in window
+ normal H
+ normal j
+
+ " Make window size bigger, then back to 2 lines
+ for i in range(1, 10)
+ wincmd +
+ redraw!
+ endfor
+ for i in range(1, 10)
+ wincmd -
+ redraw!
+ endfor
+
+ " cursor should end up in bottom line
+ let info = getwininfo(winid1)[0]
+ call assert_equal(info.topline + 1, getcurpos()[1])
+
+ only!
+ bwipe!
+ let &so = so_save
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 3eb88366d6..0bdda17299 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -10,6 +10,7 @@
#include "nvim/charset.h"
#include "nvim/main.h"
#include "nvim/aucmd.h"
+#include "nvim/ex_docmd.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -357,15 +358,17 @@ static bool handle_forced_escape(TermInput *input)
static void set_bg_deferred(void **argv)
{
char *bgvalue = argv[0];
- if (starting) {
- // Wait until after startup, so OptionSet is triggered.
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
- return;
- }
if (!option_was_set("bg") && !strequal((char *)p_bg, bgvalue)) {
// Value differs, apply it.
- set_option_value("bg", 0L, bgvalue, 0);
- reset_option_was_set("bg");
+ if (starting) {
+ // Wait until after startup, so OptionSet is triggered.
+ do_cmdline_cmd((bgvalue[0] == 'l')
+ ? "autocmd VimEnter * ++once ++nested set bg=light"
+ : "autocmd VimEnter * ++once ++nested set bg=dark");
+ } else {
+ set_option_value("bg", 0L, bgvalue, 0);
+ reset_option_was_set("bg");
+ }
}
}
@@ -424,7 +427,8 @@ static bool handle_background_color(TermInput *input)
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
char *bgvalue = luminance < 0.5 ? "dark" : "light";
DLOG("bg response: %s", bgvalue);
- loop_schedule(&main_loop, event_create(set_bg_deferred, 1, bgvalue));
+ loop_schedule_deferred(&main_loop,
+ event_create(set_bg_deferred, 1, bgvalue));
} else {
DLOG("failed to parse bg response");
}
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index e20cf15a79..3e7a3b1ba1 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -2000,7 +2000,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value,
} else if (strequal(name, "key_dc")) {
DLOG("libtermkey:kdch1=%s", value);
// Vim: "If <BS> and <DEL> are now the same, redefine <DEL>."
- if (value != NULL && strequal(stty_erase, value)) {
+ if (value != NULL && value != (char *)-1 && strequal(stty_erase, value)) {
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
}
} else if (strequal(name, "key_mouse")) {
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 9ad3f851ad..50432dd119 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -158,6 +158,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
if (insert_at < kv_size(layers)-1) {
for (size_t i = kv_size(layers)-1; i > insert_at; i--) {
kv_A(layers, i) = kv_A(layers, i-1);
+ kv_A(layers, i)->comp_index = i;
}
kv_A(layers, insert_at) = grid;
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 18fad76a95..edb5b06a2e 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -541,9 +541,7 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
Error *err)
{
- bool new = false;
if (wp == NULL) {
- new = true;
wp = win_alloc(lastwin_nofloating(), false);
win_init(wp, curwin, 0);
} else {
@@ -569,12 +567,13 @@ win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
wp->w_floating = 1;
wp->w_status_height = 0;
wp->w_vsep_width = 0;
+
+ // TODO(bfredl): use set_option_to() after merging #9110 ?
+ wp->w_p_nu = false;
+ wp->w_allbuf_opt.wo_nu = false;
win_config_float(wp, width, height, config);
wp->w_pos_changed = true;
redraw_win_later(wp, VALID);
- if (new) {
- win_enter(wp, false);
- }
return wp;
}
@@ -591,6 +590,7 @@ void win_config_float(win_T *wp, int width, int height,
config.window = curwin->handle;
}
+ bool change_external = config.external != wp->w_float_config.external;
wp->w_float_config = config;
if (!ui_has(kUIMultigrid)) {
@@ -601,6 +601,10 @@ void win_config_float(win_T *wp, int width, int height,
win_set_inner_size(wp);
must_redraw = MAX(must_redraw, VALID);
wp->w_pos_changed = true;
+ if (change_external) {
+ wp->w_hl_needs_update = true;
+ redraw_win_later(wp, NOT_VALID);
+ }
}
static void ui_ext_win_position(win_T *wp)
@@ -806,12 +810,12 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
api_set_error(err, kErrorTypeValidation,
"Only one of 'relative' and 'external' should be used");
return false;
- } else if (has_relative) {
- out->external = false;
} else if (!reconf && !has_relative && !has_external) {
api_set_error(err, kErrorTypeValidation,
"One of 'relative' and 'external' must be used");
return false;
+ } else if (has_relative) {
+ out->external = false;
}
if (out->external && !ui_has(kUIMultigrid)) {
@@ -5332,7 +5336,10 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
void set_fraction(win_T *wp)
{
if (wp->w_height_inner > 1) {
- wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height_inner / 2)
+ // When cursor is in the first line the percentage is computed as if
+ // it's halfway that line. Thus with two lines it is 25%, with three
+ // lines 17%, etc. Similarly for the last line: 75%, 83%, etc.
+ wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2)
/ (long)wp->w_height_inner;
}
}
@@ -5364,8 +5371,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
int sline, line_size;
int height = wp->w_height_inner;
- /* Don't change w_topline when height is zero. Don't set w_topline when
- * 'scrollbind' is set and this isn't the current window. */
+ // Don't change w_topline when height is zero. Don't set w_topline when
+ // 'scrollbind' is set and this isn't the current window.
if (height > 0
&& (!wp->w_p_scb || wp == curwin)
) {
@@ -5376,8 +5383,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
lnum = wp->w_cursor.lnum;
if (lnum < 1) /* can happen when starting up */
lnum = 1;
- wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L + FRACTION_MULT / 2)
- / FRACTION_MULT;
+ wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT;
line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1;
sline = wp->w_wrow - line_size;
@@ -5408,7 +5414,6 @@ void scroll_to_fraction(win_T *wp, int prev_height)
wp->w_wrow--;
}
}
- set_topline(wp, lnum);
} else if (sline > 0) {
while (sline > 0 && lnum > 1) {
(void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL);
@@ -5437,12 +5442,12 @@ void scroll_to_fraction(win_T *wp, int prev_height)
lnum++;
wp->w_wrow -= line_size + sline;
} else if (sline > 0) {
- /* First line of file reached, use that as topline. */
+ // First line of file reached, use that as topline.
lnum = 1;
wp->w_wrow -= sline;
}
- set_topline(wp, lnum);
}
+ set_topline(wp, lnum);
}
if (wp == curwin) {
@@ -5546,10 +5551,11 @@ void command_height(void)
* p_ch was changed in another tab page. */
curtab->tp_ch_used = p_ch;
- /* Find bottom frame with width of screen. */
- frp = lastwin->w_frame;
- while (frp->fr_width != Columns && frp->fr_parent != NULL)
+ // Find bottom frame with width of screen.
+ frp = lastwin_nofloating()->w_frame;
+ while (frp->fr_width != Columns && frp->fr_parent != NULL) {
frp = frp->fr_parent;
+ }
/* Avoid changing the height of a window with 'winfixheight' set. */
while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
@@ -5708,9 +5714,9 @@ file_name_in_line (
len = 0;
while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
|| ((options & FNAME_HYP) && path_is_url((char *)ptr + len))
- || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL)) {
- // After type:// we also include ?, & and = as valid characters, so that
- // http://google.com?q=this&that=ok works.
+ || (is_url && vim_strchr((char_u *)":?&=", ptr[len]) != NULL)) {
+ // After type:// we also include :, ?, & and = as valid characters, so that
+ // http://google.com:8080?q=this&that=ok works.
if ((ptr[len] >= 'A' && ptr[len] <= 'Z')
|| (ptr[len] >= 'a' && ptr[len] <= 'z')) {
if (in_type && path_is_url((char *)ptr + len + 1)) {