aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c10
-rw-r--r--src/nvim/api/vim.c8
-rw-r--r--src/nvim/api/window.c2
-rw-r--r--src/nvim/arabic.c4
-rw-r--r--src/nvim/ascii.h12
-rw-r--r--src/nvim/autocmd.c31
-rw-r--r--src/nvim/autocmd.h14
-rw-r--r--src/nvim/buffer.c92
-rw-r--r--src/nvim/buffer_defs.h11
-rw-r--r--src/nvim/buffer_updates.c4
-rw-r--r--src/nvim/diff.c5
-rw-r--r--src/nvim/edit.c33
-rw-r--r--src/nvim/eval.c35
-rw-r--r--src/nvim/eval/funcs.c6
-rw-r--r--src/nvim/eval/userfunc.c5
-rw-r--r--src/nvim/ex_cmds.c99
-rw-r--r--src/nvim/ex_cmds2.c10
-rw-r--r--src/nvim/ex_docmd.c34
-rw-r--r--src/nvim/ex_eval.c35
-rw-r--r--src/nvim/ex_getln.c30
-rw-r--r--src/nvim/ex_session.c12
-rw-r--r--src/nvim/fileio.c22
-rw-r--r--src/nvim/fold.c3
-rw-r--r--src/nvim/func_attr.h2
-rw-r--r--src/nvim/generators/c_grammar.lua1
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua17
-rw-r--r--src/nvim/getchar.c6
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/main.c27
-rw-r--r--src/nvim/memline.c6
-rw-r--r--src/nvim/message.c30
-rw-r--r--src/nvim/move.c552
-rw-r--r--src/nvim/msgpack_rpc/channel.c6
-rw-r--r--src/nvim/normal.c129
-rw-r--r--src/nvim/ops.c53
-rw-r--r--src/nvim/option.c11
-rw-r--r--src/nvim/popupmnu.c2
-rw-r--r--src/nvim/quickfix.c22
-rw-r--r--src/nvim/regexp.c33
-rw-r--r--src/nvim/regexp_nfa.c38
-rw-r--r--src/nvim/screen.c53
-rw-r--r--src/nvim/sign.c70
-rw-r--r--src/nvim/sign_defs.h16
-rw-r--r--src/nvim/spellfile.c2
-rw-r--r--src/nvim/syntax.c35
-rw-r--r--src/nvim/testdir/check.vim11
-rw-r--r--src/nvim/testdir/setup.vim1
-rw-r--r--src/nvim/testdir/test_bufline.vim2
-rw-r--r--src/nvim/testdir/test_clientserver.vim9
-rw-r--r--src/nvim/testdir/test_cmdline.vim111
-rw-r--r--src/nvim/testdir/test_digraph.vim22
-rw-r--r--src/nvim/testdir/test_fileformat.vim23
-rw-r--r--src/nvim/testdir/test_filetype.vim1
-rw-r--r--src/nvim/testdir/test_fold.vim7
-rw-r--r--src/nvim/testdir/test_global.vim11
-rw-r--r--src/nvim/testdir/test_help.vim46
-rw-r--r--src/nvim/testdir/test_messages.vim59
-rw-r--r--src/nvim/testdir/test_mksession.vim23
-rw-r--r--src/nvim/testdir/test_normal.vim15
-rw-r--r--src/nvim/testdir/test_quickfix.vim24
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim11
-rw-r--r--src/nvim/testdir/test_registers.vim57
-rw-r--r--src/nvim/testdir/test_signals.vim140
-rw-r--r--src/nvim/testdir/test_signs.vim8
-rw-r--r--src/nvim/testdir/test_startup.vim50
-rw-r--r--src/nvim/testdir/test_tabpage.vim107
-rw-r--r--src/nvim/testdir/test_textformat.vim222
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c57
69 files changed, 1917 insertions, 734 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index db37e2100d..f1151d196a 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -121,6 +121,8 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - buffer handle
/// - utf_sizes: include UTF-32 and UTF-16 size of the replaced
/// region, as args to `on_lines`.
+/// - preview: also attach to command preview (i.e. 'inccommand')
+/// events.
/// @param[out] err Error details, if any
/// @return False if attach failed (invalid parameter, or buffer isn't loaded);
/// otherwise True. TODO: LUA_API_NO_EVAL
@@ -176,6 +178,12 @@ Boolean nvim_buf_attach(uint64_t channel_id,
goto error;
}
cb.utf_sizes = v->data.boolean;
+ } else if (is_lua && strequal("preview", k.data)) {
+ if (v->type != kObjectTypeBoolean) {
+ api_set_error(err, kErrorTypeValidation, "preview must be boolean");
+ goto error;
+ }
+ cb.preview = v->data.boolean;
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
goto error;
@@ -329,6 +337,7 @@ void nvim_buf_set_lines(uint64_t channel_id,
ArrayOf(String) replacement,
Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -779,6 +788,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer)
/// - unload: Unloaded only, do not delete. See |:bunload|
void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
FUNC_API_SINCE(7)
+ FUNC_API_CHECK_TEXTLOCK
{
buf_T *buf = find_buffer_by_handle(buffer, err);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 8b80d4aad9..1e972e01be 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -857,6 +857,7 @@ String nvim_get_current_line(Error *err)
/// @param[out] err Error details, if any
void nvim_set_current_line(String line, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
}
@@ -866,6 +867,7 @@ void nvim_set_current_line(String line, Error *err)
/// @param[out] err Error details, if any
void nvim_del_current_line(Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
@@ -1060,6 +1062,7 @@ Buffer nvim_get_current_buf(void)
/// @param[out] err Error details, if any
void nvim_set_current_buf(Buffer buffer, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -1114,6 +1117,7 @@ Window nvim_get_current_win(void)
/// @param[out] err Error details, if any
void nvim_set_current_win(Window window, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
win_T *win = find_window_by_handle(window, err);
@@ -1263,6 +1267,7 @@ fail:
Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
Error *err)
FUNC_API_SINCE(6)
+ FUNC_API_CHECK_TEXTLOCK
{
FloatConfig fconfig = FLOAT_CONFIG_INIT;
if (!parse_float_config(config, &fconfig, false, err)) {
@@ -1327,6 +1332,7 @@ Tabpage nvim_get_current_tabpage(void)
/// @param[out] err Error details, if any
void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
FUNC_API_SINCE(1)
+ FUNC_API_CHECK_TEXTLOCK
{
tabpage_T *tp = find_tab_by_handle(tabpage, err);
@@ -1411,6 +1417,7 @@ Dictionary nvim_get_namespaces(void)
/// - false: Client must cancel the paste.
Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
FUNC_API_SINCE(6)
+ FUNC_API_CHECK_TEXTLOCK
{
static bool draining = false;
bool cancel = false;
@@ -1483,6 +1490,7 @@ theend:
void nvim_put(ArrayOf(String) lines, String type, Boolean after,
Boolean follow, Error *err)
FUNC_API_SINCE(6)
+ FUNC_API_CHECK_TEXTLOCK
{
yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1);
if (!prepare_yankreg_from_object(reg, type, lines.size)) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index a3ec1c8e53..f4af1632ec 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -44,6 +44,7 @@ Buffer nvim_win_get_buf(Window window, Error *err)
/// @param[out] err Error details, if any
void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
FUNC_API_SINCE(5)
+ FUNC_API_CHECK_TEXTLOCK
{
win_T *win = find_window_by_handle(window, err), *save_curwin = curwin;
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -500,6 +501,7 @@ Dictionary nvim_win_get_config(Window window, Error *err)
/// @param[out] err Error details, if any
void nvim_win_close(Window window, Boolean force, Error *err)
FUNC_API_SINCE(6)
+ FUNC_API_CHECK_TEXTLOCK
{
win_T *win = find_window_by_handle(window, err);
if (!win) {
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 7f12c0c798..9fba38a49f 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -719,9 +719,7 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1,
// half-shape current and previous character
int shape_c = half_shape(prev_c);
- // Save away current character
- int curr_c = c;
-
+ int curr_c;
int curr_laa = A_firstc_laa(c, *c1p);
int prev_laa = A_firstc_laa(prev_c, prev_c1);
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index 2397af27cc..f41068ea70 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -89,6 +89,10 @@ static inline bool ascii_iswhite(int)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
+static inline bool ascii_iswhite_or_nul(int)
+ REAL_FATTR_CONST
+ REAL_FATTR_ALWAYS_INLINE;
+
static inline bool ascii_isdigit(int)
REAL_FATTR_CONST
REAL_FATTR_ALWAYS_INLINE;
@@ -117,6 +121,14 @@ static inline bool ascii_iswhite(int c)
return c == ' ' || c == '\t';
}
+/// Checks if `c` is a space or tab character or NUL.
+///
+/// @see {ascii_isdigit}
+static inline bool ascii_iswhite_or_nul(int c)
+{
+ return ascii_iswhite(c) || c == NUL;
+}
+
/// Check whether character is a decimal digit.
///
/// Library isdigit() function is officially locale-dependent and, for
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 53b11c250e..42224d0a4f 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1106,9 +1106,9 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
win = curwin;
}
- aco->save_curwin = curwin;
- aco->save_prevwin = prevwin;
+ aco->save_curwin_handle = curwin->handle;
aco->save_curbuf = curbuf;
+ aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle;
if (win != NULL) {
// There is a window for "buf" in the current tab page, make it the
// curwin. This is preferred, it has the least side effects (esp. if
@@ -1148,7 +1148,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
curwin = aucmd_win;
}
curbuf = buf;
- aco->new_curwin = curwin;
+ aco->new_curwin_handle = curwin->handle;
set_bufref(&aco->new_curbuf, curbuf);
}
@@ -1194,14 +1194,14 @@ void aucmd_restbuf(aco_save_T *aco)
unblock_autocmds();
- if (win_valid(aco->save_curwin)) {
- curwin = aco->save_curwin;
+ win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle);
+ if (save_curwin != NULL) {
+ curwin = save_curwin;
} else {
// Hmm, original window disappeared. Just use the first one.
curwin = firstwin;
}
- prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
- : firstwin; // window disappeared?
+ prevwin = win_find_by_handle(aco->save_prevwin_handle);
vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables
hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab
curbuf = curwin->w_buffer;
@@ -1216,11 +1216,14 @@ void aucmd_restbuf(aco_save_T *aco)
curwin->w_topfill = 0;
}
} else {
- // restore curwin
- if (win_valid(aco->save_curwin)) {
+ // Restore curwin. Use the window ID, a window may have been closed
+ // and the memory re-used for another one.
+ win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle);
+ if (save_curwin != NULL) {
// Restore the buffer which was previously edited by curwin, if it was
// changed, we are still the same window and the buffer is valid.
- if (curwin == aco->new_curwin && curbuf != aco->new_curbuf.br_buf
+ if (curwin->handle == aco->new_curwin_handle
+ && curbuf != aco->new_curbuf.br_buf
&& bufref_valid(&aco->new_curbuf)
&& aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) {
if (curwin->w_s == &curbuf->b_s) {
@@ -1232,10 +1235,9 @@ void aucmd_restbuf(aco_save_T *aco)
curbuf->b_nwindows++;
}
- curwin = aco->save_curwin;
- prevwin = win_valid(aco->save_prevwin) ? aco->save_prevwin
- : firstwin; // window disappeared?
+ curwin = save_curwin;
curbuf = curwin->w_buffer;
+ prevwin = win_find_by_handle(aco->save_prevwin_handle);
// In case the autocommand moves the cursor to a position that does not
// exist in curbuf
check_cursor();
@@ -1717,7 +1719,8 @@ void unblock_autocmds(void)
}
}
-static inline bool is_autocmd_blocked(void)
+bool is_autocmd_blocked(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return autocmd_blocked != 0;
}
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index af1eeb0fc4..1c0f88f08f 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -7,13 +7,13 @@
// Struct to save values in before executing autocommands for a buffer that is
// not the current buffer.
typedef struct {
- buf_T *save_curbuf; ///< saved curbuf
- int use_aucmd_win; ///< using aucmd_win
- win_T *save_curwin; ///< saved curwin
- win_T *save_prevwin; ///< saved prevwin
- win_T *new_curwin; ///< new curwin
- bufref_T new_curbuf; ///< new curbuf
- char_u *globaldir; ///< saved value of globaldir
+ buf_T *save_curbuf; ///< saved curbuf
+ bool use_aucmd_win; ///< using aucmd_win
+ handle_T save_curwin_handle; ///< ID of saved curwin
+ handle_T new_curwin_handle; ///< ID of new curwin
+ handle_T save_prevwin_handle; ///< ID of saved prevwin
+ bufref_T new_curbuf; ///< new curbuf
+ char_u *globaldir; ///< saved value of globaldir
} aco_save_T;
typedef struct AutoCmd {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 839d61cd2e..93a03986e5 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -602,8 +602,12 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
* Remove the buffer from the list.
*/
if (wipe_buf) {
- xfree(buf->b_ffname);
- xfree(buf->b_sfname);
+ if (buf->b_sfname != buf->b_ffname) {
+ XFREE_CLEAR(buf->b_sfname);
+ } else {
+ buf->b_sfname = NULL;
+ }
+ XFREE_CLEAR(buf->b_ffname);
if (buf->b_prev == NULL) {
firstbuf = buf->b_next;
} else {
@@ -1583,7 +1587,7 @@ void enter_buffer(buf_T *buf)
need_fileinfo = true; // display file info after redraw
}
// check if file changed
- (void)buf_check_timestamp(curbuf, false);
+ (void)buf_check_timestamp(curbuf);
curwin->w_topline = 1;
curwin->w_topfill = 0;
@@ -1693,15 +1697,18 @@ static inline void buf_init_changedtick(buf_T *const buf)
/// if the buffer already exists.
/// This is the ONLY way to create a new buffer.
///
-/// @param ffname full path of fname or relative
-/// @param sfname short fname or NULL
+/// @param ffname_arg full path of fname or relative
+/// @param sfname_arg short fname or NULL
/// @param lnum preferred cursor line
/// @param flags BLN_ defines
/// @param bufnr
///
/// @return pointer to the buffer
-buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
+buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum,
+ int flags)
{
+ char_u *ffname = ffname_arg;
+ char_u *sfname = sfname_arg;
buf_T *buf;
fname_expand(curbuf, &ffname, &sfname); // will allocate ffname
@@ -1787,8 +1794,12 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf->b_wininfo = xcalloc(1, sizeof(wininfo_T));
if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) {
+ if (buf->b_sfname != buf->b_ffname) {
+ XFREE_CLEAR(buf->b_sfname);
+ } else {
+ buf->b_sfname = NULL;
+ }
XFREE_CLEAR(buf->b_ffname);
- XFREE_CLEAR(buf->b_sfname);
if (buf != curbuf) {
free_buffer(buf);
}
@@ -2778,28 +2789,32 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum)
return OK;
}
-/*
- * Set the file name for "buf"' to 'ffname', short file name to 'sfname'.
- * The file name with the full path is also remembered, for when :cd is used.
- * Returns FAIL for failure (file name already in use by other buffer)
- * OK otherwise.
- */
-int
-setfname(
+// Set the file name for "buf" to "ffname_arg", short file name to
+// "sfname_arg".
+// The file name with the full path is also remembered, for when :cd is used.
+// Returns FAIL for failure (file name already in use by other buffer)
+// OK otherwise.
+int setfname(
buf_T *buf,
- char_u *ffname,
- char_u *sfname,
+ char_u *ffname_arg,
+ char_u *sfname_arg,
bool message // give message when buffer already exists
)
{
+ char_u *ffname = ffname_arg;
+ char_u *sfname = sfname_arg;
buf_T *obuf = NULL;
FileID file_id;
bool file_id_valid = false;
if (ffname == NULL || *ffname == NUL) {
// Removing the name.
+ if (buf->b_sfname != buf->b_ffname) {
+ XFREE_CLEAR(buf->b_sfname);
+ } else {
+ buf->b_sfname = NULL;
+ }
XFREE_CLEAR(buf->b_ffname);
- XFREE_CLEAR(buf->b_sfname);
} else {
fname_expand(buf, &ffname, &sfname); // will allocate ffname
if (ffname == NULL) { // out of memory
@@ -2830,8 +2845,10 @@ setfname(
#ifdef USE_FNAME_CASE
path_fix_case(sfname); // set correct case for short file name
#endif
+ if (buf->b_sfname != buf->b_ffname) {
+ xfree(buf->b_sfname);
+ }
xfree(buf->b_ffname);
- xfree(buf->b_sfname);
buf->b_ffname = ffname;
buf->b_sfname = sfname;
}
@@ -2857,7 +2874,9 @@ void buf_set_name(int fnum, char_u *name)
buf = buflist_findnr(fnum);
if (buf != NULL) {
- xfree(buf->b_sfname);
+ if (buf->b_sfname != buf->b_ffname) {
+ xfree(buf->b_sfname);
+ }
xfree(buf->b_ffname);
buf->b_ffname = vim_strsave(name);
buf->b_sfname = NULL;
@@ -4633,16 +4652,18 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
return true;
}
-/*
- * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL.
- * "ffname" becomes a pointer to allocated memory (or NULL).
- */
+// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
+// "*ffname" becomes a pointer to allocated memory (or NULL).
+// When resolving a link both "*sfname" and "*ffname" will point to the same
+// allocated memory.
+// The "*ffname" and "*sfname" pointer values on call will not be freed.
+// Note that the resulting "*ffname" pointer should be considered not allocaed.
void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
{
- if (*ffname == NULL) { // if no file name given, nothing to do
+ if (*ffname == NULL) { // no file name given, nothing to do
return;
}
- if (*sfname == NULL) { // if no short file name given, use ffname
+ if (*sfname == NULL) { // no short file name given, use ffname
*sfname = *ffname;
}
*ffname = (char_u *)fix_fname((char *)(*ffname)); // expand to full path
@@ -4685,7 +4706,6 @@ do_arg_all(
int keep_tabs // keep current tabs, for ":tab drop file"
)
{
- int i;
char_u *opened; // Array of weight for which args are open:
// 0: not opened
// 1: opened in other tab
@@ -4694,6 +4714,7 @@ do_arg_all(
int opened_len; // length of opened[]
int use_firstwin = false; // use first window for arglist
+ bool tab_drop_empty_window = false;
int split_ret = OK;
bool p_ea_save;
alist_T *alist; // argument list to be used
@@ -4741,6 +4762,7 @@ do_arg_all(
win_T *wpnext = NULL;
tpnext = curtab->tp_next;
for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
+ int i;
wpnext = wp->w_next;
buf = wp->w_buffer;
if (buf->b_ffname == NULL
@@ -4846,14 +4868,15 @@ do_arg_all(
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
- // ":drop all" should re-use an empty window to avoid "--remote-tab"
+ // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
// leaving an empty tab page when executed locally.
if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1
&& curbuf->b_ffname == NULL && !curbuf->b_changed) {
use_firstwin = true;
+ tab_drop_empty_window = true;
}
- for (i = 0; i < count && i < opened_len && !got_int; i++) {
+ for (int i = 0; i < count && !got_int; i++) {
if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
arg_had_last = true;
}
@@ -4873,6 +4896,10 @@ do_arg_all(
}
}
} else if (split_ret == OK) {
+ // trigger events for tab drop
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter--;
+ }
if (!use_firstwin) { // split current window
p_ea_save = p_ea;
p_ea = true; // use space from all windows
@@ -4898,6 +4925,9 @@ do_arg_all(
|| bufIsChanged(curwin->w_buffer))
? ECMD_HIDE : 0) + ECMD_OLDBUF,
curwin);
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter++;
+ }
if (use_firstwin) {
autocmd_no_leave++;
}
@@ -5449,7 +5479,9 @@ int buf_signcols(buf_T *buf)
curline = sign->lnum;
linesum = 0;
}
- linesum++;
+ if (sign->has_text_or_icon) {
+ linesum++;
+ }
}
if (linesum > buf->b_signcols_max) {
buf->b_signcols_max = linesum;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index dba02a67e8..cc09b7e989 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -491,9 +491,10 @@ typedef struct {
LuaRef on_changedtick;
LuaRef on_detach;
bool utf_sizes;
+ bool preview;
} BufUpdateCallbacks;
#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, false }
+ LUA_NOREF, false, false }
EXTERN int curbuf_splice_pending INIT(= 0);
@@ -532,9 +533,11 @@ struct file_buffer {
// b_fname is the same as b_sfname, unless ":cd" has been done,
// then it is the same as b_ffname (NULL for no name).
//
- char_u *b_ffname; // full path file name
- char_u *b_sfname; // short file name
- char_u *b_fname; // current file name
+ char_u *b_ffname; // full path file name, allocated
+ char_u *b_sfname; // short file name, allocated, may be equal to
+ // b_ffname
+ char_u *b_fname; // current file name, points to b_ffname or
+ // b_sfname
bool file_id_valid;
FileID file_id;
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index fc671ad9e2..68e123896b 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -237,7 +237,7 @@ void buf_updates_send_changes(buf_T *buf,
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
bool keep = true;
- if (cb.on_lines != LUA_NOREF) {
+ if (cb.on_lines != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) {
Array args = ARRAY_DICT_INIT;
Object items[8];
args.size = 6; // may be increased to 8 below
@@ -298,7 +298,7 @@ void buf_updates_send_splice(
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
bool keep = true;
- if (cb.on_bytes != LUA_NOREF) {
+ if (cb.on_bytes != LUA_NOREF && (cb.preview || !(State & CMDPREVIEW))) {
FIXED_TEMP_ARRAY(args, 11);
// the first argument is always the buffer handle
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 1cdf84f9d0..93bc34fa4b 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -805,7 +805,7 @@ static void diff_try_update(diffio_T *dio,
for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) {
buf = curtab->tp_diffbuf[idx_new];
if (buf_valid(buf)) {
- buf_check_timestamp(buf, false);
+ buf_check_timestamp(buf);
}
}
}
@@ -1225,8 +1225,7 @@ void ex_diffpatch(exarg_T *eap)
EMSG(_("E816: Cannot read patch output"));
} else {
if (curbuf->b_fname != NULL) {
- newname = vim_strnsave(curbuf->b_fname,
- (int)(STRLEN(curbuf->b_fname) + 4));
+ newname = vim_strnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4);
STRCAT(newname, ".new");
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 1cf821f786..962ef9b245 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -381,7 +381,7 @@ static void insert_enter(InsertState *s)
// Need to recompute the cursor position, it might move when the cursor is
// on a TAB or special character.
- curs_columns(true);
+ curs_columns(curwin, true);
// Enable langmap or IME, indicated by 'iminsert'.
// Note that IME may enabled/disabled without us noticing here, thus the
@@ -594,7 +594,7 @@ static int insert_check(VimState *state)
if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
&& curwin->w_wrow == curwin->w_winrow
- + curwin->w_height_inner - 1 - get_scrolloff_value()
+ + curwin->w_height_inner - 1 - get_scrolloff_value(curwin)
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) {
@@ -608,7 +608,7 @@ static int insert_check(VimState *state)
}
// May need to adjust w_topline to show the cursor.
- update_topline();
+ update_topline(curwin);
s->did_backspace = false;
@@ -1561,7 +1561,7 @@ void edit_putchar(int c, bool highlight)
int attr;
if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) {
- update_topline(); // just in case w_topline isn't valid
+ update_topline(curwin); // just in case w_topline isn't valid
validate_cursor();
if (highlight) {
attr = HL_ATTR(HLF_8);
@@ -1677,7 +1677,7 @@ void display_dollar(colnr_T col)
// If on the last byte of a multi-byte move to the first byte.
char_u *p = get_cursor_line_ptr();
curwin->w_cursor.col -= utf_head_off(p, p + col);
- curs_columns(false); // Recompute w_wrow and w_wcol
+ curs_columns(curwin, false); // Recompute w_wrow and w_wcol
if (curwin->w_wcol < curwin->w_grid.Columns) {
edit_putchar('$', false);
dollar_vcol = curwin->w_virtcol;
@@ -3423,7 +3423,7 @@ static void ins_compl_addleader(int c)
xfree(compl_leader);
compl_leader = vim_strnsave(get_cursor_line_ptr() + compl_col,
- (int)(curwin->w_cursor.col - compl_col));
+ curwin->w_cursor.col - compl_col);
ins_compl_new_leader();
}
@@ -3716,7 +3716,7 @@ static bool ins_compl_prep(int c)
retval = true;
}
- auto_format(FALSE, TRUE);
+ auto_format(false, true);
// Trigger the CompleteDonePre event to give scripts a chance to
// act upon the completion before clearing the info, and restore
@@ -3810,10 +3810,10 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg)
*/
static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
{
- static win_T *wp;
+ static win_T *wp = NULL;
if (flag == 'w') { // just windows
- if (buf == curbuf) { // first call for this flag/expansion
+ if (buf == curbuf || wp == NULL) { // first call for this flag/expansion
wp = curwin;
}
assert(wp);
@@ -5327,8 +5327,9 @@ static int ins_complete(int c, bool enable_pum)
compl_curr_match->cp_number);
edit_submode_extra = match_ref;
edit_submode_highl = HLF_R;
- if (dollar_vcol >= 0)
- curs_columns(FALSE);
+ if (dollar_vcol >= 0) {
+ curs_columns(curwin, false);
+ }
}
}
}
@@ -6158,7 +6159,7 @@ internal_format (
curwin->w_p_lbr = has_lbr;
if (!format_only && haveto_redraw) {
- update_topline();
+ update_topline(curwin);
redraw_curbuf_later(VALID);
}
}
@@ -6503,7 +6504,7 @@ stop_insert (
curwin->w_cursor = tpos;
}
- auto_format(TRUE, FALSE);
+ auto_format(true, false);
if (ascii_iswhite(cc)) {
if (gchar_cursor() != NUL)
@@ -6807,7 +6808,7 @@ cursor_up (
coladvance(curwin->w_curswant);
if (upd_topline) {
- update_topline(); // make sure curwin->w_topline is valid
+ update_topline(curwin); // make sure curwin->w_topline is valid
}
return OK;
@@ -6858,7 +6859,7 @@ cursor_down (
coladvance(curwin->w_curswant);
if (upd_topline) {
- update_topline(); // make sure curwin->w_topline is valid
+ update_topline(curwin); // make sure curwin->w_topline is valid
}
return OK;
@@ -7802,7 +7803,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Otherwise remove the mode message.
if (reg_recording != 0 || restart_edit != NUL) {
showmode();
- } else if (p_smd) {
+ } else if (p_smd && (got_int || !skip_showmode())) {
MSG("");
}
// Exit Insert mode
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a7a860ba72..7916e3a66a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2556,6 +2556,7 @@ void free_for_info(void *fi_void)
void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
+ FUNC_ATTR_NONNULL_ALL
{
int got_eq = FALSE;
int c;
@@ -2638,6 +2639,23 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
}
}
}
+
+ // ":exe one two" completes "two"
+ if ((cmdidx == CMD_execute
+ || cmdidx == CMD_echo
+ || cmdidx == CMD_echon
+ || cmdidx == CMD_echomsg)
+ && xp->xp_context == EXPAND_EXPRESSION) {
+ for (;;) {
+ char_u *const n = skiptowhite(arg);
+
+ if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) {
+ break;
+ }
+ arg = skipwhite(n);
+ }
+ }
+
xp->xp_pattern = arg;
}
@@ -5404,7 +5422,7 @@ static int get_literal_key(char_u **arg, typval_T *tv)
for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {
}
tv->v_type = VAR_STRING;
- tv->vval.v_string = vim_strnsave(*arg, (int)(p - *arg));
+ tv->vval.v_string = vim_strnsave(*arg, p - *arg);
*arg = skipwhite(p);
return OK;
@@ -7070,7 +7088,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append,
}
}
check_cursor_col();
- update_topline();
+ update_topline(curwin);
}
if (!is_curbuf) {
@@ -7782,13 +7800,13 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,
if (name[0] == 'w' && dollar_lnum) {
pos.col = 0;
if (name[1] == '0') { // "w0": first visible line
- update_topline();
+ update_topline(curwin);
// In silent Ex mode topline is zero, but that's not a valid line
// number; use one instead.
pos.lnum = curwin->w_topline > 0 ? curwin->w_topline : 1;
return &pos;
} else if (name[1] == '$') { // "w$": last visible line
- validate_botline();
+ validate_botline(curwin);
// In silent Ex mode botline is zero, return zero then.
pos.lnum = curwin->w_botline > 0 ? curwin->w_botline - 1 : 0;
return &pos;
@@ -10246,9 +10264,6 @@ repeat:
if (src[*usedlen] == ':'
&& (src[*usedlen + 1] == 's'
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
- char_u *str;
- char_u *pat;
- char_u *sub;
int sep;
char_u *flags;
int didit = FALSE;
@@ -10265,13 +10280,13 @@ repeat:
// find end of pattern
p = vim_strchr(s, sep);
if (p != NULL) {
- pat = vim_strnsave(s, (int)(p - s));
+ char_u *const pat = vim_strnsave(s, p - s);
s = p + 1;
// find end of substitution
p = vim_strchr(s, sep);
if (p != NULL) {
- sub = vim_strnsave(s, (int)(p - s));
- str = vim_strnsave(*fnamep, *fnamelen);
+ char_u *const sub = vim_strnsave(s, p - s);
+ char_u *const str = vim_strnsave(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 89d9a85775..16eb6f8898 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -4206,6 +4206,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = true;
} else if (STRICMP(name, "syntax_items") == 0) {
n = syntax_present(curwin);
+ } else if (STRICMP(name, "clipboard_working") == 0) {
+ n = eval_has_provider("clipboard");
#ifdef UNIX
} else if (STRICMP(name, "unnamedplus") == 0) {
n = eval_has_provider("clipboard");
@@ -6551,7 +6553,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (prevlen == 0) {
assert(len < INT_MAX);
- s = vim_strnsave(start, (int)len);
+ s = vim_strnsave(start, len);
} else {
/* Change "prev" buffer to be the right size. This way
* the bytes are only copied once, and very long lines are
@@ -10853,7 +10855,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
}
- rettv->vval.v_string = vim_strnsave(head, (int)(tail - head));
+ rettv->vval.v_string = vim_strnsave(head, tail - head);
}
/*
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 8daef00985..70c998ef39 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2297,9 +2297,9 @@ void ex_function(exarg_T *eap)
// Ignore leading white space.
p = skipwhite(p + 4);
heredoc_trimmed =
- vim_strnsave(theline, (int)(skipwhite(theline) - theline));
+ vim_strnsave(theline, skipwhite(theline) - theline);
}
- skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p));
+ skip_until = vim_strnsave(p, skiptowhite(p) - p);
do_concat = false;
is_heredoc = true;
}
@@ -2484,6 +2484,7 @@ errret_2:
ga_clear_strings(&newlines);
ret_free:
xfree(skip_until);
+ xfree(heredoc_trimmed);
xfree(line_to_free);
xfree(fudi.fd_newkey);
xfree(name);
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index b0a51eaefd..a7d97c904b 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2066,19 +2066,20 @@ static int check_readonly(int *forceit, buf_T *buf)
return FALSE;
}
-/*
- * Try to abandon current file and edit a new or existing file.
- * "fnum" is the number of the file, if zero use ffname/sfname.
- * "lnum" is the line number for the cursor in the new file (if non-zero).
- *
- * Return:
- * GETFILE_ERROR for "normal" error,
- * GETFILE_NOT_WRITTEN for "not written" error,
- * GETFILE_SAME_FILE for success
- * GETFILE_OPEN_OTHER for successfully opening another file.
- */
-int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, int forceit)
+// Try to abandon the current file and edit a new or existing file.
+// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg".
+// "lnum" is the line number for the cursor in the new file (if non-zero).
+//
+// Return:
+// GETFILE_ERROR for "normal" error,
+// GETFILE_NOT_WRITTEN for "not written" error,
+// GETFILE_SAME_FILE for success
+// GETFILE_OPEN_OTHER for successfully opening another file.
+int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm,
+ linenr_T lnum, int forceit)
{
+ char_u *ffname = ffname_arg;
+ char_u *sfname = sfname_arg;
int other;
int retval;
char_u *free_me = NULL;
@@ -2322,7 +2323,7 @@ int do_ecmd(
// Existing memfile.
oldbuf = true;
set_bufref(&bufref, buf);
- (void)buf_check_timestamp(buf, false);
+ (void)buf_check_timestamp(buf);
// Check if autocommands made buffer invalid or changed the current
// buffer.
if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) {
@@ -2705,7 +2706,7 @@ int do_ecmd(
if (topline == 0 && command == NULL) {
*so_ptr = 999; // force cursor to be vertically centered in the window
}
- update_topline();
+ update_topline(curwin);
curwin->w_scbind_pos = curwin->w_topline;
*so_ptr = n;
redraw_curbuf_later(NOT_VALID); // redraw this buffer later
@@ -2795,9 +2796,10 @@ void ex_append(exarg_T *eap)
p = vim_strchr(eap->nextcmd, NL);
if (p == NULL)
p = eap->nextcmd + STRLEN(eap->nextcmd);
- theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd));
- if (*p != NUL)
- ++p;
+ theline = vim_strnsave(eap->nextcmd, p - eap->nextcmd);
+ if (*p != NUL) {
+ p++;
+ }
eap->nextcmd = p;
} else {
// Set State to avoid the cursor shape to be set to INSERT mode
@@ -3704,15 +3706,16 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
+ len_change;
highlight_match = TRUE;
- update_topline();
+ update_topline(curwin);
validate_cursor();
update_screen(SOME_VALID);
highlight_match = false;
redraw_later(curwin, SOME_VALID);
curwin->w_p_fen = save_p_fen;
- if (msg_row == Rows - 1)
- msg_didout = FALSE; /* avoid a scroll-up */
+ if (msg_row == Rows - 1) {
+ msg_didout = false; // avoid a scroll-up
+ }
msg_starthere();
i = msg_scroll;
msg_scroll = 0; /* truncate msg when
@@ -3731,8 +3734,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
typed = plain_vgetc();
no_mapping--;
- /* clear the question */
- msg_didout = FALSE; /* don't scroll up */
+ // clear the question
+ msg_didout = false; // don't scroll up
msg_col = 0;
gotocmdline(TRUE);
@@ -3909,17 +3912,13 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
ADJUST_SUB_FIRSTLNUM();
- // TODO(bfredl): adjust also in preview, because decorations?
- // this has some robustness issues, will look into later.
- bool do_splice = !preview;
+ // TODO(bfredl): this has some robustness issues, look into later.
bcount_t replaced_bytes = 0;
lpos_T start = regmatch.startpos[0], end = regmatch.endpos[0];
- if (do_splice) {
- for (i = 0; i < nmatch-1; i++) {
- replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1;
- }
- replaced_bytes += end.col - start.col;
+ for (i = 0; i < nmatch-1; i++) {
+ replaced_bytes += STRLEN(ml_get(lnum_start+i)) + 1;
}
+ replaced_bytes += end.col - start.col;
// Now the trick is to replace CTRL-M chars with a real line
@@ -3964,14 +3963,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
current_match.end.col = new_endcol;
current_match.end.lnum = lnum;
- if (do_splice) {
- int matchcols = end.col - ((end.lnum == start.lnum)
- ? start.col : 0);
- int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0);
- extmark_splice(curbuf, lnum_start-1, start_col,
- end.lnum-start.lnum, matchcols, replaced_bytes,
- lnum-lnum_start, subcols, sublen-1, kExtmarkUndo);
- }
+ int matchcols = end.col - ((end.lnum == start.lnum)
+ ? start.col : 0);
+ int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0);
+ extmark_splice(curbuf, lnum_start-1, start_col,
+ end.lnum-start.lnum, matchcols, replaced_bytes,
+ lnum-lnum_start, subcols, sublen-1, kExtmarkUndo);
}
@@ -5254,8 +5251,10 @@ void ex_viusage(exarg_T *eap)
/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
/// French)
/// @param add_help_tags Whether to add the "help-tags" tag
-static void helptags_one(char_u *const dir, const char_u *const ext,
- const char_u *const tagfname, const bool add_help_tags)
+/// @param ignore_writeerr ignore write error
+static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname,
+ bool add_help_tags, bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
{
garray_T ga;
int filecount;
@@ -5299,7 +5298,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext,
FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
if (fd_tags == NULL) {
- EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
+ if (!ignore_writeerr) {
+ EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
+ }
FreeWild(filecount, files);
return;
}
@@ -5447,7 +5448,9 @@ static void helptags_one(char_u *const dir, const char_u *const ext,
}
/// Generate tags in one help directory, taking care of translations.
-static void do_helptags(char_u *dirname, bool add_help_tags)
+static void do_helptags(char_u *dirname, bool add_help_tags,
+ bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
{
int len;
garray_T ga;
@@ -5529,17 +5532,17 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
ext[1] = fname[5];
ext[2] = fname[6];
}
- helptags_one(dirname, ext, fname, add_help_tags);
+ helptags_one(dirname, ext, fname, add_help_tags, ignore_writeerr);
}
ga_clear(&ga);
FreeWild(filecount, files);
}
- static void
-helptags_cb(char_u *fname, void *cookie)
+static void helptags_cb(char_u *fname, void *cookie)
+ FUNC_ATTR_NONNULL_ALL
{
- do_helptags(fname, *(bool *)cookie);
+ do_helptags(fname, *(bool *)cookie, true);
}
/*
@@ -5568,7 +5571,7 @@ void ex_helptags(exarg_T *eap)
if (dirname == NULL || !os_isdir(dirname)) {
EMSG2(_("E150: Not a directory: %s"), eap->arg);
} else {
- do_helptags(dirname, add_help_tags);
+ do_helptags(dirname, add_help_tags, false);
}
xfree(dirname);
}
@@ -5739,7 +5742,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
redraw_later(curwin, SOME_VALID);
win_enter(save_curwin, false); // Return to original window
- update_topline();
+ update_topline(curwin);
// Update screen now.
int save_rd = RedrawingDisabled;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 3b9c44c3cd..bde584d27e 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -3392,7 +3392,7 @@ void ex_checktime(exarg_T *eap)
} else {
buf = buflist_findnr((int)eap->line2);
if (buf != NULL) { // cannot happen?
- (void)buf_check_timestamp(buf, false);
+ (void)buf_check_timestamp(buf);
}
}
no_check_timestamps = save_no_check_timestamps;
@@ -3790,6 +3790,14 @@ void ex_drop(exarg_T *eap)
if (wp->w_buffer == buf) {
goto_tabpage_win(tp, wp);
curwin->w_arg_idx = 0;
+ if (!bufIsChanged(curbuf)) {
+ const int save_ar = curbuf->b_p_ar;
+
+ // reload the file if it is newer
+ curbuf->b_p_ar = 1;
+ buf_check_timestamp(curbuf);
+ curbuf->b_p_ar = save_ar;
+ }
return;
}
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index a5eccc12b9..3fc02e0693 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2979,6 +2979,15 @@ const char * set_one_cmd_context(
const char *arg = (const char *)skipwhite((const char_u *)p);
+ // Skip over ++argopt argument
+ if ((ea.argt & ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
+ p = arg;
+ while (*p && !ascii_isspace(*p)) {
+ MB_PTR_ADV(p);
+ }
+ arg = (const char *)skipwhite((const char_u *)p);
+ }
+
if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
if (*arg == '>') { // Append.
if (*++arg == '>') {
@@ -4979,7 +4988,6 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
FUNC_ATTR_NONNULL_ARG(1, 3)
{
ucmd_T *cmd = NULL;
- char_u *p;
int i;
int cmp = 1;
char_u *rep_buf = NULL;
@@ -5039,7 +5047,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
if (cmp != 0) {
ga_grow(gap, 1);
- p = vim_strnsave(name, (int)name_len);
+ char_u *const p = vim_strnsave(name, name_len);
cmd = USER_CMD_GA(gap, i);
memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
@@ -6188,8 +6196,9 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp,
return FAIL;
}
- if (arg != NULL)
- *compl_arg = vim_strnsave(arg, (int)arglen);
+ if (arg != NULL) {
+ *compl_arg = vim_strnsave(arg, arglen);
+ }
return OK;
}
@@ -7362,7 +7371,7 @@ static void ex_syncbind(exarg_T *eap)
topline = curwin->w_topline;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_scb && wp->w_buffer) {
- y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value();
+ y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value(curwin);
if (topline > y) {
topline = y;
}
@@ -8050,7 +8059,7 @@ static void ex_redraw(exarg_T *eap)
RedrawingDisabled = 0;
p_lz = FALSE;
validate_cursor();
- update_topline();
+ update_topline(curwin);
if (eap->forceit) {
redraw_all_later(NOT_VALID);
}
@@ -8062,8 +8071,8 @@ static void ex_redraw(exarg_T *eap)
RedrawingDisabled = r;
p_lz = p;
- /* Reset msg_didout, so that a message that's there is overwritten. */
- msg_didout = FALSE;
+ // Reset msg_didout, so that a message that's there is overwritten.
+ msg_didout = false;
msg_col = 0;
/* No need to wait after an intentional redraw. */
@@ -8199,10 +8208,11 @@ static void ex_mark(exarg_T *eap)
*/
void update_topline_cursor(void)
{
- check_cursor(); /* put cursor on valid line */
- update_topline();
- if (!curwin->w_p_wrap)
+ check_cursor(); // put cursor on valid line
+ update_topline(curwin);
+ if (!curwin->w_p_wrap) {
validate_cursor();
+ }
update_curswant();
}
@@ -9282,7 +9292,7 @@ static void ex_match(exarg_T *eap)
} else {
p = skiptowhite(eap->arg);
if (!eap->skip) {
- g = vim_strnsave(eap->arg, (int)(p - eap->arg));
+ g = vim_strnsave(eap->arg, p - eap->arg);
}
p = skipwhite(p);
if (*p == NUL) {
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 0c7562980a..0917c6dd02 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -518,10 +518,13 @@ fail:
* Discard an exception. "was_finished" is set when the exception has been
* caught and the catch clause has been ended normally.
*/
-static void discard_exception(except_T *excp, int was_finished)
+static void discard_exception(except_T *excp, bool was_finished)
{
char_u *saved_IObuff;
+ if (current_exception == excp) {
+ current_exception = NULL;
+ }
if (excp == NULL) {
internal_error("discard_exception()");
return;
@@ -569,7 +572,6 @@ void discard_current_exception(void)
{
if (current_exception != NULL) {
discard_exception(current_exception, false);
- current_exception = NULL;
}
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
@@ -652,8 +654,8 @@ static void finish_exception(except_T *excp)
set_vim_var_string(VV_THROWPOINT, NULL, -1);
}
- /* Discard the exception, but use the finish message for 'verbose'. */
- discard_exception(excp, TRUE);
+ // Discard the exception, but use the finish message for 'verbose'.
+ discard_exception(excp, true);
}
/*
@@ -1812,11 +1814,12 @@ void leave_cleanup(cleanup_T *csp)
* made pending by it. Report this to the user if required by the
* 'verbose' option or when debugging. */
if (aborting() || need_rethrow) {
- if (pending & CSTP_THROW)
- /* Cancel the pending exception (includes report). */
- discard_exception(csp->exception, FALSE);
- else
+ if (pending & CSTP_THROW) {
+ // Cancel the pending exception (includes report).
+ discard_exception(csp->exception, false);
+ } else {
report_discard_pending(pending, NULL);
+ }
/* If an error was about to be converted to an exception when
* enter_cleanup() was called, free the message list. */
@@ -1916,15 +1919,13 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
default:
if (cstack->cs_flags[idx] & CSF_FINALLY) {
if (cstack->cs_pending[idx] & CSTP_THROW) {
- /* Cancel the pending exception. This is in the
- * finally clause, so that the stack of the
- * caught exceptions is not involved. */
- discard_exception((except_T *)
- cstack->cs_exception[idx],
- FALSE);
- } else
- report_discard_pending(cstack->cs_pending[idx],
- NULL);
+ // Cancel the pending exception. This is in the
+ // finally clause, so that the stack of the
+ // caught exceptions is not involved.
+ discard_exception((except_T *)cstack->cs_exception[idx], false);
+ } else {
+ report_discard_pending(cstack->cs_pending[idx], NULL);
+ }
cstack->cs_pending[idx] = CSTP_NONE;
}
break;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a0910f1394..0bc52f1e97 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -523,7 +523,7 @@ static void may_do_incsearch_highlighting(int firstc, long count,
// positioned in the same way as the actual search command
restore_viewstate(&s->old_viewstate);
changed_cline_bef_curs();
- update_topline();
+ update_topline(curwin);
if (found != 0) {
pos_T save_pos = curwin->w_cursor;
@@ -1082,6 +1082,7 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_DOWN && ccline.cmdpos > 0
&& ccline.cmdbuff[ccline.cmdpos - 1] == '.') {
s->c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
} else if (s->c == K_UP) {
// Hitting <Up>: Remove one submenu name in front of the
// cursor
@@ -1112,6 +1113,7 @@ static int command_line_execute(VimState *state, int key)
cmdline_del(i);
}
s->c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
s->xpc.xp_context = EXPAND_NOTHING;
}
}
@@ -1134,6 +1136,7 @@ static int command_line_execute(VimState *state, int key)
|| ccline.cmdbuff[ccline.cmdpos - 3] != '.')) {
// go down a directory
s->c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
} else if (STRNCMP(s->xpc.xp_pattern, upseg + 1, 3) == 0
&& s->c == K_DOWN) {
// If in a direct ancestor, strip off one ../ to go down
@@ -1154,6 +1157,7 @@ static int command_line_execute(VimState *state, int key)
&& (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) {
cmdline_del(j - 2);
s->c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
}
} else if (s->c == K_UP) {
// go up a directory
@@ -1546,7 +1550,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
set_search_match(&s->match_end);
curwin->w_cursor = s->match_start;
changed_cline_bef_curs();
- update_topline();
+ update_topline(curwin);
validate_cursor();
highlight_match = true;
save_viewstate(&s->old_viewstate);
@@ -2242,7 +2246,7 @@ static int command_line_changed(CommandLineState *s)
// Restore the window "view".
curwin->w_cursor = s->is_state.save_cursor;
restore_viewstate(&s->is_state.old_viewstate);
- update_topline();
+ update_topline(curwin);
redrawcmdline();
@@ -2251,7 +2255,9 @@ static int command_line_changed(CommandLineState *s)
close_preview_windows();
update_screen(SOME_VALID); // Clear 'inccommand' preview.
} else {
- may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
+ if (s->xpc.xp_context == EXPAND_NOTHING) {
+ may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
+ }
}
if (cmdmsg_rl || (p_arshape && !p_tbidi)) {
@@ -2740,8 +2746,8 @@ redraw:
no_mapping--;
- /* make following messages go to the next line */
- msg_didout = FALSE;
+ // make following messages go to the next line
+ msg_didout = false;
msg_col = 0;
if (msg_row < Rows - 1) {
msg_row++;
@@ -4229,17 +4235,11 @@ ExpandOne (
* Prepare an expand structure for use.
*/
void ExpandInit(expand_T *xp)
+ FUNC_ATTR_NONNULL_ALL
{
- xp->xp_pattern = NULL;
- xp->xp_pattern_len = 0;
+ CLEAR_POINTER(xp);
xp->xp_backslash = XP_BS_NONE;
-#ifndef BACKSLASH_IN_FILENAME
- xp->xp_shell = FALSE;
-#endif
xp->xp_numfiles = -1;
- xp->xp_files = NULL;
- xp->xp_arg = NULL;
- xp->xp_line = NULL;
}
/*
@@ -5399,7 +5399,7 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file,
}
/// Call "user_expand_func()" to invoke a user defined Vim script function and
-/// return the result (either a string or a List).
+/// return the result (either a string, a List or NULL).
static void * call_user_expand_func(user_expand_func_T user_expand_func,
expand_T *xp, int *num_file, char_u ***file)
FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 42a9ef08f9..d831ffc050 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -893,6 +893,9 @@ void ex_mkrc(exarg_T *eap)
&& (*flagp & SSOP_OPTIONS))) {
failed |= (makemap(fd, NULL) == FAIL
|| makeset(fd, OPT_GLOBAL, false) == FAIL);
+ if (p_hls && fprintf(fd, "%s", "set hlsearch\n") < 0) {
+ failed = true;
+ }
}
if (!failed && view_session) {
@@ -949,11 +952,16 @@ void ex_mkrc(exarg_T *eap)
}
if (fprintf(fd,
"%s",
- "let &g:so = s:so_save | let &g:siso = s:siso_save\n"
- "doautoall SessionLoadPost\n")
+ "let &g:so = s:so_save | let &g:siso = s:siso_save\n")
< 0) {
failed = true;
}
+ if (no_hlsearch && fprintf(fd, "%s", "nohlsearch\n") < 0) {
+ failed = true;
+ }
+ if (fprintf(fd, "%s", "doautoall SessionLoadPost\n") < 0) {
+ failed = true;
+ }
if (eap->cmdidx == CMD_mksession) {
if (fprintf(fd, "unlet SessionLoad\n") < 0) {
failed = true;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index f4dd90fad2..a542bb19dd 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1652,9 +1652,6 @@ failed:
# ifdef HAVE_ICONV
if (iconv_fd != (iconv_t)-1) {
iconv_close(iconv_fd);
-# ifndef __clang_analyzer__
- iconv_fd = (iconv_t)-1;
-# endif
}
# endif
@@ -2032,7 +2029,7 @@ static char_u *next_fenc(char_u **pp, bool *alloced)
r = enc_canonize(*pp);
*pp += STRLEN(*pp);
} else {
- r = vim_strnsave(*pp, (int)(p - *pp));
+ r = vim_strnsave(*pp, p - *pp);
*pp = p + 1;
p = enc_canonize(r);
xfree(r);
@@ -4217,7 +4214,9 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
&& (force
|| buf->b_sfname == NULL
|| path_is_absolute(buf->b_sfname))) {
- XFREE_CLEAR(buf->b_sfname);
+ if (buf->b_sfname != buf->b_ffname) {
+ XFREE_CLEAR(buf->b_sfname);
+ }
p = path_shorten_fname(buf->b_ffname, dirname);
if (p != NULL) {
buf->b_sfname = vim_strsave(p);
@@ -4673,7 +4672,6 @@ check_timestamps(
)
{
int didit = 0;
- int n;
/* Don't check timestamps while system() or another low-level function may
* cause us to lose and gain focus. */
@@ -4701,7 +4699,7 @@ check_timestamps(
if (buf->b_nwindows > 0) {
bufref_T bufref;
set_bufref(&bufref, buf);
- n = buf_check_timestamp(buf, focus);
+ const int n = buf_check_timestamp(buf);
if (didit < n) {
didit = n;
}
@@ -4771,11 +4769,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf)
* return 2 if a message has been displayed.
* return 0 otherwise.
*/
-int
-buf_check_timestamp(
- buf_T *buf,
- int focus /* called for GUI focus event */
-)
+int buf_check_timestamp(buf_T *buf)
FUNC_ATTR_NONNULL_ALL
{
int retval = 0;
@@ -5103,8 +5097,8 @@ void buf_reload(buf_T *buf, int orig_mode)
curwin->w_topline = old_topline;
curwin->w_cursor = old_cursor;
check_cursor();
- update_topline();
- keep_filetype = FALSE;
+ update_topline(curwin);
+ keep_filetype = false;
/* Update folds unless they are defined manually. */
FOR_ALL_TAB_WINDOWS(tp, wp) {
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 654aa6d5ba..0593c16999 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -902,8 +902,9 @@ int foldMoveTo(
bool last = false;
for (;; ) {
if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) {
- if (!updown)
+ if (!updown || gap->ga_len == 0) {
break;
+ }
/* When moving up, consider a fold above the cursor; when
* moving down consider a fold below the cursor. */
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index 38b51b5564..e0b0610646 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -213,6 +213,8 @@
# define FUNC_API_REMOTE_ONLY
/// API function not exposed in VimL/remote.
# define FUNC_API_LUA_ONLY
+/// API function checked textlock.
+# define FUNC_API_CHECK_TEXTLOCK
/// API function introduced at the given API level.
# define FUNC_API_SINCE(X)
/// API function deprecated since the given API level.
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index de098b7a7b..c9ab0cf709 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -43,6 +43,7 @@ local c_proto = Ct(
(fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) *
(fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) *
(fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1) *
+ (fill * Cg((P('FUNC_API_CHECK_TEXTLOCK') * Cc(true)), 'check_textlock') ^ -1) *
(fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) *
(fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) *
(fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) *
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index b31209e8ff..7e78b9e9d6 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -260,6 +260,13 @@ for i = 1, #functions do
args[#args + 1] = converted
end
+ if fn.check_textlock then
+ output:write('\n if (textlock != 0) {')
+ output:write('\n api_set_error(error, kErrorTypeException, "%s", e_secure);')
+ output:write('\n goto cleanup;')
+ output:write('\n }\n')
+ end
+
-- function call
local call_args = table.concat(args, ', ')
output:write('\n ')
@@ -393,6 +400,16 @@ local function process_function(fn)
}
]], fn.name))
end
+
+ if fn.check_textlock then
+ write_shifted_output(output, [[
+ if (textlock != 0) {
+ api_set_error(&err, kErrorTypeException, "%s", e_secure);
+ goto exit_0;
+ }
+ ]])
+ end
+
local cparams = ''
local free_code = {}
for j = #fn.parameters,1,-1 do
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 2e2993ed26..eddbdef739 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2490,8 +2490,10 @@ int inchar(
}
// Always flush the output characters when getting input characters
- // from the user.
- ui_flush();
+ // from the user and not just peeking.
+ if (wait_time == -1L || wait_time > 10L) {
+ ui_flush();
+ }
// Fill up to a third of the buffer, because each character may be
// tripled below.
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index c53c1546a4..6b962dc1f6 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -139,8 +139,9 @@ EXTERN int mod_mask INIT(= 0x0); // current key modifiers
EXTERN int cmdline_row;
EXTERN int redraw_cmdline INIT(= false); // cmdline must be redrawn
+EXTERN bool redraw_mode INIT(= false); // mode must be redrawn
EXTERN int clear_cmdline INIT(= false); // cmdline must be cleared
-EXTERN int mode_displayed INIT(= false); // mode is being displayed
+EXTERN bool mode_displayed INIT(= false); // mode is being displayed
EXTERN int cmdline_star INIT(= false); // cmdline is crypted
EXTERN int redrawing_cmdline INIT(= false); // cmdline is being redrawn
EXTERN int cmdline_was_last_drawn INIT(= false); // cmdline was last drawn
@@ -199,7 +200,7 @@ EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg
EXTERN int keep_msg_more INIT(= false); // keep_msg was set by msgmore()
EXTERN int need_fileinfo INIT(= false); // do fileinfo() after redraw
EXTERN int msg_scroll INIT(= false); // msg_start() will scroll
-EXTERN int msg_didout INIT(= false); // msg_outstr() was used in line
+EXTERN bool msg_didout INIT(= false); // msg_outstr() was used in line
EXTERN int msg_didany INIT(= false); // msg_outstr() was used at all
EXTERN int msg_nowait INIT(= false); // don't wait for this msg
EXTERN int emsg_off INIT(= 0); // don't display errors for now,
diff --git a/src/nvim/main.c b/src/nvim/main.c
index fd8264583b..e068b2361c 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -539,7 +539,7 @@ int main(int argc, char **argv)
// When a startup script or session file setup for diff'ing and
// scrollbind, sync the scrollbind now.
if (curwin->w_p_diff && curwin->w_p_scb) {
- update_topline();
+ update_topline(curwin);
check_scrollbind((linenr_T)0, 0L);
TIME_MSG("diff scrollbinding");
}
@@ -653,7 +653,18 @@ void getout(int exitval)
}
}
}
- apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf);
+
+ int unblock = 0;
+ // deathtrap() blocks autocommands, but we do want to trigger
+ // VimLeavePre.
+ if (is_autocmd_blocked()) {
+ unblock_autocmds();
+ unblock++;
+ }
+ apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, false, curbuf);
+ if (unblock) {
+ block_autocmds();
+ }
}
if (p_shada && *p_shada != NUL) {
@@ -662,7 +673,17 @@ void getout(int exitval)
}
if (v_dying <= 1) {
+ int unblock = 0;
+
+ // deathtrap() blocks autocommands, but we do want to trigger VimLeave.
+ if (is_autocmd_blocked()) {
+ unblock_autocmds();
+ unblock++;
+ }
apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, false, curbuf);
+ if (unblock) {
+ block_autocmds();
+ }
}
profile_dump();
@@ -1985,7 +2006,7 @@ static void version(void)
info_message = TRUE; // use mch_msg(), not mch_errmsg()
list_version();
msg_putchar('\n');
- msg_didout = FALSE;
+ msg_didout = false;
}
/// Prints help message for "nvim -h" or "nvim --help".
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 70225484ec..31dc6b3649 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -975,9 +975,9 @@ void ml_recover(bool checkext)
if (b0p->b0_flags & B0_HAS_FENC) {
int fnsize = B0_FNAME_SIZE_NOCRYPT;
- for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
- ;
- b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p));
+ for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; p--) {
+ }
+ b0_fenc = vim_strnsave(p, b0p->b0_fname + fnsize - p);
}
mf_put(mfp, hp, false, false); /* release block 0 */
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 02a7732f5c..530b930fed 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1128,11 +1128,11 @@ void wait_return(int redraw)
if (p_more) {
if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
|| c == K_UP || c == K_PAGEUP) {
- if (msg_scrolled > Rows)
- /* scroll back to show older messages */
+ if (msg_scrolled > Rows) {
+ // scroll back to show older messages
do_more_prompt(c);
- else {
- msg_didout = FALSE;
+ } else {
+ msg_didout = false;
c = K_IGNORE;
msg_col =
cmdmsg_rl ? Columns - 1 :
@@ -2097,15 +2097,17 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true);
}
- if (*s == '\n') { /* go to next line */
- msg_didout = FALSE; /* remember that line is empty */
- if (cmdmsg_rl)
+ if (*s == '\n') { // go to next line
+ msg_didout = false; // remember that line is empty
+ if (cmdmsg_rl) {
msg_col = Columns - 1;
- else
+ } else {
msg_col = 0;
- if (++msg_row >= Rows) /* safety check */
+ }
+ if (++msg_row >= Rows) { // safety check
msg_row = Rows - 1;
- } else if (*s == '\r') { /* go to column 0 */
+ }
+ } else if (*s == '\r') { // go to column 0
msg_col = 0;
} else if (*s == '\b') { /* go to previous char */
if (msg_col)
@@ -2878,10 +2880,10 @@ void repeat_message(void)
ui_cursor_goto(msg_row, msg_col); /* put cursor back */
} else if (State == HITRETURN || State == SETWSIZE) {
if (msg_row == Rows - 1) {
- /* Avoid drawing the "hit-enter" prompt below the previous one,
- * overwrite it. Esp. useful when regaining focus and a
- * FocusGained autocmd exists but didn't draw anything. */
- msg_didout = FALSE;
+ // Avoid drawing the "hit-enter" prompt below the previous one,
+ // overwrite it. Esp. useful when regaining focus and a
+ // FocusGained autocmd exists but didn't draw anything.
+ msg_didout = false;
msg_col = 0;
msg_clr_eos();
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index fdcf6bb189..a6afdc27d9 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -129,98 +129,99 @@ void redraw_for_cursorline(win_T *wp)
*/
void update_topline_redraw(void)
{
- update_topline();
- if (must_redraw)
+ update_topline(curwin);
+ if (must_redraw) {
update_screen(0);
+ }
}
/*
* Update curwin->w_topline to move the cursor onto the screen.
*/
-void update_topline(void)
+void update_topline(win_T *wp)
{
linenr_T old_topline;
int old_topfill;
bool check_topline = false;
bool check_botline = false;
- long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
+ long *so_ptr = wp->w_p_so >= 0 ? &wp->w_p_so : &p_so;
long save_so = *so_ptr;
// If there is no valid screen and when the window height is zero just use
// the cursor line.
- if (!default_grid.chars || curwin->w_height_inner == 0) {
- curwin->w_topline = curwin->w_cursor.lnum;
- curwin->w_botline = curwin->w_topline;
- curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
- curwin->w_viewport_invalid = true;
- curwin->w_scbind_pos = 1;
+ if (!default_grid.chars || wp->w_height_inner == 0) {
+ wp->w_topline = wp->w_cursor.lnum;
+ wp->w_botline = wp->w_topline;
+ wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
+ wp->w_viewport_invalid = true;
+ wp->w_scbind_pos = 1;
return;
}
- check_cursor_moved(curwin);
- if (curwin->w_valid & VALID_TOPLINE)
+ check_cursor_moved(wp);
+ if (wp->w_valid & VALID_TOPLINE) {
return;
+ }
// When dragging with the mouse, don't scroll that quickly
if (mouse_dragging > 0) {
*so_ptr = mouse_dragging - 1;
}
- old_topline = curwin->w_topline;
- old_topfill = curwin->w_topfill;
+ old_topline = wp->w_topline;
+ old_topfill = wp->w_topfill;
// If the buffer is empty, always set topline to 1.
if (BUFEMPTY()) { // special case - file is empty
- if (curwin->w_topline != 1) {
- redraw_later(curwin, NOT_VALID);
+ if (wp->w_topline != 1) {
+ redraw_later(wp, NOT_VALID);
}
- curwin->w_topline = 1;
- curwin->w_botline = 2;
- curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
- curwin->w_viewport_invalid = true;
- curwin->w_scbind_pos = 1;
- }
- /*
- * If the cursor is above or near the top of the window, scroll the window
- * to show the line the cursor is in, with 'scrolloff' context.
- */
- else {
- if (curwin->w_topline > 1) {
- /* If the cursor is above topline, scrolling is always needed.
- * If the cursor is far below topline and there is no folding,
- * scrolling down is never needed. */
- if (curwin->w_cursor.lnum < curwin->w_topline)
+ wp->w_topline = 1;
+ wp->w_botline = 2;
+ wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
+ wp->w_viewport_invalid = true;
+ wp->w_scbind_pos = 1;
+ } else {
+ // If the cursor is above or near the top of the window, scroll the window
+ // to show the line the cursor is in, with 'scrolloff' context.
+ if (wp->w_topline > 1) {
+ // If the cursor is above topline, scrolling is always needed.
+ // If the cursor is far below topline and there is no folding,
+ // scrolling down is never needed.
+ if (wp->w_cursor.lnum < wp->w_topline) {
check_topline = true;
- else if (check_top_offset())
+ } else if (check_top_offset()) {
check_topline = true;
+ }
}
- /* Check if there are more filler lines than allowed. */
- if (!check_topline && curwin->w_topfill > diff_check_fill(curwin,
- curwin->w_topline))
+ // Check if there are more filler lines than allowed.
+ if (!check_topline && wp->w_topfill > diff_check_fill(wp, wp->w_topline)) {
check_topline = true;
+ }
if (check_topline) {
- int halfheight = curwin->w_height_inner / 2 - 1;
+ int halfheight = wp->w_height_inner / 2 - 1;
if (halfheight < 2) {
halfheight = 2;
}
long n;
- if (hasAnyFolding(curwin)) {
- /* Count the number of logical lines between the cursor and
- * topline + p_so (approximation of how much will be
- * scrolled). */
+ if (hasAnyFolding(wp)) {
+ // Count the number of logical lines between the cursor and
+ // topline + p_so (approximation of how much will be
+ // scrolled).
n = 0;
- for (linenr_T lnum = curwin->w_cursor.lnum;
- lnum < curwin->w_topline + *so_ptr; lnum++) {
+ for (linenr_T lnum = wp->w_cursor.lnum;
+ lnum < wp->w_topline + *so_ptr; lnum++) {
n++;
// stop at end of file or when we know we are far off
- if (lnum >= curbuf->b_ml.ml_line_count || n >= halfheight) {
+ assert(wp->w_buffer != 0);
+ if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) {
break;
}
- (void)hasFolding(lnum, NULL, &lnum);
+ (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL);
}
} else {
- n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum;
+ n = wp->w_topline + *so_ptr - wp->w_cursor.lnum;
}
/* If we weren't very close to begin with, we scroll to put the
@@ -233,8 +234,8 @@ void update_topline(void)
check_botline = true;
}
} else {
- /* Make sure topline is the first line of a fold. */
- (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
+ // Make sure topline is the first line of a fold.
+ (void)hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL);
check_botline = true;
}
}
@@ -248,35 +249,36 @@ void update_topline(void)
* for every small change.
*/
if (check_botline) {
- if (!(curwin->w_valid & VALID_BOTLINE_AP))
- validate_botline();
-
- if (curwin->w_botline <= curbuf->b_ml.ml_line_count) {
- if (curwin->w_cursor.lnum < curwin->w_botline) {
- if (((long)curwin->w_cursor.lnum
- >= (long)curwin->w_botline - *so_ptr
- || hasAnyFolding(curwin)
- )) {
+ if (!(wp->w_valid & VALID_BOTLINE_AP)) {
+ validate_botline(wp);
+ }
+
+ assert(wp->w_buffer != 0);
+ if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
+ if (wp->w_cursor.lnum < wp->w_botline) {
+ if (((long)wp->w_cursor.lnum
+ >= (long)wp->w_botline - *so_ptr
+ || hasAnyFolding(wp))) {
lineoff_T loff;
/* Cursor is (a few lines) above botline, check if there are
* 'scrolloff' window lines below the cursor. If not, need to
* scroll. */
- int n = curwin->w_empty_rows;
- loff.lnum = curwin->w_cursor.lnum;
- /* In a fold go to its last line. */
- (void)hasFolding(loff.lnum, NULL, &loff.lnum);
+ int n = wp->w_empty_rows;
+ loff.lnum = wp->w_cursor.lnum;
+ // In a fold go to its last line.
+ (void)hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL);
loff.fill = 0;
- n += curwin->w_filler_rows;
+ n += wp->w_filler_rows;
loff.height = 0;
- while (loff.lnum < curwin->w_botline
- && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0)
+ while (loff.lnum < wp->w_botline
+ && (loff.lnum + 1 < wp->w_botline || loff.fill == 0)
) {
n += loff.height;
if (n >= *so_ptr) {
break;
}
- botline_forw(&loff);
+ botline_forw(wp, &loff);
}
if (n >= *so_ptr) {
// sufficient context, no need to scroll
@@ -289,23 +291,23 @@ void update_topline(void)
}
if (check_botline) {
long line_count = 0;
- if (hasAnyFolding(curwin)) {
- /* Count the number of logical lines between the cursor and
- * botline - p_so (approximation of how much will be
- * scrolled). */
- for (linenr_T lnum = curwin->w_cursor.lnum;
- lnum >= curwin->w_botline - *so_ptr; lnum--) {
+ if (hasAnyFolding(wp)) {
+ // Count the number of logical lines between the cursor and
+ // botline - p_so (approximation of how much will be
+ // scrolled).
+ for (linenr_T lnum = wp->w_cursor.lnum;
+ lnum >= wp->w_botline - *so_ptr; lnum--) {
line_count++;
// stop at end of file or when we know we are far off
- if (lnum <= 0 || line_count > curwin->w_height_inner + 1) {
+ if (lnum <= 0 || line_count > wp->w_height_inner + 1) {
break;
}
(void)hasFolding(lnum, &lnum, NULL);
}
- } else
- line_count = curwin->w_cursor.lnum - curwin->w_botline
- + 1 + *so_ptr;
- if (line_count <= curwin->w_height_inner + 1) {
+ } else {
+ line_count = wp->w_cursor.lnum - wp->w_botline + 1 + *so_ptr;
+ }
+ if (line_count <= wp->w_height_inner + 1) {
scroll_cursor_bot(scrolljump_value(), false);
} else {
scroll_cursor_halfway(false);
@@ -313,25 +315,25 @@ void update_topline(void)
}
}
}
- curwin->w_valid |= VALID_TOPLINE;
- curwin->w_viewport_invalid = true;
- win_check_anchored_floats(curwin);
+ wp->w_valid |= VALID_TOPLINE;
+ wp->w_viewport_invalid = true;
+ win_check_anchored_floats(wp);
/*
* Need to redraw when topline changed.
*/
- if (curwin->w_topline != old_topline
- || curwin->w_topfill != old_topfill
+ if (wp->w_topline != old_topline
+ || wp->w_topfill != old_topfill
) {
dollar_vcol = -1;
- if (curwin->w_skipcol != 0) {
- curwin->w_skipcol = 0;
- redraw_later(curwin, NOT_VALID);
+ if (wp->w_skipcol != 0) {
+ wp->w_skipcol = 0;
+ redraw_later(wp, NOT_VALID);
} else {
- redraw_later(curwin, VALID);
+ redraw_later(wp, VALID);
}
// May need to set w_skipcol when cursor in w_topline.
- if (curwin->w_cursor.lnum == curwin->w_topline) {
+ if (wp->w_cursor.lnum == wp->w_topline) {
validate_cursor();
}
}
@@ -346,7 +348,7 @@ void update_topline_win(win_T* win)
{
win_T *save_curwin;
switch_win(&save_curwin, NULL, win, NULL, true);
- update_topline();
+ update_topline(curwin);
restore_win(save_curwin, NULL, true);
}
@@ -368,7 +370,7 @@ static int scrolljump_value(void)
*/
static bool check_top_offset(void)
{
- long so = get_scrolloff_value();
+ long so = get_scrolloff_value(curwin);
if (curwin->w_cursor.lnum < curwin->w_topline + so
|| hasAnyFolding(curwin)
) {
@@ -378,7 +380,7 @@ static bool check_top_offset(void)
int n = curwin->w_topfill; // always have this context
// Count the visible screen lines above the cursor line.
while (n < so) {
- topline_back(&loff);
+ topline_back(curwin, &loff);
// Stop when included a line above the window.
if (loff.lnum < curwin->w_topline
|| (loff.lnum == curwin->w_topline && loff.fill > 0)
@@ -498,10 +500,11 @@ void changed_line_abv_curs_win(win_T *wp)
/*
* Make sure the value of curwin->w_botline is valid.
*/
-void validate_botline(void)
+void validate_botline(win_T *wp)
{
- if (!(curwin->w_valid & VALID_BOTLINE))
- comp_botline(curwin);
+ if (!(wp->w_valid & VALID_BOTLINE)) {
+ comp_botline(wp);
+ }
}
/*
@@ -539,8 +542,9 @@ int cursor_valid(void)
void validate_cursor(void)
{
check_cursor_moved(curwin);
- if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
- curs_columns(true);
+ if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) {
+ curs_columns(curwin, true);
+ }
}
/*
@@ -720,8 +724,10 @@ int curwin_col_off2(void)
// Compute curwin->w_wcol and curwin->w_virtcol.
// Also updates curwin->w_wrow and curwin->w_cline_row.
// Also updates curwin->w_leftcol.
+// @param may_scroll when true, may scroll horizontally
void curs_columns(
- int may_scroll /* when true, may scroll horizontally */
+ win_T *wp,
+ int may_scroll
)
{
int n;
@@ -729,136 +735,136 @@ void curs_columns(
colnr_T startcol;
colnr_T endcol;
colnr_T prev_skipcol;
- long so = get_scrolloff_value();
- long siso = get_sidescrolloff_value();
+ long so = get_scrolloff_value(wp);
+ long siso = get_sidescrolloff_value(wp);
/*
* First make sure that w_topline is valid (after moving the cursor).
*/
- update_topline();
+ update_topline(wp);
// Next make sure that w_cline_row is valid.
- if (!(curwin->w_valid & VALID_CROW)) {
- curs_rows(curwin);
+ if (!(wp->w_valid & VALID_CROW)) {
+ curs_rows(wp);
}
/*
* Compute the number of virtual columns.
*/
- if (curwin->w_cline_folded)
- /* In a folded line the cursor is always in the first column */
- startcol = curwin->w_virtcol = endcol = curwin->w_leftcol;
- else
- getvvcol(curwin, &curwin->w_cursor,
- &startcol, &(curwin->w_virtcol), &endcol);
+ if (wp->w_cline_folded) {
+ // In a folded line the cursor is always in the first column
+ startcol = wp->w_virtcol = endcol = wp->w_leftcol;
+ } else {
+ getvvcol(wp, &wp->w_cursor, &startcol, &(wp->w_virtcol), &endcol);
+ }
/* remove '$' from change command when cursor moves onto it */
if (startcol > dollar_vcol)
dollar_vcol = -1;
- int extra = curwin_col_off();
- curwin->w_wcol = curwin->w_virtcol + extra;
+ int extra = win_col_off(wp);
+ wp->w_wcol = wp->w_virtcol + extra;
endcol += extra;
- /*
- * Now compute w_wrow, counting screen lines from w_cline_row.
- */
- curwin->w_wrow = curwin->w_cline_row;
+ // Now compute w_wrow, counting screen lines from w_cline_row.
+ wp->w_wrow = wp->w_cline_row;
- int textwidth = curwin->w_width_inner - extra;
+ int textwidth = wp->w_width_inner - extra;
if (textwidth <= 0) {
// No room for text, put cursor in last char of window.
- curwin->w_wcol = curwin->w_width_inner - 1;
- curwin->w_wrow = curwin->w_height_inner - 1;
- } else if (curwin->w_p_wrap
- && curwin->w_width_inner != 0
+ wp->w_wcol = wp->w_width_inner - 1;
+ wp->w_wrow = wp->w_height_inner - 1;
+ } else if (wp->w_p_wrap
+ && wp->w_width_inner != 0
) {
- width = textwidth + curwin_col_off2();
+ width = textwidth + win_col_off2(wp);
- // long line wrapping, adjust curwin->w_wrow
- if (curwin->w_wcol >= curwin->w_width_inner) {
+ // long line wrapping, adjust wp->w_wrow
+ if (wp->w_wcol >= wp->w_width_inner) {
// this same formula is used in validate_cursor_col()
- n = (curwin->w_wcol - curwin->w_width_inner) / width + 1;
- curwin->w_wcol -= n * width;
- curwin->w_wrow += n;
+ n = (wp->w_wcol - wp->w_width_inner) / width + 1;
+ wp->w_wcol -= n * width;
+ wp->w_wrow += n;
/* When cursor wraps to first char of next line in Insert
* mode, the 'showbreak' string isn't shown, backup to first
* column */
if (*p_sbr && *get_cursor_pos_ptr() == NUL
- && curwin->w_wcol == (int)vim_strsize(p_sbr))
- curwin->w_wcol = 0;
+ && wp->w_wcol == (int)vim_strsize(p_sbr)) {
+ wp->w_wcol = 0;
+ }
}
- }
- /* No line wrapping: compute curwin->w_leftcol if scrolling is on and line
- * is not folded.
- * If scrolling is off, curwin->w_leftcol is assumed to be 0 */
- else if (may_scroll
- && !curwin->w_cline_folded
- ) {
- /*
- * If Cursor is left of the screen, scroll rightwards.
- * If Cursor is right of the screen, scroll leftwards
- * If we get closer to the edge than 'sidescrolloff', scroll a little
- * extra
- */
+ } else if (may_scroll
+ && !wp->w_cline_folded
+ ) {
+ // No line wrapping: compute wp->w_leftcol if scrolling is on and line
+ // is not folded.
+ // If scrolling is off, wp->w_leftcol is assumed to be 0
+
+ // If Cursor is left of the screen, scroll rightwards.
+ // If Cursor is right of the screen, scroll leftwards
+ // If we get closer to the edge than 'sidescrolloff', scroll a little
+ // extra
assert(siso <= INT_MAX);
- int off_left = startcol - curwin->w_leftcol - (int)siso;
+ int off_left = startcol - wp->w_leftcol - (int)siso;
int off_right =
- endcol - curwin->w_leftcol - curwin->w_width_inner + (int)siso + 1;
+ endcol - wp->w_leftcol - wp->w_width_inner + (int)siso + 1;
if (off_left < 0 || off_right > 0) {
int diff = (off_left < 0) ? -off_left: off_right;
/* When far off or not enough room on either side, put cursor in
* middle of window. */
int new_leftcol;
- if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left)
- new_leftcol = curwin->w_wcol - extra - textwidth / 2;
- else {
+ if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) {
+ new_leftcol = wp->w_wcol - extra - textwidth / 2;
+ } else {
if (diff < p_ss) {
assert(p_ss <= INT_MAX);
diff = (int)p_ss;
}
- if (off_left < 0)
- new_leftcol = curwin->w_leftcol - diff;
- else
- new_leftcol = curwin->w_leftcol + diff;
+ if (off_left < 0) {
+ new_leftcol = wp->w_leftcol - diff;
+ } else {
+ new_leftcol = wp->w_leftcol + diff;
+ }
}
if (new_leftcol < 0)
new_leftcol = 0;
- if (new_leftcol != (int)curwin->w_leftcol) {
- curwin->w_leftcol = new_leftcol;
- win_check_anchored_floats(curwin);
- // screen has to be redrawn with new curwin->w_leftcol
- redraw_later(curwin, NOT_VALID);
+ if (new_leftcol != (int)wp->w_leftcol) {
+ wp->w_leftcol = new_leftcol;
+ win_check_anchored_floats(wp);
+ // screen has to be redrawn with new wp->w_leftcol
+ redraw_later(wp, NOT_VALID);
}
}
- curwin->w_wcol -= curwin->w_leftcol;
- } else if (curwin->w_wcol > (int)curwin->w_leftcol)
- curwin->w_wcol -= curwin->w_leftcol;
- else
- curwin->w_wcol = 0;
+ wp->w_wcol -= wp->w_leftcol;
+ } else if (wp->w_wcol > (int)wp->w_leftcol) {
+ wp->w_wcol -= wp->w_leftcol;
+ } else {
+ wp->w_wcol = 0;
+ }
/* Skip over filler lines. At the top use w_topfill, there
* may be some filler lines above the window. */
- if (curwin->w_cursor.lnum == curwin->w_topline)
- curwin->w_wrow += curwin->w_topfill;
- else
- curwin->w_wrow += diff_check_fill(curwin, curwin->w_cursor.lnum);
+ if (wp->w_cursor.lnum == wp->w_topline) {
+ wp->w_wrow += wp->w_topfill;
+ } else {
+ wp->w_wrow += diff_check_fill(wp, wp->w_cursor.lnum);
+ }
- prev_skipcol = curwin->w_skipcol;
+ prev_skipcol = wp->w_skipcol;
int plines = 0;
- if ((curwin->w_wrow >= curwin->w_height_inner
+ if ((wp->w_wrow >= wp->w_height_inner
|| ((prev_skipcol > 0
- || curwin->w_wrow + so >= curwin->w_height_inner)
+ || wp->w_wrow + so >= wp->w_height_inner)
&& (plines =
- plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1
- >= curwin->w_height_inner))
- && curwin->w_height_inner != 0
- && curwin->w_cursor.lnum == curwin->w_topline
+ plines_win_nofill(wp, wp->w_cursor.lnum, false)) - 1
+ >= wp->w_height_inner))
+ && wp->w_height_inner != 0
+ && wp->w_cursor.lnum == wp->w_topline
&& width > 0
- && curwin->w_width_inner != 0
+ && wp->w_width_inner != 0
) {
/* Cursor past end of screen. Happens with a single line that does
* not fit on screen. Find a skipcol to show the text around the
@@ -867,87 +873,88 @@ void curs_columns(
* 2: Less than "p_so" lines below
* 3: both of them */
extra = 0;
- if (curwin->w_skipcol + so * width > curwin->w_virtcol) {
+ if (wp->w_skipcol + so * width > wp->w_virtcol) {
extra = 1;
}
// Compute last display line of the buffer line that we want at the
// bottom of the window.
if (plines == 0) {
- plines = plines_win(curwin, curwin->w_cursor.lnum, false);
+ plines = plines_win(wp, wp->w_cursor.lnum, false);
}
plines--;
- if (plines > curwin->w_wrow + so) {
+ if (plines > wp->w_wrow + so) {
assert(so <= INT_MAX);
- n = curwin->w_wrow + (int)so;
+ n = wp->w_wrow + (int)so;
} else {
n = plines;
}
- if ((colnr_T)n >= curwin->w_height_inner + curwin->w_skipcol / width) {
+ if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width) {
extra += 2;
}
if (extra == 3 || plines < so * 2) {
// not enough room for 'scrolloff', put cursor in the middle
- n = curwin->w_virtcol / width;
- if (n > curwin->w_height_inner / 2) {
- n -= curwin->w_height_inner / 2;
+ n = wp->w_virtcol / width;
+ if (n > wp->w_height_inner / 2) {
+ n -= wp->w_height_inner / 2;
} else {
n = 0;
}
// don't skip more than necessary
- if (n > plines - curwin->w_height_inner + 1) {
- n = plines - curwin->w_height_inner + 1;
+ if (n > plines - wp->w_height_inner + 1) {
+ n = plines - wp->w_height_inner + 1;
}
- curwin->w_skipcol = n * width;
+ wp->w_skipcol = n * width;
} else if (extra == 1) {
// less then 'scrolloff' lines above, decrease skipcol
assert(so <= INT_MAX);
- extra = (curwin->w_skipcol + (int)so * width - curwin->w_virtcol
+ extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol
+ width - 1) / width;
if (extra > 0) {
- if ((colnr_T)(extra * width) > curwin->w_skipcol)
- extra = curwin->w_skipcol / width;
- curwin->w_skipcol -= extra * width;
+ if ((colnr_T)(extra * width) > wp->w_skipcol) {
+ extra = wp->w_skipcol / width;
+ }
+ wp->w_skipcol -= extra * width;
}
} else if (extra == 2) {
// less then 'scrolloff' lines below, increase skipcol
- endcol = (n - curwin->w_height_inner + 1) * width;
- while (endcol > curwin->w_virtcol) {
+ endcol = (n - wp->w_height_inner + 1) * width;
+ while (endcol > wp->w_virtcol) {
endcol -= width;
}
- if (endcol > curwin->w_skipcol) {
- curwin->w_skipcol = endcol;
+ if (endcol > wp->w_skipcol) {
+ wp->w_skipcol = endcol;
}
}
- curwin->w_wrow -= curwin->w_skipcol / width;
- if (curwin->w_wrow >= curwin->w_height_inner) {
+ wp->w_wrow -= wp->w_skipcol / width;
+ if (wp->w_wrow >= wp->w_height_inner) {
// small window, make sure cursor is in it
- extra = curwin->w_wrow - curwin->w_height_inner + 1;
- curwin->w_skipcol += extra * width;
- curwin->w_wrow -= extra;
+ extra = wp->w_wrow - wp->w_height_inner + 1;
+ wp->w_skipcol += extra * width;
+ wp->w_wrow -= extra;
}
// extra could be either positive or negative
- extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
- win_scroll_lines(curwin, 0, extra);
+ extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width;
+ win_scroll_lines(wp, 0, extra);
} else {
- curwin->w_skipcol = 0;
+ wp->w_skipcol = 0;
}
- if (prev_skipcol != curwin->w_skipcol) {
- redraw_later(curwin, NOT_VALID);
+ if (prev_skipcol != wp->w_skipcol) {
+ redraw_later(wp, NOT_VALID);
}
- /* Redraw when w_virtcol changes and 'cursorcolumn' is set */
- if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0
+ // Redraw when w_virtcol changes and 'cursorcolumn' is set
+ if (wp->w_p_cuc && (wp->w_valid & VALID_VIRTCOL) == 0
&& !pum_visible()) {
- redraw_later(curwin, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
// now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
- curwin->w_valid_leftcol = curwin->w_leftcol;
+ wp->w_valid_leftcol = wp->w_leftcol;
- curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
+ wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
}
/// Compute the screen position of text character at "pos" in window "wp"
@@ -1231,7 +1238,7 @@ void scrolldown_clamp(void)
end_row += curwin->w_cline_height - 1 -
curwin->w_virtcol / curwin->w_width_inner;
}
- if (end_row < curwin->w_height_inner - get_scrolloff_value()) {
+ if (end_row < curwin->w_height_inner - get_scrolloff_value(curwin)) {
if (can_fill) {
++curwin->w_topfill;
check_topfill(curwin, true);
@@ -1271,7 +1278,7 @@ void scrollup_clamp(void)
validate_virtcol();
start_row -= curwin->w_virtcol / curwin->w_width_inner;
}
- if (start_row >= get_scrolloff_value()) {
+ if (start_row >= get_scrolloff_value(curwin)) {
if (curwin->w_topfill > 0) {
curwin->w_topfill--;
} else {
@@ -1289,22 +1296,22 @@ void scrollup_clamp(void)
* Returns the height of the added line in "lp->height".
* Lines above the first one are incredibly high: MAXCOL.
*/
-static void topline_back(lineoff_T *lp)
+static void topline_back(win_T *wp, lineoff_T *lp)
{
- if (lp->fill < diff_check_fill(curwin, lp->lnum)) {
- /* Add a filler line. */
- ++lp->fill;
+ if (lp->fill < diff_check_fill(wp, lp->lnum)) {
+ // Add a filler line
+ lp->fill++;
lp->height = 1;
} else {
--lp->lnum;
lp->fill = 0;
- if (lp->lnum < 1)
+ if (lp->lnum < 1) {
lp->height = MAXCOL;
- else if (hasFolding(lp->lnum, &lp->lnum, NULL))
- /* Add a closed fold */
+ } else if (hasFolding(lp->lnum, &lp->lnum, NULL)) {
+ // Add a closed fold
lp->height = 1;
- else {
- lp->height = plines_nofill(lp->lnum);
+ } else {
+ lp->height = plines_win_nofill(wp, lp->lnum, true);
}
}
}
@@ -1315,22 +1322,23 @@ static void topline_back(lineoff_T *lp)
* Returns the height of the added line in "lp->height".
* Lines below the last one are incredibly high.
*/
-static void botline_forw(lineoff_T *lp)
+static void botline_forw(win_T *wp, lineoff_T *lp)
{
- if (lp->fill < diff_check_fill(curwin, lp->lnum + 1)) {
- /* Add a filler line. */
- ++lp->fill;
+ if (lp->fill < diff_check_fill(wp, lp->lnum + 1)) {
+ // Add a filler line.
+ lp->fill++;
lp->height = 1;
} else {
++lp->lnum;
lp->fill = 0;
- if (lp->lnum > curbuf->b_ml.ml_line_count) {
+ assert(wp->w_buffer != 0);
+ if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) {
lp->height = MAXCOL;
- } else if (hasFolding(lp->lnum, NULL, &lp->lnum)) {
+ } else if (hasFoldingWin(wp, lp->lnum, NULL, &lp->lnum, true, NULL)) {
// Add a closed fold
lp->height = 1;
} else {
- lp->height = plines_nofill(lp->lnum);
+ lp->height = plines_win_nofill(wp, lp->lnum, true);
}
}
}
@@ -1374,7 +1382,7 @@ void scroll_cursor_top(int min_scroll, int always)
linenr_T old_topline = curwin->w_topline;
linenr_T old_topfill = curwin->w_topfill;
linenr_T new_topline;
- int off = (int)get_scrolloff_value();
+ int off = (int)get_scrolloff_value(curwin);
if (mouse_dragging > 0)
off = mouse_dragging - 1;
@@ -1518,7 +1526,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
int old_valid = curwin->w_valid;
int old_empty_rows = curwin->w_empty_rows;
linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number
- long so = get_scrolloff_value();
+ long so = get_scrolloff_value(curwin);
if (set_topbot) {
used = 0;
@@ -1528,7 +1536,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_topline > 1;
curwin->w_topline = loff.lnum) {
loff.lnum = curwin->w_topline;
- topline_back(&loff);
+ topline_back(curwin, &loff);
if (loff.height == MAXCOL
|| used + loff.height > curwin->w_height_inner) {
break;
@@ -1542,8 +1550,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
|| curwin->w_topfill != old_topfill
)
curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
- } else
- validate_botline();
+ } else {
+ validate_botline(curwin);
+ }
/* The lines of the cursor line itself are always used. */
used = plines_nofill(cln);
@@ -1586,8 +1595,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
break;
}
- /* Add one line above */
- topline_back(&loff);
+ // Add one line above
+ topline_back(curwin, &loff);
if (loff.height == MAXCOL) {
used = MAXCOL;
} else {
@@ -1609,8 +1618,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
}
if (boff.lnum < curbuf->b_ml.ml_line_count) {
- /* Add one line below */
- botline_forw(&boff);
+ // Add one line below
+ botline_forw(curwin, &boff);
used += boff.height;
if (used > curwin->w_height_inner) {
break;
@@ -1647,7 +1656,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
boff.lnum = curwin->w_topline - 1;
int i;
for (i = 0; i < scrolled && boff.lnum < curwin->w_botline; ) {
- botline_forw(&boff);
+ botline_forw(curwin, &boff);
i += boff.height;
++line_count;
}
@@ -1701,7 +1710,7 @@ void scroll_cursor_halfway(int atend)
while (topline > 1) {
if (below <= above) { /* add a line below the cursor first */
if (boff.lnum < curbuf->b_ml.ml_line_count) {
- botline_forw(&boff);
+ botline_forw(curwin, &boff);
used += boff.height;
if (used > curwin->w_height_inner) {
break;
@@ -1714,12 +1723,13 @@ void scroll_cursor_halfway(int atend)
}
}
- if (below > above) { /* add a line above the cursor */
- topline_back(&loff);
- if (loff.height == MAXCOL)
+ if (below > above) { // add a line above the cursor
+ topline_back(curwin, &loff);
+ if (loff.height == MAXCOL) {
used = MAXCOL;
- else
+ } else {
used += loff.height;
+ }
if (used > curwin->w_height_inner) {
break;
}
@@ -1751,8 +1761,8 @@ void cursor_correct(void)
* How many lines we would like to have above/below the cursor depends on
* whether the first/last line of the file is on screen.
*/
- int above_wanted = (int)get_scrolloff_value();
- int below_wanted = (int)get_scrolloff_value();
+ int above_wanted = (int)get_scrolloff_value(curwin);
+ int below_wanted = (int)get_scrolloff_value(curwin);
if (mouse_dragging > 0) {
above_wanted = mouse_dragging - 1;
below_wanted = mouse_dragging - 1;
@@ -1764,7 +1774,7 @@ void cursor_correct(void)
below_wanted = max_off;
}
}
- validate_botline();
+ validate_botline(curwin);
if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1
&& mouse_dragging == 0) {
below_wanted = 0;
@@ -1848,21 +1858,19 @@ int onepage(Direction dir, long count)
int retval = OK;
lineoff_T loff;
linenr_T old_topline = curwin->w_topline;
- long so = get_scrolloff_value();
+ long so = get_scrolloff_value(curwin);
if (curbuf->b_ml.ml_line_count == 1) { /* nothing to do */
beep_flush();
return FAIL;
}
- for (; count > 0; --count) {
- validate_botline();
- /*
- * It's an error to move a page up when the first line is already on
- * the screen. It's an error to move a page down when the last line
- * is on the screen and the topline is 'scrolloff' lines from the
- * last line.
- */
+ for (; count > 0; count--) {
+ validate_botline(curwin);
+ // It's an error to move a page up when the first line is already on
+ // the screen. It's an error to move a page down when the last line
+ // is on the screen and the topline is 'scrolloff' lines from the
+ // last line.
if (dir == FORWARD
? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
&& curwin->w_botline > curbuf->b_ml.ml_line_count)
@@ -1945,11 +1953,12 @@ int onepage(Direction dir, long count)
* at the bottom of the window. */
n = 0;
while (n <= curwin->w_height_inner && loff.lnum >= 1) {
- topline_back(&loff);
- if (loff.height == MAXCOL)
+ topline_back(curwin, &loff);
+ if (loff.height == MAXCOL) {
n = MAXCOL;
- else
+ } else {
n += loff.height;
+ }
}
if (loff.lnum < 1) { /* at begin of file */
curwin->w_topline = 1;
@@ -1958,11 +1967,11 @@ int onepage(Direction dir, long count)
} else {
/* Go two lines forward again. */
topline_botline(&loff);
- botline_forw(&loff);
- botline_forw(&loff);
+ botline_forw(curwin, &loff);
+ botline_forw(curwin, &loff);
botline_topline(&loff);
- /* We're at the wrong end of a fold now. */
- (void)hasFolding(loff.lnum, &loff.lnum, NULL);
+ // We're at the wrong end of a fold now.
+ (void)hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL);
/* Always scroll at least one line. Avoid getting stuck on
* very long lines. */
@@ -2046,10 +2055,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
return; /* no overlap */
lineoff_T loff0 = *lp;
- if (dir > 0)
- botline_forw(lp);
- else
- topline_back(lp);
+ if (dir > 0) {
+ botline_forw(curwin, lp);
+ } else {
+ topline_back(curwin, lp);
+ }
int h2 = lp->height;
if (h2 == MAXCOL || h2 + h1 > min_height) {
*lp = loff0; /* no overlap */
@@ -2057,10 +2067,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
}
lineoff_T loff1 = *lp;
- if (dir > 0)
- botline_forw(lp);
- else
- topline_back(lp);
+ if (dir > 0) {
+ botline_forw(curwin, lp);
+ } else {
+ topline_back(curwin, lp);
+ }
int h3 = lp->height;
if (h3 == MAXCOL || h3 + h2 > min_height) {
*lp = loff0; /* no overlap */
@@ -2068,10 +2079,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
}
lineoff_T loff2 = *lp;
- if (dir > 0)
- botline_forw(lp);
- else
- topline_back(lp);
+ if (dir > 0) {
+ botline_forw(curwin, lp);
+ } else {
+ topline_back(curwin, lp);
+ }
int h4 = lp->height;
if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height)
*lp = loff1; /* 1 line overlap */
@@ -2094,8 +2106,8 @@ void halfpage(bool flag, linenr_T Prenum)
int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr
: curwin->w_height_inner;
- update_topline();
- validate_botline();
+ update_topline(curwin);
+ validate_botline(curwin);
int room = curwin->w_empty_rows + curwin->w_filler_rows;
if (flag) {
/*
@@ -2255,7 +2267,7 @@ void do_check_cursorbind(void)
// Only scroll when 'scrollbind' hasn't done this.
if (!curwin->w_p_scb) {
- update_topline();
+ update_topline(curwin);
}
curwin->w_redr_status = true;
}
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 68ef4cd41e..a0b439ac45 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -377,6 +377,10 @@ static void request_event(void **argv)
Channel *channel = e->channel;
MsgpackRpcRequestHandler handler = e->handler;
Error error = ERROR_INIT;
+ if (channel->rpc.closed) {
+ // channel was closed, abort any pending requests
+ goto free_ret;
+ }
Object result = handler.fn(channel->id, e->args, &error);
if (e->type == kMessageTypeRequest || ERROR_SET(&error)) {
// Send the response.
@@ -391,6 +395,8 @@ static void request_event(void **argv)
} else {
api_free_object(result);
}
+
+free_ret:
api_free_array(e->args);
channel_decref(channel);
xfree(e);
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0293bb4a73..f93d772068 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -168,7 +168,7 @@ static const struct nv_cmd {
{ NL, nv_down, 0, false },
{ Ctrl_K, nv_error, 0, 0 },
{ Ctrl_L, nv_clear, 0, 0 },
- { Ctrl_M, nv_down, 0, true },
+ { CAR, nv_down, 0, true },
{ Ctrl_N, nv_down, NV_STS, false },
{ Ctrl_O, nv_ctrlo, 0, 0 },
{ Ctrl_P, nv_up, NV_STS, false },
@@ -1261,7 +1261,7 @@ static void normal_redraw(NormalState *s)
{
// Before redrawing, make sure w_topline is correct, and w_leftcol
// if lines don't wrap, and w_skipcol if lines wrap.
- update_topline();
+ update_topline(curwin);
validate_cursor();
// If the cursor moves horizontally when 'concealcursor' is active, then the
@@ -1341,7 +1341,7 @@ static int normal_check(VimState *state)
} else if (do_redraw || stuff_empty()) {
// Need to make sure w_topline and w_leftcol are correct before
// normal_check_window_scrolled() is called.
- update_topline();
+ update_topline(curwin);
normal_check_cursor_moved(s);
normal_check_text_changed(s);
@@ -2629,7 +2629,7 @@ do_mouse (
/* Set global flag that we are extending the Visual area with mouse
* dragging; temporarily minimize 'scrolloff'. */
- if (VIsual_active && is_drag && get_scrolloff_value()) {
+ if (VIsual_active && is_drag && get_scrolloff_value(curwin)) {
// In the very first line, allow scrolling one line
if (mouse_row == 0) {
mouse_dragging = 2;
@@ -3052,57 +3052,57 @@ static bool find_is_eval_item(
return false;
}
-/*
- * Find the identifier under or to the right of the cursor.
- * "find_type" can have one of three values:
- * FIND_IDENT: find an identifier (keyword)
- * FIND_STRING: find any non-white string
- * FIND_IDENT + FIND_STRING: find any non-white string, identifier preferred.
- * FIND_EVAL: find text useful for C program debugging
- *
- * There are three steps:
- * 1. Search forward for the start of an identifier/string. Doesn't move if
- * already on one.
- * 2. Search backward for the start of this identifier/string.
- * This doesn't match the real Vi but I like it a little better and it
- * shouldn't bother anyone.
- * 3. Search forward to the end of this identifier/string.
- * When FIND_IDENT isn't defined, we backup until a blank.
- *
- * Returns the length of the string, or zero if no string is found.
- * If a string is found, a pointer to the string is put in "*string". This
- * string is not always NUL terminated.
- */
-size_t find_ident_under_cursor(char_u **string, int find_type)
+// Find the identifier under or to the right of the cursor.
+// "find_type" can have one of three values:
+// FIND_IDENT: find an identifier (keyword)
+// FIND_STRING: find any non-white text
+// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
+// FIND_EVAL: find text useful for C program debugging
+//
+// There are three steps:
+// 1. Search forward for the start of an identifier/text. Doesn't move if
+// already on one.
+// 2. Search backward for the start of this identifier/text.
+// This doesn't match the real Vi but I like it a little better and it
+// shouldn't bother anyone.
+// 3. Search forward to the end of this identifier/text.
+// When FIND_IDENT isn't defined, we backup until a blank.
+//
+// Returns the length of the text, or zero if no text is found.
+// If text is found, a pointer to the text is put in "*text". This
+// points into the current buffer line and is not always NUL terminated.
+size_t find_ident_under_cursor(char_u **text, int find_type)
+ FUNC_ATTR_NONNULL_ARG(1)
{
return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
- curwin->w_cursor.col, string, find_type);
+ curwin->w_cursor.col, text, NULL, find_type);
}
/*
* Like find_ident_under_cursor(), but for any window and any position.
* However: Uses 'iskeyword' from the current window!.
*/
-size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
- char_u **string, int find_type)
-{
- char_u *ptr;
- int col = 0; /* init to shut up GCC */
+size_t find_ident_at_pos(
+ win_T *wp,
+ linenr_T lnum,
+ colnr_T startcol,
+ char_u **text,
+ int *textcol, // column where "text" starts, can be NULL
+ int find_type)
+ FUNC_ATTR_NONNULL_ARG(1, 4)
+{
+ int col = 0; // init to shut up GCC
int i;
int this_class = 0;
int prev_class;
int prevcol;
int bn = 0; // bracket nesting
- /*
- * if i == 0: try to find an identifier
- * if i == 1: try to find any non-white string
- */
- ptr = ml_get_buf(wp->w_buffer, lnum, false);
- for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i) {
- /*
- * 1. skip to start of identifier/string
- */
+ // if i == 0: try to find an identifier
+ // if i == 1: try to find any non-white text
+ char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false);
+ for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) {
+ // 1. skip to start of identifier/text
col = startcol;
while (ptr[col] != NUL) {
// Stop at a ']' to evaluate "a[x]".
@@ -3120,7 +3120,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
bn = ptr[col] == ']';
//
- // 2. Back up to start of identifier/string.
+ // 2. Back up to start of identifier/text.
//
// Remember class of character under cursor.
if ((find_type & FIND_EVAL) && ptr[col] == ']') {
@@ -3143,7 +3143,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
col = prevcol;
}
- // If we don't want just any old string, or we've found an
+ // If we don't want just any old text, or we've found an
// identifier, stop searching.
if (this_class > 2) {
this_class = 2;
@@ -3154,7 +3154,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
}
if (ptr[col] == NUL || (i == 0 && this_class != 2)) {
- // Didn't find an identifier or string.
+ // Didn't find an identifier or text.
if (find_type & FIND_STRING) {
EMSG(_("E348: No string under cursor"));
} else {
@@ -3163,11 +3163,12 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol,
return 0;
}
ptr += col;
- *string = ptr;
+ *text = ptr;
+ if (textcol != NULL) {
+ *textcol = col;
+ }
- /*
- * 3. Find the end if the identifier/string.
- */
+ // 3. Find the end if the identifier/text.
bn = 0;
startcol -= col;
col = 0;
@@ -4135,7 +4136,7 @@ void scroll_redraw(int up, long count)
scrollup(count, true) :
scrolldown(count, true);
- if (get_scrolloff_value()) {
+ if (get_scrolloff_value(curwin)) {
// Adjust the cursor position for 'scrolloff'. Mark w_topline as
// valid, otherwise the screen jumps back at the end of the file.
cursor_correct();
@@ -4185,7 +4186,7 @@ static void nv_zet(cmdarg_T *cap)
int old_fen = curwin->w_p_fen;
bool undo = false;
- int l_p_siso = (int)get_sidescrolloff_value();
+ int l_p_siso = (int)get_sidescrolloff_value(curwin);
assert(l_p_siso <= INT_MAX);
if (ascii_isdigit(nchar)) {
@@ -4253,12 +4254,13 @@ dozet:
/* "z+", "z<CR>" and "zt": put cursor at top of screen */
case '+':
if (cap->count0 == 0) {
- /* No count given: put cursor at the line below screen */
- validate_botline(); /* make sure w_botline is valid */
- if (curwin->w_botline > curbuf->b_ml.ml_line_count)
+ // No count given: put cursor at the line below screen
+ validate_botline(curwin); // make sure w_botline is valid
+ if (curwin->w_botline > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- else
+ } else {
curwin->w_cursor.lnum = curwin->w_botline;
+ }
}
FALLTHROUGH;
case NL:
@@ -5053,7 +5055,7 @@ static void nv_scroll(cmdarg_T *cap)
setpcmark();
if (cap->cmdchar == 'L') {
- validate_botline(); /* make sure curwin->w_botline is valid */
+ validate_botline(curwin); // make sure curwin->w_botline is valid
curwin->w_cursor.lnum = curwin->w_botline - 1;
if (cap->count1 - 1 >= curwin->w_cursor.lnum)
curwin->w_cursor.lnum = 1;
@@ -5074,7 +5076,7 @@ static void nv_scroll(cmdarg_T *cap)
/* Don't count filler lines above the window. */
used -= diff_check_fill(curwin, curwin->w_topline)
- curwin->w_topfill;
- validate_botline(); // make sure w_empty_rows is valid
+ validate_botline(curwin); // make sure w_empty_rows is valid
half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
// Count half he number of filler lines to be "below this
@@ -6653,16 +6655,15 @@ static void nv_g_cmd(cmdarg_T *cap)
VIsual = curwin->w_cursor;
curwin->w_cursor = tpos;
check_cursor();
- update_topline();
- /*
- * When called from normal "g" command: start Select mode when
- * 'selectmode' contains "cmd". When called for K_SELECT, always
- * start Select mode.
- */
- if (cap->arg)
+ update_topline(curwin);
+ // When called from normal "g" command: start Select mode when
+ // 'selectmode' contains "cmd". When called for K_SELECT, always
+ // start Select mode.
+ if (cap->arg) {
VIsual_select = true;
- else
+ } else {
may_start_select('c');
+ }
setmouse();
redraw_curbuf_later(INVERTED);
showmode();
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 40dd5f0b5c..052b07ed44 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1197,7 +1197,13 @@ int insert_reg(
retval = FAIL;
} else {
for (size_t i = 0; i < reg->y_size; i++) {
- stuffescaped((const char *)reg->y_array[i], literally);
+ if (regname == '-') {
+ AppendCharToRedobuff(Ctrl_R);
+ AppendCharToRedobuff(regname);
+ do_put(regname, NULL, BACKWARD, 1L, PUT_CURSEND);
+ } else {
+ stuffescaped((const char *)reg->y_array[i], literally);
+ }
// Insert a newline between lines and after last line if
// y_type is kMTLineWise.
if (reg->y_type == kMTLineWise || i < reg->y_size - 1) {
@@ -1683,6 +1689,7 @@ int op_delete(oparg_T *oap)
(int)oap->line_count-1, n, deleted_bytes,
0, 0, 0, kExtmarkUndo);
}
+ auto_format(false, true);
}
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
@@ -3547,15 +3554,21 @@ void ex_display(exarg_T *eap)
int name;
char_u *arg = eap->arg;
int clen;
+ char_u type[2];
if (arg != NULL && *arg == NUL)
arg = NULL;
int attr = HL_ATTR(HLF_8);
- /* Highlight title */
- MSG_PUTS_TITLE(_("\n--- Registers ---"));
+ // Highlight title
+ msg_puts_title(_("\nType Name Content"));
for (int i = -1; i < NUM_REGISTERS && !got_int; i++) {
name = get_register_name(i);
+ switch (get_reg_type(name, NULL)) {
+ case kMTLineWise: type[0] = 'l'; break;
+ case kMTCharWise: type[0] = 'c'; break;
+ default: type[0] = 'b'; break;
+ }
if (arg != NULL && vim_strchr(arg, name) == NULL) {
continue; /* did not ask for this register */
@@ -3580,11 +3593,14 @@ void ex_display(exarg_T *eap)
if (yb->y_array != NULL) {
msg_putchar('\n');
+ msg_puts(" ");
+ msg_putchar(type[0]);
+ msg_puts(" ");
msg_putchar('"');
msg_putchar(name);
MSG_PUTS(" ");
- int n = Columns - 6;
+ int n = Columns - 11;
for (size_t j = 0; j < yb->y_size && n > 1; j++) {
if (j) {
MSG_PUTS_ATTR("^J", attr);
@@ -3609,8 +3625,8 @@ void ex_display(exarg_T *eap)
*/
if ((p = get_last_insert()) != NULL
&& (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) {
- MSG_PUTS("\n\". ");
- dis_msg(p, TRUE);
+ msg_puts("\n c \". ");
+ dis_msg(p, true);
}
/*
@@ -3618,8 +3634,8 @@ void ex_display(exarg_T *eap)
*/
if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL)
&& !got_int) {
- MSG_PUTS("\n\": ");
- dis_msg(last_cmdline, FALSE);
+ msg_puts("\n c \": ");
+ dis_msg(last_cmdline, false);
}
/*
@@ -3627,8 +3643,8 @@ void ex_display(exarg_T *eap)
*/
if (curbuf->b_fname != NULL
&& (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
- MSG_PUTS("\n\"% ");
- dis_msg(curbuf->b_fname, FALSE);
+ msg_puts("\n c \"% ");
+ dis_msg(curbuf->b_fname, false);
}
/*
@@ -3639,8 +3655,8 @@ void ex_display(exarg_T *eap)
linenr_T dummy;
if (buflist_name_nr(0, &fname, &dummy) != FAIL) {
- MSG_PUTS("\n\"# ");
- dis_msg(fname, FALSE);
+ msg_puts("\n c \"# ");
+ dis_msg(fname, false);
}
}
@@ -3649,8 +3665,8 @@ void ex_display(exarg_T *eap)
*/
if (last_search_pat() != NULL
&& (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) {
- MSG_PUTS("\n\"/ ");
- dis_msg(last_search_pat(), FALSE);
+ msg_puts("\n c \"/ ");
+ dis_msg(last_search_pat(), false);
}
/*
@@ -3658,8 +3674,8 @@ void ex_display(exarg_T *eap)
*/
if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL)
&& !got_int) {
- MSG_PUTS("\n\"= ");
- dis_msg(expr_line, FALSE);
+ msg_puts("\n c \"= ");
+ dis_msg(expr_line, false);
}
}
@@ -3669,9 +3685,10 @@ void ex_display(exarg_T *eap)
*/
static void
dis_msg(
- char_u *p,
- int skip_esc /* if TRUE, ignore trailing ESC */
+ const char_u *p,
+ bool skip_esc // if true, ignore trailing ESC
)
+ FUNC_ATTR_NONNULL_ALL
{
int n;
int l;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 7cf8412269..d60c8bc01c 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -320,7 +320,7 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2",
"auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9",
"yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8",
"yes:9", "number", NULL };
-static char *(p_fdc_values[]) = { "auto:1", "auto:2",
+static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2",
"auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL };
@@ -4338,6 +4338,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
char buf_old[NUMBUFLEN];
char buf_new[NUMBUFLEN];
char buf_type[7];
+
vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value);
vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value);
vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
@@ -7159,20 +7160,20 @@ dict_T *get_winbuf_options(const int bufopt)
/// Return the effective 'scrolloff' value for the current window, using the
/// global value when appropriate.
-long get_scrolloff_value(void)
+long get_scrolloff_value(win_T *wp)
{
// Disallow scrolloff in terminal-mode. #11915
if (State & TERM_FOCUS) {
return 0;
}
- return curwin->w_p_so < 0 ? p_so : curwin->w_p_so;
+ return wp->w_p_so < 0 ? p_so : wp->w_p_so;
}
/// Return the effective 'sidescrolloff' value for the current window, using the
/// global value when appropriate.
-long get_sidescrolloff_value(void)
+long get_sidescrolloff_value(win_T *wp)
{
- return curwin->w_p_siso < 0 ? p_siso : curwin->w_p_siso;
+ return wp->w_p_siso < 0 ? p_siso : wp->w_p_siso;
}
Dictionary get_vimoption(String name, Error *err)
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 551a650045..aef7ffa397 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -809,7 +809,7 @@ static int pum_set_selected(int n, int repeat)
no_u_sync++;
win_enter(curwin_save, true);
no_u_sync--;
- update_topline();
+ update_topline(curwin);
}
// Update the screen before drawing the popup menu.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 7fdc998e20..aeac6e4905 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3696,7 +3696,7 @@ void ex_copen(exarg_T *eap)
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
check_cursor();
- update_topline(); // scroll to show the line
+ update_topline(curwin); // scroll to show the line
}
// Move the cursor in the quickfix window to "lnum".
@@ -3710,7 +3710,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum)
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
curwin->w_curswant = 0;
- update_topline(); // scroll to show the line
+ update_topline(curwin); // scroll to show the line
redraw_later(curwin, VALID);
curwin->w_redr_status = true; // update ruler
curwin = old_curwin;
@@ -3824,17 +3824,21 @@ static buf_T *qf_find_buf(qf_info_T *qi)
return NULL;
}
-/// Update the w:quickfix_title variable in the quickfix/location list window
+/// Update the w:quickfix_title variable in the quickfix/location list window in
+/// all the tab pages.
static void qf_update_win_titlevar(qf_info_T *qi)
+ FUNC_ATTR_NONNULL_ALL
{
- win_T *win;
+ qf_list_T *const qfl = qf_get_curlist(qi);
+ win_T *const save_curwin = curwin;
- if ((win = qf_find_win(qi)) != NULL) {
- win_T *curwin_save = curwin;
- curwin = win;
- qf_set_title_var(qf_get_curlist(qi));
- curwin = curwin_save;
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (is_qf_win(win, qi)) {
+ curwin = win;
+ qf_set_title_var(qfl);
+ }
}
+ curwin = save_curwin;
}
// Find the quickfix buffer. If it exists, update the contents.
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 2878e73573..a2589ac431 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -3605,19 +3605,21 @@ theend:
if (backpos.ga_maxlen > BACKPOS_INITIAL)
ga_clear(&backpos);
- // Make sure the end is never before the start. Can happen when \zs and
- // \ze are used.
- if (REG_MULTI) {
- const lpos_T *const start = &rex.reg_mmatch->startpos[0];
- const lpos_T *const end = &rex.reg_mmatch->endpos[0];
+ if (retval > 0) {
+ // Make sure the end is never before the start. Can happen when \zs
+ // and \ze are used.
+ if (REG_MULTI) {
+ const lpos_T *const start = &rex.reg_mmatch->startpos[0];
+ const lpos_T *const end = &rex.reg_mmatch->endpos[0];
- if (end->lnum < start->lnum
- || (end->lnum == start->lnum && end->col < start->col)) {
- rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
- }
- } else {
- if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
- rex.reg_match->endp[0] = rex.reg_match->startp[0];
+ if (end->lnum < start->lnum
+ || (end->lnum == start->lnum && end->col < start->col)) {
+ rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
+ }
+ } else {
+ if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
+ rex.reg_match->endp[0] = rex.reg_match->startp[0];
+ }
}
}
@@ -3722,8 +3724,7 @@ static long regtry(bt_regprog_T *prog,
} else {
if (reg_startzp[i] != NULL && reg_endzp[i] != NULL)
re_extmatch_out->matches[i] =
- vim_strnsave(reg_startzp[i],
- (int)(reg_endzp[i] - reg_startzp[i]));
+ vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]);
}
}
}
@@ -6563,7 +6564,7 @@ static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv,
if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL;
} else {
- s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s));
+ s = vim_strnsave(s, rsm.sm_match->endp[i] - s);
}
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->vval.v_string = s;
@@ -7082,7 +7083,7 @@ char_u *reg_submatch(int no)
if (s == NULL || rsm.sm_match->endp[no] == NULL) {
retval = NULL;
} else {
- retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s));
+ retval = vim_strnsave(s, rsm.sm_match->endp[no] - s);
}
}
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 137b2304c0..8b5ee59d40 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -5243,9 +5243,12 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
switch (t->state->c) {
case NFA_MATCH:
{
- // If the match ends before a composing characters and
- // rex.reg_icombine is not set, that is not really a match.
- if (!rex.reg_icombine && utf_iscomposing(curc)) {
+ // If the match is not at the start of the line, ends before a
+ // composing characters and rex.reg_icombine is not set, that
+ // is not really a match.
+ if (!rex.reg_icombine
+ && rex.input != rex.line
+ && utf_iscomposing(curc)) {
break;
}
nfa_match = true;
@@ -6477,8 +6480,7 @@ static long nfa_regtry(nfa_regprog_T *prog,
if (lpos->start != NULL && lpos->end != NULL)
re_extmatch_out->matches[i] =
- vim_strnsave(lpos->start,
- (int)(lpos->end - lpos->start));
+ vim_strnsave(lpos->start, lpos->end - lpos->start);
}
}
}
@@ -6591,19 +6593,21 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol,
#endif
theend:
- // Make sure the end is never before the start. Can happen when \zs and
- // \ze are used.
- if (REG_MULTI) {
- const lpos_T *const start = &rex.reg_mmatch->startpos[0];
- const lpos_T *const end = &rex.reg_mmatch->endpos[0];
+ if (retval > 0) {
+ // Make sure the end is never before the start. Can happen when \zs and
+ // \ze are used.
+ if (REG_MULTI) {
+ const lpos_T *const start = &rex.reg_mmatch->startpos[0];
+ const lpos_T *const end = &rex.reg_mmatch->endpos[0];
- if (end->lnum < start->lnum
- || (end->lnum == start->lnum && end->col < start->col)) {
- rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
- }
- } else {
- if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
- rex.reg_match->endp[0] = rex.reg_match->startp[0];
+ if (end->lnum < start->lnum
+ || (end->lnum == start->lnum && end->col < start->col)) {
+ rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0];
+ }
+ } else {
+ if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) {
+ rex.reg_match->endp[0] = rex.reg_match->startp[0];
+ }
}
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2eeeebb88d..dc028f0ed7 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -620,7 +620,7 @@ int update_screen(int type)
/* Clear or redraw the command line. Done last, because scrolling may
* mess up the command line. */
- if (clear_cmdline || redraw_cmdline) {
+ if (clear_cmdline || redraw_cmdline || redraw_mode) {
showmode();
}
@@ -683,7 +683,7 @@ void conceal_check_cursor_line(void)
redrawWinline(curwin, curwin->w_cursor.lnum);
// Need to recompute cursor column, e.g., when starting Visual mode
// without concealing. */
- curs_columns(true);
+ curs_columns(curwin, true);
}
}
@@ -1698,7 +1698,7 @@ static void win_update(win_T *wp, Providers *providers)
const int new_wcol = wp->w_wcol;
recursive = true;
curwin->w_valid &= ~VALID_TOPLINE;
- update_topline(); // may invalidate w_botline again
+ update_topline(curwin); // may invalidate w_botline again
if (old_wcol != new_wcol
&& (wp->w_valid & (VALID_WCOL|VALID_WROW))
@@ -2013,6 +2013,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// end-of-line
int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
+ bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines >= 0;
// saved "extra" items for when draw_state becomes WL_LINE (again)
int saved_n_extra = 0;
@@ -2196,7 +2197,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
if (wp->w_p_spell
- && foldinfo.fi_lines == 0
+ && !has_fold
&& *wp->w_s->b_p_spl != NUL
&& !GA_EMPTY(&wp->w_s->b_langp)
&& *(char **)(wp->w_s->b_langp.ga_data) != NULL) {
@@ -2299,6 +2300,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// handle 'incsearch' and ":s///c" highlighting
} else if (highlight_match
&& wp == curwin
+ && !has_fold
&& lnum >= curwin->w_cursor.lnum
&& lnum <= curwin->w_cursor.lnum + search_match_lines) {
if (lnum == curwin->w_cursor.lnum) {
@@ -2551,7 +2553,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
cur = wp->w_match_head;
shl_flag = false;
while ((cur != NULL || !shl_flag) && !number_only
- && foldinfo.fi_lines == 0
+ && !has_fold
) {
if (!shl_flag) {
shl = &search_hl;
@@ -3883,8 +3885,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
/* check if line ends before left margin */
if (vcol < v + col - win_col_off(wp))
vcol = v + col - win_col_off(wp);
- /* Get rid of the boguscols now, we want to draw until the right
- * edge for 'cursorcolumn'. */
+ // Get rid of the boguscols now, we want to draw until the right
+ // edge for 'cursorcolumn'.
col -= boguscols;
// boguscols = 0; // Disabled because value never read after this
@@ -6557,12 +6559,28 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
return;
}
+// Return true when postponing displaying the mode message: when not redrawing
+// or inside a mapping.
+bool skip_showmode(void)
+{
+ // Call char_avail() only when we are going to show something, because it
+ // takes a bit of time. redrawing() may also call char_avail_avail().
+ if (global_busy
+ || msg_silent != 0
+ || !redrawing()
+ || (char_avail() && !KeyTyped)) {
+ redraw_mode = true; // show mode later
+ return true;
+ }
+ return false;
+}
// Show the current mode and ruler.
//
// If clear_cmdline is TRUE, clear the rest of the cmdline.
// If clear_cmdline is FALSE there may be a message there that needs to be
// cleared only if a mode is shown.
+// If redraw_mode is true show or clear the mode.
// Return the length of the message (0 if no message).
int showmode(void)
{
@@ -6588,12 +6606,8 @@ int showmode(void)
|| restart_edit
|| VIsual_active));
if (do_mode || reg_recording != 0) {
- // Don't show mode right now, when not redrawing or inside a mapping.
- // Call char_avail() only when we are going to show something, because
- // it takes a bit of time.
- if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0) {
- redraw_cmdline = TRUE; /* show mode later */
- return 0;
+ if (skip_showmode()) {
+ return 0; // show mode later
}
nwr_save = need_wait_return;
@@ -6713,10 +6727,11 @@ int showmode(void)
need_clear = true;
}
- mode_displayed = TRUE;
- if (need_clear || clear_cmdline)
+ mode_displayed = true;
+ if (need_clear || clear_cmdline || redraw_mode) {
msg_clr_eos();
- msg_didout = FALSE; /* overwrite this message */
+ }
+ msg_didout = false; // overwrite this message
length = msg_col;
msg_col = 0;
msg_no_more = false;
@@ -6725,6 +6740,9 @@ int showmode(void)
} else if (clear_cmdline && msg_silent == 0) {
// Clear the whole command line. Will reset "clear_cmdline".
msg_clr_cmdline();
+ } else if (redraw_mode) {
+ msg_pos_mode();
+ msg_clr_eos();
}
// NB: also handles clearing the showmode if it was emtpy or disabled
@@ -6741,6 +6759,7 @@ int showmode(void)
win_redr_ruler(last, true);
}
redraw_cmdline = false;
+ redraw_mode = false;
clear_cmdline = false;
return length;
@@ -7380,7 +7399,7 @@ void screen_resize(int width, int height)
cmdline_pum_display(false);
}
} else {
- update_topline();
+ update_topline(curwin);
if (pum_drawn()) {
// TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
// For now make sure the nested update_screen(0) won't redraw the
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index ffe51287c5..fc9f53c192 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -173,13 +173,15 @@ static void insert_sign(
const char_u *group, // sign group; NULL for global group
int prio, // sign priority
linenr_T lnum, // line number which gets the mark
- int typenr // typenr of sign we are adding
+ int typenr, // typenr of sign we are adding
+ bool has_text_or_icon // sign has text or icon
)
{
signlist_T *newsign = xmalloc(sizeof(signlist_T));
newsign->id = id;
newsign->lnum = lnum;
newsign->typenr = typenr;
+ newsign->has_text_or_icon = has_text_or_icon;
if (group != NULL) {
newsign->group = sign_group_ref(group);
} else {
@@ -210,13 +212,14 @@ static void insert_sign(
/// Insert a new sign sorted by line number and sign priority.
static void insert_sign_by_lnum_prio(
- buf_T *buf, // buffer to store sign in
- signlist_T *prev, // previous sign entry
- int id, // sign ID
- const char_u *group, // sign group; NULL for global group
- int prio, // sign priority
- linenr_T lnum, // line number which gets the mark
- int typenr // typenr of sign we are adding
+ buf_T *buf, // buffer to store sign in
+ signlist_T *prev, // previous sign entry
+ int id, // sign ID
+ const char_u *group, // sign group; NULL for global group
+ int prio, // sign priority
+ linenr_T lnum, // line number which gets the mark
+ int typenr, // typenr of sign we are adding
+ bool has_text_or_icon // sign has text or icon
)
{
signlist_T *sign;
@@ -234,7 +237,7 @@ static void insert_sign_by_lnum_prio(
sign = prev->next;
}
- insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
+ insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon);
}
/// Get the name of a sign by its typenr.
@@ -342,12 +345,13 @@ static void sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign)
/// Add the sign into the signlist. Find the right spot to do it though.
void buf_addsign(
- buf_T *buf, // buffer to store sign in
- int id, // sign ID
+ buf_T *buf, // buffer to store sign in
+ int id, // sign ID
const char_u *groupname, // sign group
- int prio, // sign priority
- linenr_T lnum, // line number which gets the mark
- int typenr // typenr of sign we are adding
+ int prio, // sign priority
+ linenr_T lnum, // line number which gets the mark
+ int typenr, // typenr of sign we are adding
+ bool has_text_or_icon // sign has text or icon
)
{
signlist_T *sign; // a sign in the signlist
@@ -363,13 +367,29 @@ void buf_addsign(
sign_sort_by_prio_on_line(buf, sign);
return;
} else if (lnum < sign->lnum) {
- insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
+ insert_sign_by_lnum_prio(
+ buf,
+ prev,
+ id,
+ groupname,
+ prio,
+ lnum,
+ typenr,
+ has_text_or_icon);
return;
}
prev = sign;
}
- insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
+ insert_sign_by_lnum_prio(
+ buf,
+ prev,
+ id,
+ groupname,
+ prio,
+ lnum,
+ typenr,
+ has_text_or_icon);
}
// For an existing, placed sign "markId" change the type to "typenr".
@@ -786,11 +806,15 @@ static int sign_define_init_text(sign_T *sp, char_u *text)
}
cells += utf_ptr2cells(s);
}
- // Currently must be one or two display cells
- if (s != endp || cells < 1 || cells > 2) {
+ // Currently must be empty, one or two display cells
+ if (s != endp || cells > 2) {
EMSG2(_("E239: Invalid sign text: %s"), text);
return FAIL;
}
+ if (cells < 1) {
+ sp->sn_text = NULL;
+ return OK;
+ }
xfree(sp->sn_text);
// Allocate one byte more if we need to pad up
@@ -939,7 +963,15 @@ int sign_place(
if (lnum > 0) {
// ":sign place {id} line={lnum} name={name} file={fname}":
// place a sign
- buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
+ bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL;
+ buf_addsign(
+ buf,
+ *sign_id,
+ sign_group,
+ prio,
+ lnum,
+ sp->sn_typenr,
+ has_text_or_icon);
} else {
// ":sign place {id} file={fname}": change sign type
lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 687c15bbd6..c898dba890 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -1,6 +1,7 @@
#ifndef NVIM_SIGN_DEFS_H
#define NVIM_SIGN_DEFS_H
+#include <stdbool.h>
#include "nvim/pos.h"
#include "nvim/types.h"
@@ -22,13 +23,14 @@ typedef struct signlist signlist_T;
struct signlist
{
- int id; // unique identifier for each placed sign
- linenr_T lnum; // line number which has this sign
- int typenr; // typenr of sign
- signgroup_T *group; // sign group
- int priority; // priority for highlighting
- signlist_T *next; // next signlist entry
- signlist_T *prev; // previous entry -- for easy reordering
+ int id; // unique identifier for each placed sign
+ linenr_T lnum; // line number which has this sign
+ int typenr; // typenr of sign
+ bool has_text_or_icon; // has text or icon
+ signgroup_T *group; // sign group
+ int priority; // priority for highlighting
+ signlist_T *next; // next signlist entry
+ signlist_T *prev; // previous entry -- for easy reordering
};
// Default sign priority for highlighting
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 90af010164..90945eafd7 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -3942,7 +3942,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
msg_start();
msg_puts(_(msg_compressing));
msg_clr_eos();
- msg_didout = FALSE;
+ msg_didout = false;
msg_col = 0;
ui_flush();
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index e91d560284..4d88df5a3a 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -4186,10 +4186,10 @@ get_syn_options(
arg = skiptowhite(arg);
if (gname_start == arg)
return NULL;
- gname = vim_strnsave(gname_start, (int)(arg - gname_start));
- if (STRCMP(gname, "NONE") == 0)
+ gname = vim_strnsave(gname_start, arg - gname_start);
+ if (STRCMP(gname, "NONE") == 0) {
*opt->sync_idx = NONE_IDX;
- else {
+ } else {
syn_id = syn_name2id(gname);
int i;
for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
@@ -4587,7 +4587,7 @@ syn_cmd_region(
while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=')
++key_end;
xfree(key);
- key = vim_strnsave_up(rest, (int)(key_end - rest));
+ key = vim_strnsave_up(rest, key_end - rest);
if (STRCMP(key, "MATCHGROUP") == 0) {
item = ITEM_MATCHGROUP;
} else if (STRCMP(key, "START") == 0) {
@@ -5047,8 +5047,8 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
return NULL;
}
- /* store the pattern and compiled regexp program */
- ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1));
+ // store the pattern and compiled regexp program
+ ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1);
/* Make 'cpoptions' empty, to avoid the 'l' flag */
cpo_save = p_cpo;
@@ -5136,7 +5136,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
arg_end = skiptowhite(arg_start);
next_arg = skipwhite(arg_end);
xfree(key);
- key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
+ key = vim_strnsave_up(arg_start, arg_end - arg_start);
if (STRCMP(key, "CCOMMENT") == 0) {
if (!eap->skip)
curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
@@ -5195,7 +5195,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (!eap->skip) {
/* store the pattern and compiled regexp program */
curwin->w_s->b_syn_linecont_pat =
- vim_strnsave(next_arg + 1, (int)(arg_end - next_arg - 1));
+ vim_strnsave(next_arg + 1, arg_end - next_arg - 1);
curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
/* Make 'cpoptions' empty, to avoid the 'l' flag */
@@ -5555,18 +5555,17 @@ void ex_syntax(exarg_T *eap)
{
char_u *arg = eap->arg;
char_u *subcmd_end;
- char_u *subcmd_name;
- int i;
syn_cmdlinep = eap->cmdlinep;
- /* isolate subcommand name */
- for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
- ;
- subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
- if (eap->skip) /* skip error messages for all subcommands */
- ++emsg_skip;
- for (i = 0;; ++i) {
+ // isolate subcommand name
+ for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); subcmd_end++) {
+ }
+ char_u *const subcmd_name = vim_strnsave(arg, subcmd_end - arg);
+ if (eap->skip) { // skip error messages for all subcommands
+ emsg_skip++;
+ }
+ for (int i = 0;; i++) {
if (subcommands[i].name == NULL) {
EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
break;
@@ -6719,7 +6718,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
xfree(key);
key = (char *)vim_strnsave_up((const char_u *)key_start,
- (int)(linep - key_start));
+ linep - key_start);
linep = (const char *)skipwhite((const char_u *)linep);
if (strcmp(key, "NONE") == 0) {
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 7f6b7dcfec..24d3959f83 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -12,6 +12,9 @@ endfunc
" Command to check for the presence of a working option.
command -nargs=1 CheckOption call CheckOption(<f-args>)
func CheckOption(name)
+ if !exists('&' .. a:name)
+ throw 'Checking for non-existent option ' .. a:name
+ endif
if !exists('+' .. a:name)
throw 'Skipped: ' .. a:name .. ' option not supported'
endif
@@ -74,6 +77,14 @@ func CheckCanRunGui()
endif
endfunc
+" Command to check that we are using the GUI
+command CheckGui call CheckGui()
+func CheckGui()
+ if !has('gui_running')
+ throw 'Skipped: only works in the GUI'
+ endif
+endfunc
+
" Command to check that not currently using the GUI
command CheckNotGui call CheckNotGui()
func CheckNotGui()
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index d032c9a739..fd9cfb54be 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -20,6 +20,7 @@ set tags=./tags,tags
set undodir^=.
set wildoptions=
set startofline
+set sessionoptions&vi
" Prevent Nvim log from writing to stderr.
let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 076f03fdd8..2567dd2be2 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -19,7 +19,7 @@ func Test_setbufline_getbufline()
let b = bufnr('%')
wincmd w
call assert_equal(1, setbufline(b, 5, ['x']))
- call assert_equal(1, setbufline(1234, 1, ['x']))
+ call assert_equal(1, setbufline(bufnr('$') + 1, 1, ['x']))
call assert_equal(0, setbufline(b, 4, ['d', 'e']))
call assert_equal(['c'], getbufline(b, 3))
call assert_equal(['d'], getbufline(b, 4))
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index 3377f86126..53704bd094 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -61,6 +61,15 @@ func Test_client_server()
call assert_fails('call remote_send("XXX", ":let testvar = ''yes''\<CR>")', 'E241')
+ call writefile(['one'], 'Xclientfile')
+ let cmd = GetVimProg() .. ' --servername ' .. name .. ' --remote Xclientfile'
+ call system(cmd)
+ call WaitForAssert({-> assert_equal('Xclientfile', remote_expr(name, "bufname()", "", 2))})
+ call WaitForAssert({-> assert_equal('one', remote_expr(name, "getline(1)", "", 2))})
+ call writefile(['one', 'two'], 'Xclientfile')
+ call system(cmd)
+ call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))})
+
" Expression evaluated locally.
if v:servername == ''
call remote_startserver('MYSELF')
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 81f653c393..39f865144a 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -51,6 +51,22 @@ func Test_complete_wildmenu()
call feedkeys(":e Xdir1/\<Tab>\<Down>\<Up>\<Right>\<CR>", 'tx')
call assert_equal('testfile1', getline(1))
+ " this fails in some Unix GUIs, not sure why
+ if !has('unix') || !has('gui_running')
+ " <C-J>/<C-K> mappings to go up/down directories when 'wildcharm' is
+ " different than 'wildchar'.
+ set wildcharm=<C-Z>
+ cnoremap <C-J> <Down><C-Z>
+ cnoremap <C-K> <Up><C-Z>
+ call feedkeys(":e Xdir1/\<Tab>\<C-J>\<CR>", 'tx')
+ call assert_equal('testfile3', getline(1))
+ call feedkeys(":e Xdir1/\<Tab>\<C-J>\<C-K>\<CR>", 'tx')
+ call assert_equal('testfile1', getline(1))
+ set wildcharm=0
+ cunmap <C-J>
+ cunmap <C-K>
+ endif
+
" cleanup
%bwipe
call delete('Xdir1/Xdir2/Xtestfile4')
@@ -62,6 +78,33 @@ func Test_complete_wildmenu()
set nowildmenu
endfunc
+func Test_wildmenu_screendump()
+ CheckScreendump
+
+ let lines =<< trim [SCRIPT]
+ set wildmenu hlsearch
+ [SCRIPT]
+ call writefile(lines, 'XTest_wildmenu')
+
+ let buf = RunVimInTerminal('-S XTest_wildmenu', {'rows': 8})
+ call term_sendkeys(buf, ":vim\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_1', {})
+
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_2', {})
+
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_3', {})
+
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_4', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_wildmenu')
+endfunc
+
func Test_map_completion()
if !has('cmdline_compl')
return
@@ -548,6 +591,13 @@ func Test_cmdline_complete_user_names()
endif
endfunc
+func Test_cmdline_complete_bang()
+ if executable('whoami')
+ call feedkeys(":!whoam\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^".*\<whoami\>', @:)
+ endif
+endfunc
+
funct Test_cmdline_complete_languages()
let lang = substitute(execute('language messages'), '.*"\(.*\)"$', '\1', '')
@@ -570,6 +620,17 @@ funct Test_cmdline_complete_languages()
endif
endfunc
+func Test_cmdline_complete_expression()
+ let g:SomeVar = 'blah'
+ for cmd in ['exe', 'echo', 'echon', 'echomsg']
+ call feedkeys(":" .. cmd .. " SomeV\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"' .. cmd .. ' SomeVar', @:)
+ call feedkeys(":" .. cmd .. " foo SomeV\<Tab>\<C-B>\"\<CR>", 'tx')
+ call assert_match('"' .. cmd .. ' foo SomeVar', @:)
+ endfor
+ unlet g:SomeVar
+endfunc
+
func Test_cmdline_write_alternatefile()
new
call setline('.', ['one', 'two'])
@@ -819,6 +880,36 @@ func Test_cmdwin_cedit()
delfunc CmdWinType
endfunc
+func Test_cmdwin_restore()
+ CheckScreendump
+
+ let lines =<< trim [SCRIPT]
+ call setline(1, range(30))
+ 2split
+ [SCRIPT]
+ call writefile(lines, 'XTest_restore')
+
+ let buf = RunVimInTerminal('-S XTest_restore', {'rows': 12})
+ call term_wait(buf, 100)
+ call term_sendkeys(buf, "q:")
+ call VerifyScreenDump(buf, 'Test_cmdwin_restore_1', {})
+
+ " normal restore
+ call term_sendkeys(buf, ":q\<CR>")
+ call VerifyScreenDump(buf, 'Test_cmdwin_restore_2', {})
+
+ " restore after setting 'lines' with one window
+ call term_sendkeys(buf, ":close\<CR>")
+ call term_sendkeys(buf, "q:")
+ call term_sendkeys(buf, ":set lines=18\<CR>")
+ call term_sendkeys(buf, ":q\<CR>")
+ call VerifyScreenDump(buf, 'Test_cmdwin_restore_3', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_restore')
+endfunc
+
func Test_buffers_lastused()
" check that buffers are sorted by time when wildmode has lastused
edit bufc " oldest
@@ -894,5 +985,23 @@ func Test_zero_line_search()
q!
endfunc
+func Test_read_shellcmd()
+ CheckUnix
+ if executable('ls')
+ " There should be ls in the $PATH
+ call feedkeys(":r! l\<c-a>\<c-b>\"\<cr>", 'tx')
+ call assert_match('^"r! .*\<ls\>', @:)
+ endif
+
+ if executable('rm')
+ call feedkeys(":r! ++enc=utf-8 r\<c-a>\<c-b>\"\<cr>", 'tx')
+ call assert_notmatch('^"r!.*\<runtest.vim\>', @:)
+ call assert_match('^"r!.*\<rm\>', @:)
+
+ call feedkeys(":r ++enc=utf-8 !rm\<c-a>\<c-b>\"\<cr>", 'tx')
+ call assert_notmatch('^"r.*\<runtest.vim\>', @:)
+ call assert_match('^"r ++enc\S\+ !.*\<rm\>', @:)
+ endif
+endfunc
-" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index 9eea27740d..b6d9687560 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -1,8 +1,8 @@
" Tests for digraphs
-if !has("digraphs")
- finish
-endif
+source check.vim
+CheckFeature digraphs
+source term_util.vim
func Put_Dig(chars)
exe "norm! o\<c-k>".a:chars
@@ -487,4 +487,20 @@ func Test_show_digraph_cp1251()
bwipe!
endfunc
+" Test for the characters displayed on the screen when entering a digraph
+func Test_entering_digraph()
+ CheckRunVimInTerminal
+ let buf = RunVimInTerminal('', {'rows': 6})
+ call term_sendkeys(buf, "i\<C-K>")
+ call term_wait(buf)
+ call assert_equal('?', term_getline(buf, 1))
+ call term_sendkeys(buf, "1")
+ call term_wait(buf)
+ call assert_equal('1', term_getline(buf, 1))
+ call term_sendkeys(buf, "2")
+ call term_wait(buf)
+ call assert_equal('½', term_getline(buf, 1))
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
index 8dc25f62b1..465613f1cf 100644
--- a/src/nvim/testdir/test_fileformat.vim
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -31,3 +31,26 @@ func Test_fileformat_autocommand()
au! BufReadPre Xfile
bw!
endfunc
+
+" Test for changing the fileformat using ++read
+func Test_fileformat_plusplus_read()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ w ++ff=dos Xfile1
+ enew!
+ set ff=unix
+ " A :read doesn't change the fileformat, but does apply to the read lines.
+ r ++fileformat=unix Xfile1
+ call assert_equal('unix', &fileformat)
+ call assert_equal("three\r", getline('$'))
+ 3r ++edit Xfile1
+ call assert_equal('dos', &fileformat)
+ close!
+ call delete('Xfile1')
+ set fileformat&
+ call assert_fails('e ++fileformat Xfile1', 'E474:')
+ call assert_fails('e ++ff=abc Xfile1', 'E474:')
+ call assert_fails('e ++abc1 Xfile1', 'E474:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 356a7ce135..2123780f11 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -181,6 +181,7 @@ let s:filename_checks = {
\ 'gdb': ['.gdbinit'],
\ 'gdmo': ['file.mo', 'file.gdmo'],
\ 'gedcom': ['file.ged', 'lltxxxxx.txt'],
+ \ 'gift': ['file.gift'],
\ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'],
\ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig'],
\ 'gitolite': ['gitolite.conf'],
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 3c90c45952..88ce64b9eb 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -815,4 +815,11 @@ func Test_fold_create_delete_create()
bwipe!
endfunc
+" this was crashing
+func Test_fold_create_delete()
+ new
+ norm zFzFzdzj
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index bdeaf8e2cf..7ccf2812ff 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -1,3 +1,4 @@
+source check.vim
func Test_yank_put_clipboard()
new
@@ -10,6 +11,16 @@ func Test_yank_put_clipboard()
bwipe!
endfunc
+func Test_global_set_clipboard()
+ CheckFeature clipboard_working
+ new
+ set clipboard=unnamedplus
+ let @+='clipboard' | g/^/set cb= | let @" = 'unnamed' | put
+ call assert_equal(['','unnamed'], getline(1, '$'))
+ set clipboard&
+ bwipe!
+endfunc
+
func Test_nested_global()
new
call setline(1, ['nothing', 'found', 'found bad', 'bad'])
diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim
index 01fb9917e9..8e59efd22d 100644
--- a/src/nvim/testdir/test_help.vim
+++ b/src/nvim/testdir/test_help.vim
@@ -56,3 +56,49 @@ func Test_help_local_additions()
call delete('Xruntime', 'rf')
let &rtp = rtp_save
endfunc
+
+" Test for the :helptags command
+func Test_helptag_cmd()
+ call mkdir('Xdir/a/doc', 'p')
+
+ " No help file to process in the directory
+ call assert_fails('helptags Xdir', 'E151:')
+
+ call writefile([], 'Xdir/a/doc/sample.txt')
+
+ " Test for ++t argument
+ helptags ++t Xdir
+ call assert_equal(["help-tags\ttags\t1"], readfile('Xdir/tags'))
+ call delete('Xdir/tags')
+
+ " The following tests fail on FreeBSD for some reason
+ if has('unix') && !has('bsd')
+ " Read-only tags file
+ call mkdir('Xdir/doc', 'p')
+ call writefile([''], 'Xdir/doc/tags')
+ call writefile([], 'Xdir/doc/sample.txt')
+ call setfperm('Xdir/doc/tags', 'r-xr--r--')
+ call assert_fails('helptags Xdir/doc', 'E152:', getfperm('Xdir/doc/tags'))
+
+ let rtp = &rtp
+ let &rtp = 'Xdir'
+ helptags ALL
+ let &rtp = rtp
+
+ call delete('Xdir/doc/tags')
+
+ " No permission to read the help file
+ call setfperm('Xdir/a/doc/sample.txt', '-w-------')
+ call assert_fails('helptags Xdir', 'E153:', getfperm('Xdir/a/doc/sample.txt'))
+ call delete('Xdir/a/doc/sample.txt')
+ call delete('Xdir/tags')
+ endif
+
+ " Duplicate tags in the help file
+ call writefile(['*tag1*', '*tag1*', '*tag2*'], 'Xdir/a/doc/sample.txt')
+ call assert_fails('helptags Xdir', 'E154:')
+
+ call delete('Xdir', 'rf')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 30239a90c2..d0a8f342c9 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -89,6 +89,65 @@ func Test_echoerr()
call test_ignore_error('RESET')
endfunc
+func Test_mode_message_at_leaving_insert_by_ctrl_c()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'func StatusLine() abort',
+ \ ' return ""',
+ \ 'endfunc',
+ \ 'set statusline=%!StatusLine()',
+ \ 'set laststatus=2',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<C-C>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
+func Test_mode_message_at_leaving_insert_with_esc_mapped()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'set laststatus=2',
+ \ 'inoremap <Esc> <Esc>00',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<Esc>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
func Test_echospace()
set noruler noshowcmd laststatus=1
call assert_equal(&columns - 1, v:echospace)
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 215065f941..8ef8bbc23a 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -128,6 +128,7 @@ func Test_mksession()
call delete('Xtest_mks.out')
call delete(tmpfile)
let &wrap = wrap_save
+ set sessionoptions&
endfunc
func Test_mksession_winheight()
@@ -154,7 +155,7 @@ func Test_mksession_rtp()
return
endif
new
- set sessionoptions+=options
+ set sessionoptions&vi
let _rtp=&rtp
" Make a real long (invalid) runtimepath value,
" that should exceed PATH_MAX (hopefully)
@@ -174,6 +175,7 @@ func Test_mksession_rtp()
call assert_equal(expected, li)
call delete('Xtest_mks.out')
+ set sessionoptions&
endfunc
" Verify that arglist is stored correctly to the session file.
@@ -218,6 +220,25 @@ func Test_mksession_one_buffer_two_windows()
call delete('Xtest_mks.out')
endfunc
+if has('extra_search')
+
+func Test_mksession_hlsearch()
+ set sessionoptions&vi
+ set hlsearch
+ mksession! Xtest_mks.out
+ nohlsearch
+ source Xtest_mks.out
+ call assert_equal(1, v:hlsearch, 'session should restore search highlighting state')
+ nohlsearch
+ mksession! Xtest_mks.out
+ source Xtest_mks.out
+ call assert_equal(0, v:hlsearch, 'session should restore search highlighting state')
+ set sessionoptions&
+ call delete('Xtest_mks.out')
+endfunc
+
+endif
+
" Test :mkview with a file argument.
func Test_mkview_file()
" Create a view with line number and a fold.
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index ad6d325510..7a846e5ea0 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -222,6 +222,21 @@ func Test_normal05_formatexpr_setopt()
set formatexpr=
endfunc
+" When 'formatexpr' returns non-zero, internal formatting is used.
+func Test_normal_formatexpr_returns_nonzero()
+ new
+ call setline(1, ['one', 'two'])
+ func! Format()
+ return 1
+ endfunc
+ setlocal formatexpr=Format()
+ normal VGgq
+ call assert_equal(['one two'], getline(1, '$'))
+ setlocal formatexpr=
+ delfunc Format
+ close!
+endfunc
+
func Test_normal06_formatprg()
" basic test for formatprg
" only test on non windows platform
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 5323b1acf3..563dbd90d9 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -3762,6 +3762,30 @@ func Test_qftitle()
call setqflist([], 'r', {'items' : [{'filename' : 'a.c', 'lnum' : 10}]})
call assert_equal('Errors', w:quickfix_title)
cclose
+
+ " Switching to another quickfix list in one tab page should update the
+ " quickfix window title and statusline in all the other tab pages also
+ call setqflist([], 'f')
+ %bw!
+ cgetexpr ['file_one:1:1: error in the first quickfix list']
+ call setqflist([], 'a', {'title': 'first quickfix list'})
+ cgetexpr ['file_two:2:1: error in the second quickfix list']
+ call setqflist([], 'a', {'title': 'second quickfix list'})
+ copen
+ wincmd t
+ tabnew two
+ copen
+ wincmd t
+ colder
+ call assert_equal('first quickfix list', gettabwinvar(1, 2, 'quickfix_title'))
+ call assert_equal('first quickfix list', gettabwinvar(2, 2, 'quickfix_title'))
+ call assert_equal(1, tabpagewinnr(1))
+ call assert_equal(1, tabpagewinnr(2))
+ tabnew
+ call setqflist([], 'a', {'title': 'new quickfix title'})
+ call assert_equal('new quickfix title', gettabwinvar(1, 2, 'quickfix_title'))
+ call assert_equal('new quickfix title', gettabwinvar(2, 2, 'quickfix_title'))
+ %bw!
endfunc
func Test_lbuffer_with_bwipe()
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index 4466ad436a..513780938e 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -533,4 +533,15 @@ func Test_search_with_end_offset()
close!
endfunc
+" Check that "^" matches even when the line starts with a combining char
+func Test_match_start_of_line_combining()
+ new
+ call setline(1, ['', "\u05ae", ''])
+ exe "normal gg/^\<CR>"
+ call assert_equal(2, getcurpos()[1])
+ bwipe!
+endfunc
+
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 19a7c6c9d0..8d2a768ba0 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -2,6 +2,9 @@
" Tests for register operations
"
+source check.vim
+source view_util.vim
+
" This test must be executed first to check for empty and unset registers.
func Test_aaa_empty_reg_test()
call assert_fails('normal @@', 'E748:')
@@ -52,31 +55,44 @@ func Test_display_registers()
let b = execute('registers')
call assert_equal(a, b)
- call assert_match('^\n--- Registers ---\n'
- \ . '"" a\n'
- \ . '"0 ba\n'
- \ . '"1 b\n'
- \ . '"a b\n'
+ call assert_match('^\nType Name Content\n'
+ \ . ' c "" a\n'
+ \ . ' c "0 ba\n'
+ \ . ' c "1 b\n'
+ \ . ' c "a b\n'
\ . '.*'
- \ . '"- a\n'
+ \ . ' c "- a\n'
\ . '.*'
- \ . '": ls\n'
- \ . '"% file2\n'
- \ . '"# file1\n'
- \ . '"/ bar\n'
- \ . '"= 2\*4', a)
+ \ . ' c ": ls\n'
+ \ . ' c "% file2\n'
+ \ . ' c "# file1\n'
+ \ . ' c "/ bar\n'
+ \ . ' c "= 2\*4', a)
let a = execute('registers a')
- call assert_match('^\n--- Registers ---\n'
- \ . '"a b', a)
+ call assert_match('^\nType Name Content\n'
+ \ . ' c "a b', a)
let a = execute('registers :')
- call assert_match('^\n--- Registers ---\n'
- \ . '": ls', a)
+ call assert_match('^\nType Name Content\n'
+ \ . ' c ": ls', a)
bwipe!
endfunc
+func Test_recording_status_in_ex_line()
+ norm qx
+ redraw!
+ call assert_equal('recording @x', Screenline(&lines))
+ set shortmess=q
+ redraw!
+ call assert_equal('recording', Screenline(&lines))
+ set shortmess&
+ norm q
+ redraw!
+ call assert_equal('', Screenline(&lines))
+endfunc
+
" Check that replaying a typed sequence does not use an Esc and following
" characters as an escape sequence.
func Test_recording_esc_sequence()
@@ -254,4 +270,15 @@ func Test_ve_blockpaste()
bwipe!
endfunc
+func Test_insert_small_delete()
+ new
+ call setline(1, ['foo foobar bar'])
+ call cursor(1,1)
+ exe ":norm! ciw'\<C-R>-'"
+ call assert_equal("'foo' foobar bar", getline(1))
+ exe ":norm! w.w."
+ call assert_equal("'foo' 'foobar' 'bar'", getline(1))
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_signals.vim b/src/nvim/testdir/test_signals.vim
new file mode 100644
index 0000000000..338c0d79ff
--- /dev/null
+++ b/src/nvim/testdir/test_signals.vim
@@ -0,0 +1,140 @@
+" Test signal handling.
+
+source check.vim
+source term_util.vim
+
+CheckUnix
+
+source shared.vim
+
+" Check whether a signal is available on this system.
+func HasSignal(signal)
+ let signals = system('kill -l')
+ return signals =~# '\<' .. a:signal .. '\>'
+endfunc
+
+" Test signal WINCH (window resize signal)
+func Test_signal_WINCH()
+ throw 'skipped: Nvim cannot avoid terminal resize'
+ if has('gui_running') || !HasSignal('WINCH')
+ return
+ endif
+
+ " We do not actually want to change the size of the terminal.
+ let old_WS = ''
+ if exists('&t_WS')
+ let old_WS = &t_WS
+ let &t_WS = ''
+ endif
+
+ let old_lines = &lines
+ let old_columns = &columns
+ let new_lines = &lines - 2
+ let new_columns = &columns - 2
+
+ exe 'set lines=' .. new_lines
+ exe 'set columns=' .. new_columns
+ call assert_equal(new_lines, &lines)
+ call assert_equal(new_columns, &columns)
+
+ " Send signal and wait for signal to be processed.
+ " 'lines' and 'columns' should have been restored
+ " after handing signal WINCH.
+ exe 'silent !kill -s WINCH ' .. getpid()
+ call WaitForAssert({-> assert_equal(old_lines, &lines)})
+ call assert_equal(old_columns, &columns)
+
+ if old_WS != ''
+ let &t_WS = old_WS
+ endif
+endfunc
+
+" Test signal PWR, which should update the swap file.
+func Test_signal_PWR()
+ if !HasSignal('PWR')
+ return
+ endif
+
+ " Set a very large 'updatetime' and 'updatecount', so that we can be sure
+ " that swap file is updated as a result of sending PWR signal, and not
+ " because of exceeding 'updatetime' or 'updatecount' when changing buffer.
+ set updatetime=100000 updatecount=100000
+ new Xtest_signal_PWR
+ let swap_name = swapname('%')
+ call setline(1, '123')
+ preserve
+ let swap_content = readfile(swap_name, 'b')
+
+ " Update the buffer and check that the swap file is not yet updated,
+ " since we set 'updatetime' and 'updatecount' to large values.
+ call setline(1, 'abc')
+ call assert_equal(swap_content, readfile(swap_name, 'b'))
+
+ " Sending PWR signal should update the swap file.
+ exe 'silent !kill -s PWR ' .. getpid()
+ call WaitForAssert({-> assert_notequal(swap_content, readfile(swap_name, 'b'))})
+
+ bwipe!
+ set updatetime& updatecount&
+endfunc
+
+" Test a deadly signal.
+"
+" There are several deadly signals: SISEGV, SIBUS, SIGTERM...
+" Test uses signal SIGTERM as it does not create a core
+" dump file unlike SIGSEGV, SIGBUS, etc. See "man 7 signals.
+"
+" Vim should exit with a deadly signal and unsaved changes
+" should be recoverable from the swap file preserved as a
+" result of the deadly signal handler.
+func Test_deadly_signal_TERM()
+ if !HasSignal('TERM')
+ throw 'Skipped: TERM signal not supported'
+ endif
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot run vim in terminal'
+ endif
+ let cmd = GetVimCommand()
+ if cmd =~ 'valgrind'
+ throw 'Skipped: cannot test signal TERM with valgrind'
+ endif
+
+ " If test fails once, it can leave temporary files and trying to rerun
+ " the test would then fail again if they are not deleted first.
+ call delete('.Xsig_TERM.swp')
+ call delete('XsetupAucmd')
+ call delete('XautoOut')
+ let lines =<< trim END
+ au VimLeave * call writefile(["VimLeave triggered"], "XautoOut", "as")
+ au VimLeavePre * call writefile(["VimLeavePre triggered"], "XautoOut", "as")
+ END
+ call writefile(lines, 'XsetupAucmd')
+
+ let buf = RunVimInTerminal('-S XsetupAucmd Xsig_TERM', {'rows': 6})
+ let pid_vim = term_getjob(buf)->job_info().process
+
+ call term_sendkeys(buf, ":call setline(1, 'foo')\n")
+ call WaitForAssert({-> assert_equal('foo', term_getline(buf, 1))})
+
+ call assert_false(filereadable('Xsig_TERM'))
+ exe 'silent !kill -s TERM ' .. pid_vim
+ call WaitForAssert({-> assert_true(filereadable('.Xsig_TERM.swp'))})
+
+ " Don't call StopVimInTerminal() as it expects job to be still running.
+ call WaitForAssert({-> assert_equal("finished", term_getstatus(buf))})
+
+ new
+ silent recover .Xsig_TERM.swp
+ call assert_equal(['foo'], getline(1, '$'))
+
+ let result = readfile('XautoOut')
+ call assert_match('VimLeavePre triggered', result[0])
+ call assert_match('VimLeave triggered', result[1])
+
+ %bwipe!
+ call delete('.Xsig_TERM.swp')
+ call delete('XsetupAucmd')
+ call delete('XautoOut')
+endfunc
+
+" vim: ts=8 sw=2 sts=2 tw=80 fdm=marker
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index 1e6c311374..4bbd722fdb 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -122,9 +122,9 @@ func Test_sign()
call assert_fails("sign define Sign4 text=a\e linehl=Comment", 'E239:')
call assert_fails("sign define Sign4 text=\ea linehl=Comment", 'E239:')
- " Only 1 or 2 character text is allowed
+ " Only 0, 1 or 2 character text is allowed
call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:')
- call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:')
+ " call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:')
call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:')
" define sign with whitespace
@@ -306,7 +306,7 @@ func Test_sign_invalid_commands()
call assert_fails('sign jump 1 name=', 'E474:')
call assert_fails('sign jump 1 name=Sign1', 'E474:')
call assert_fails('sign jump 1 line=100', '474:')
- call assert_fails('sign define Sign2 text=', 'E239:')
+ " call assert_fails('sign define Sign2 text=', 'E239:')
" Non-numeric identifier for :sign place
call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr(''),
\ 'E474:')
@@ -415,7 +415,7 @@ func Test_sign_funcs()
" Tests for invalid arguments to sign_define()
call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:')
- call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:')
+ " call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:')
call assert_fails('call sign_define([])', 'E730:')
call assert_fails('call sign_define("sign6", [])', 'E715:')
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 6214975ef5..e7f332bc4c 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -686,3 +686,53 @@ func Test_v_argv()
call assert_true(idx > 2)
call assert_equal(['arg1', '--cmd', 'echo v:argv', '--cmd', 'q'']'], list[idx:])
endfunc
+
+" Test the '-T' argument which sets the 'term' option.
+func Test_T_arg()
+ throw 'skipped: Nvim does not support "-T" argument'
+ CheckNotGui
+ let after =<< trim [CODE]
+ call writefile([&term], "Xtest_T_arg")
+ qall
+ [CODE]
+
+ for t in ['builtin_dumb', 'builtin_ansi']
+ if RunVim([], after, '-T ' .. t)
+ let lines = readfile('Xtest_T_arg')
+ call assert_equal([t], lines)
+ endif
+ endfor
+
+ call delete('Xtest_T_arg')
+endfunc
+
+" Test the '-x' argument to read/write encrypted files.
+func Test_x_arg()
+ CheckRunVimInTerminal
+ CheckFeature cryptv
+
+ " Create an encrypted file Xtest_x_arg.
+ let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0})
+ call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))})
+ call term_sendkeys(buf, "foo\n")
+ call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))})
+ call term_sendkeys(buf, "foo\n")
+ call WaitForAssert({-> assert_match(' All$', term_getline(buf, 10))})
+ call term_sendkeys(buf, "itest\<Esc>:w\<Enter>")
+ call WaitForAssert({-> assert_match('"Xtest_x_arg" \[New\]\[blowfish2\] 1L, 5B written',
+ \ term_getline(buf, 10))})
+ call StopVimInTerminal(buf)
+
+ " Read the encrypted file and check that it contains the expected content "test"
+ let buf = RunVimInTerminal('-n -x Xtest_x_arg', #{rows: 10, wait_for_ruler: 0})
+ call WaitForAssert({-> assert_match('^Enter encryption key: ', term_getline(buf, 10))})
+ call term_sendkeys(buf, "foo\n")
+ call WaitForAssert({-> assert_match('^Enter same key again: ', term_getline(buf, 10))})
+ call term_sendkeys(buf, "foo\n")
+ call WaitForAssert({-> assert_match('^test', term_getline(buf, 1))})
+ call StopVimInTerminal(buf)
+
+ call delete('Xtest_x_arg')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index bc7c69d920..2b6a89647e 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -1,6 +1,7 @@
" Tests for tabpage
source screendump.vim
+source check.vim
function Test_tabpage()
bw!
@@ -222,6 +223,34 @@ function Test_tabpage_with_autocmd()
1tabonly!
endfunction
+" Test autocommands on tab drop
+function Test_tabpage_with_autocmd_tab_drop()
+ augroup TestTabpageGroup
+ au!
+ autocmd TabEnter * call add(s:li, 'TabEnter')
+ autocmd WinEnter * call add(s:li, 'WinEnter')
+ autocmd BufEnter * call add(s:li, 'BufEnter')
+ autocmd TabLeave * call add(s:li, 'TabLeave')
+ autocmd WinLeave * call add(s:li, 'WinLeave')
+ autocmd BufLeave * call add(s:li, 'BufLeave')
+ augroup END
+
+ let s:li = []
+ tab drop test1
+ call assert_equal(['BufLeave', 'BufEnter'], s:li)
+
+ let s:li = []
+ tab drop test2 test3
+ call assert_equal([
+ \ 'TabLeave', 'TabEnter', 'TabLeave', 'TabEnter',
+ \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter',
+ \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li)
+
+ autocmd! TestTabpageGroup
+ augroup! TestTabpageGroup
+ 1tabonly!
+endfunction
+
function Test_tabpage_with_tab_modifier()
for n in range(4)
tabedit
@@ -579,4 +608,82 @@ func Test_tabpage_cmdheight()
call delete('XTest_tabpage_cmdheight')
endfunc
+" Return the terminal key code for selecting a tab page from the tabline. This
+" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
+" KS_FILLER (0x58) and then the tab page number.
+func TabLineSelectPageCode(tabnr)
+ return "\x9b\xf0\x58" .. nr2char(a:tabnr)
+endfunc
+
+" Return the terminal key code for opening a new tabpage from the tabpage
+" menu. This sequence consists of the following codes: a CSI (0x9b),
+" KS_TABMENU (0xef), KS_FILLER (0x58), the tab page number and
+" TABLINE_MENU_NEW (2).
+func TabMenuNewItemCode(tabnr)
+ return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(2)
+endfunc
+
+" Return the terminal key code for closing a tabpage from the tabpage menu.
+" This sequence consists of the following codes: a CSI (0x9b), KS_TABMENU
+" (0xef), KS_FILLER (0x58), the tab page number and TABLINE_MENU_CLOSE (1).
+func TabMenuCloseItemCode(tabnr)
+ return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(1)
+endfunc
+
+" Test for using the tabpage menu from the insert and normal modes
+func Test_tabline_tabmenu()
+ " only works in GUI
+ CheckGui
+
+ %bw!
+ tabnew
+ tabnew
+ call assert_equal(3, tabpagenr())
+
+ " go to tab page 2 in normal mode
+ call feedkeys(TabLineSelectPageCode(2), "Lx!")
+ call assert_equal(2, tabpagenr())
+
+ " close tab page 3 in normal mode
+ call feedkeys(TabMenuCloseItemCode(3), "Lx!")
+ call assert_equal(2, tabpagenr('$'))
+ call assert_equal(2, tabpagenr())
+
+ " open new tab page before tab page 1 in normal mode
+ call feedkeys(TabMenuNewItemCode(1), "Lx!")
+ call assert_equal(1, tabpagenr())
+ call assert_equal(3, tabpagenr('$'))
+
+ " go to tab page 2 in operator-pending mode (should beep)
+ call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")')
+
+ " open new tab page before tab page 1 in operator-pending mode (should beep)
+ call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")')
+
+ " open new tab page after tab page 3 in normal mode
+ call feedkeys(TabMenuNewItemCode(4), "Lx!")
+ call assert_equal(4, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
+
+ " go to tab page 2 in insert mode
+ call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!")
+ call assert_equal(2, tabpagenr())
+
+ " close tab page 2 in insert mode
+ call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!")
+ call assert_equal(3, tabpagenr('$'))
+
+ " open new tab page before tab page 3 in insert mode
+ call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!")
+ call assert_equal(3, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
+
+ " open new tab page after tab page 4 in insert mode
+ call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!")
+ call assert_equal(5, tabpagenr())
+ call assert_equal(5, tabpagenr('$'))
+
+ %bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 2223be952c..4af52b536c 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -424,6 +424,15 @@ func Test_format_align()
\ ], getline(1, '$'))
enew!
+ " align text with 'wrapmargin'
+ 50vnew
+ call setline(1, ['Vim'])
+ setl textwidth=0
+ setl wrapmargin=30
+ right
+ call assert_equal("\t\t Vim", getline(1))
+ q!
+
set tw&
endfunc
@@ -931,4 +940,217 @@ func Test_substitute()
call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
endfunc
+" Test for 'a' and 'w' flags in 'formatoptions'
+func Test_fo_a_w()
+ new
+ setlocal fo+=aw tw=10
+ call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt')
+ call assert_equal(['abc abcde ', 'a abc'], getline(1, '$'))
+
+ " Test for 'a', 'w' and '1' options.
+ setlocal textwidth=0
+ setlocal fo=1aw
+ %d
+ call setline(1, '. foo')
+ normal 72ig
+ call feedkeys('a uu uu uu', 'xt')
+ call assert_equal('g uu uu ', getline(1)[-8:])
+ call assert_equal(['uu. foo'], getline(2, '$'))
+
+ " using backspace or "x" triggers reformat
+ call setline(1, ['1 2 3 4 5 ', '6 7 8 9'])
+ set tw=10
+ set fo=taw
+ set bs=indent,eol,start
+ exe "normal 1G4la\<BS>\<BS>\<Esc>"
+ call assert_equal(['1 2 4 5 6 ', '7 8 9'], getline(1, 2))
+ exe "normal f4xx"
+ call assert_equal(['1 2 5 6 7 ', '8 9'], getline(1, 2))
+
+ set tw=0
+ set fo&
+ %bw!
+endfunc
+
+" Test for formatting lines using gq in visual mode
+func Test_visual_gq_format()
+ new
+ call setline(1, ['one two three four', 'five six', 'one two'])
+ setl textwidth=10
+ call feedkeys('ggv$jj', 'xt')
+ redraw!
+ normal gq
+ %d
+ call setline(1, ['one two three four', 'five six', 'one two'])
+ normal G$
+ call feedkeys('v0kk', 'xt')
+ redraw!
+ normal gq
+ setl textwidth&
+ close!
+endfunc
+
+" Test for 'n' flag in 'formatoptions' to format numbered lists
+func Test_fo_n()
+ new
+ setlocal autoindent
+ setlocal textwidth=12
+ setlocal fo=n
+ call setline(1, [' 1) one two three four', ' 2) two'])
+ normal gggqG
+ call assert_equal([' 1) one two', ' three', ' four', ' 2) two'],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" Test for 'formatlistpat' option
+func Test_formatlistpat()
+ new
+ setlocal autoindent
+ setlocal textwidth=10
+ setlocal fo=n
+ setlocal formatlistpat=^\\s*-\\s*
+ call setline(1, [' - one two three', ' - two'])
+ normal gggqG
+ call assert_equal([' - one', ' two', ' three', ' - two'],
+ \ getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'b' and 'v' flags in 'formatoptions'
+" Text should wrap only if a space character is inserted at or before
+" 'textwidth'
+func Test_fo_b()
+ new
+ setlocal textwidth=20
+
+ setlocal formatoptions=t
+ call setline(1, 'one two three four')
+ call feedkeys('Amore', 'xt')
+ call assert_equal(['one two three', 'fourmore'], getline(1, '$'))
+
+ setlocal formatoptions=bt
+ %d
+ call setline(1, 'one two three four')
+ call feedkeys('Amore five', 'xt')
+ call assert_equal(['one two three fourmore five'], getline(1, '$'))
+
+ setlocal formatoptions=bt
+ %d
+ call setline(1, 'one two three four')
+ call feedkeys('A five', 'xt')
+ call assert_equal(['one two three four', 'five'], getline(1, '$'))
+
+ setlocal formatoptions=vt
+ %d
+ call setline(1, 'one two three four')
+ call feedkeys('Amore five', 'xt')
+ call assert_equal(['one two three fourmore', 'five'], getline(1, '$'))
+
+ close!
+endfunc
+
+" Test for the '1' flag in 'formatoptions'. Don't wrap text after a one letter
+" word.
+func Test_fo_1()
+ new
+ setlocal textwidth=20
+
+ setlocal formatoptions=t
+ call setline(1, 'one two three four')
+ call feedkeys('A a bird', 'xt')
+ call assert_equal(['one two three four a', 'bird'], getline(1, '$'))
+
+ %d
+ setlocal formatoptions=t1
+ call setline(1, 'one two three four')
+ call feedkeys('A a bird', 'xt')
+ call assert_equal(['one two three four', 'a bird'], getline(1, '$'))
+
+ close!
+endfunc
+
+" Test for 'l' flag in 'formatoptions'. When starting insert mode, if a line
+" is longer than 'textwidth', then it is not broken.
+func Test_fo_l()
+ new
+ setlocal textwidth=20
+
+ setlocal formatoptions=t
+ call setline(1, 'one two three four five')
+ call feedkeys('A six', 'xt')
+ call assert_equal(['one two three four', 'five six'], getline(1, '$'))
+
+ %d
+ setlocal formatoptions=tl
+ call setline(1, 'one two three four five')
+ call feedkeys('A six', 'xt')
+ call assert_equal(['one two three four five six'], getline(1, '$'))
+
+ close!
+endfunc
+
+" Test for the '2' flag in 'formatoptions'
+func Test_fo_2()
+ new
+ setlocal autoindent
+ setlocal formatoptions=t2
+ setlocal textwidth=30
+ call setline(1, ["\tfirst line of a paragraph.",
+ \ "second line of the same paragraph.",
+ \ "third line."])
+ normal gggqG
+ call assert_equal(["\tfirst line of a",
+ \ "paragraph. second line of the",
+ \ "same paragraph. third line."], getline(1, '$'))
+ close!
+endfunc
+
+" Test for formatting lines where only the first line has a comment.
+func Test_fo_gq_with_firstline_comment()
+ new
+ setlocal formatoptions=tcq
+ call setline(1, ['- one two', 'three'])
+ normal gggqG
+ call assert_equal(['- one two three'], getline(1, '$'))
+
+ %d
+ call setline(1, ['- one', '- two'])
+ normal gggqG
+ call assert_equal(['- one', '- two'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for trying to join a comment line with a non-comment line
+func Test_join_comments()
+ new
+ call setline(1, ['one', '/* two */', 'three'])
+ normal gggqG
+ call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for using 'a' in 'formatoptions' with comments
+func Test_autoformat_comments()
+ new
+ setlocal formatoptions+=a
+ call feedkeys("a- one\n- two\n", 'xt')
+ call assert_equal(['- one', '- two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("a\none\n", 'xt')
+ call assert_equal(['', 'one', ''], getline(1, '$'))
+
+ setlocal formatoptions+=aw
+ %d
+ call feedkeys("aone \ntwo\n", 'xt')
+ call assert_equal(['one two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("aone\ntwo\n", 'xt')
+ call assert_equal(['one', 'two', ''], getline(1, '$'))
+
+ close!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 900f2acd81..01f20cf29a 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -208,6 +208,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Size in bytes of the hash used in the undo file.
#define UNDO_HASH_SIZE 32
+#define CLEAR_POINTER(ptr) memset((ptr), 0, sizeof(*(ptr)))
// defines to avoid typecasts from (char_u *) to (char *) and back
// (vim_strchr() is now in strings.c)
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9eb16e67ec..2dcce2d8cb 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -641,7 +641,7 @@ void win_set_minimal_style(win_T *wp)
wp->w_p_scl = (char_u *)xstrdup("auto");
}
- // foldcolumn: use 'auto'
+ // foldcolumn: use '0'
if (wp->w_p_fdc[0] != '0') {
xfree(wp->w_p_fdc);
wp->w_p_fdc = (char_u *)xstrdup("0");
@@ -700,9 +700,10 @@ int win_fdccol_count(win_T *wp)
const char *fdc = (const char *)wp->w_p_fdc;
// auto:<NUM>
- if (strncmp(fdc, "auto:", 5) == 0) {
+ if (strncmp(fdc, "auto", 4) == 0) {
+ const int fdccol = fdc[4] == ':' ? fdc[5] - '0' : 1;
int needed_fdccols = getDeepestNesting(wp);
- return MIN(fdc[5] - '0', needed_fdccols);
+ return MIN(fdccol, needed_fdccols);
} else {
return fdc[0] - '0';
}
@@ -1636,6 +1637,19 @@ bool win_valid(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
return false;
}
+// Find window "handle" in the current tab page.
+// Return NULL if not found.
+win_T *win_find_by_handle(handle_T handle)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->handle == handle) {
+ return wp;
+ }
+ }
+ return NULL;
+}
+
/// Check if "win" is a pointer to an existing window in any tabpage.
///
/// @param win window to check
@@ -4526,7 +4540,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
// Might need to scroll the old window before switching, e.g., when the
// cursor was moved.
- update_topline();
+ update_topline(curwin);
// may have to copy the buffer options when 'cpo' contains 'S'
if (wp->w_buffer != curbuf) {
@@ -4999,7 +5013,10 @@ void win_size_save(garray_T *gap)
{
ga_init(gap, (int)sizeof(int), 1);
- ga_grow(gap, win_count() * 2);
+ ga_grow(gap, win_count() * 2 + 1);
+ // first entry is value of 'lines'
+ ((int *)gap->ga_data)[gap->ga_len++] = Rows;
+
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
((int *)gap->ga_data)[gap->ga_len++] =
wp->w_width + wp->w_vsep_width;
@@ -5007,18 +5024,18 @@ void win_size_save(garray_T *gap)
}
}
-/*
- * Restore window sizes, but only if the number of windows is still the same.
- * Does not free the growarray.
- */
+// Restore window sizes, but only if the number of windows is still the same
+// and 'lines' didn't change.
+// Does not free the growarray.
void win_size_restore(garray_T *gap)
+ FUNC_ATTR_NONNULL_ALL
{
- if (win_count() * 2 == gap->ga_len) {
- /* The order matters, because frames contain other frames, but it's
- * difficult to get right. The easy way out is to do it twice. */
- for (int j = 0; j < 2; ++j)
- {
- int i = 0;
+ if (win_count() * 2 + 1 == gap->ga_len
+ && ((int *)gap->ga_data)[0] == Rows) {
+ // The order matters, because frames contain other frames, but it's
+ // difficult to get right. The easy way out is to do it twice.
+ for (int j = 0; j < 2; j++) {
+ int i = 1;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
int width = ((int *)gap->ga_data)[i++];
int height = ((int *)gap->ga_data)[i++];
@@ -5859,10 +5876,10 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
if (wp == curwin) {
- if (get_scrolloff_value()) {
- update_topline();
+ if (get_scrolloff_value(wp)) {
+ update_topline(wp);
}
- curs_columns(false); // validate w_wrow
+ curs_columns(wp, false); // validate w_wrow
}
if (prev_height > 0) {
wp->w_prev_fraction_row = wp->w_wrow;
@@ -5918,8 +5935,8 @@ void win_set_inner_size(win_T *wp)
changed_line_abv_curs_win(wp);
invalidate_botline_win(wp);
if (wp == curwin) {
- update_topline();
- curs_columns(true); // validate w_wrow
+ update_topline(wp);
+ curs_columns(wp, true); // validate w_wrow
}
redraw_later(wp, NOT_VALID);
}