aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt41
-rw-r--r--src/nvim/api/buffer.c181
-rw-r--r--src/nvim/api/vim.c24
-rw-r--r--src/nvim/autocmd.c4
-rw-r--r--src/nvim/buffer.c88
-rw-r--r--src/nvim/buffer.h3
-rw-r--r--src/nvim/buffer_defs.h17
-rw-r--r--src/nvim/buffer_updates.c32
-rw-r--r--src/nvim/channel.c14
-rw-r--r--src/nvim/charset.c2
-rw-r--r--src/nvim/context.c2
-rw-r--r--src/nvim/decoration.c91
-rw-r--r--src/nvim/decoration.h8
-rw-r--r--src/nvim/diff.c23
-rw-r--r--src/nvim/edit.c17
-rw-r--r--src/nvim/eval.c58
-rw-r--r--src/nvim/eval.h17
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c138
-rw-r--r--src/nvim/eval/typval.h1
-rw-r--r--src/nvim/eval/userfunc.c91
-rw-r--r--src/nvim/event/process.c3
-rw-r--r--src/nvim/ex_cmds.c100
-rw-r--r--src/nvim/ex_cmds.h2
-rw-r--r--src/nvim/ex_cmds.lua10
-rw-r--r--src/nvim/ex_cmds2.c107
-rw-r--r--src/nvim/ex_cmds_defs.h4
-rw-r--r--src/nvim/ex_docmd.c39
-rw-r--r--src/nvim/ex_getln.c14
-rw-r--r--src/nvim/ex_getln.h1
-rw-r--r--src/nvim/ex_session.c16
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/fold.c7
-rw-r--r--src/nvim/globals.h16
-rw-r--r--src/nvim/if_cscope.c2
-rw-r--r--src/nvim/log.h9
-rw-r--r--src/nvim/lua/vim.lua27
-rw-r--r--src/nvim/main.c6
-rw-r--r--src/nvim/memline.c2
-rw-r--r--src/nvim/memline_defs.h8
-rw-r--r--src/nvim/menu.c8
-rw-r--r--src/nvim/misc1.c2
-rw-r--r--src/nvim/mouse.c71
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/normal.c49
-rw-r--r--src/nvim/ops.c23
-rw-r--r--src/nvim/option.c24
-rw-r--r--src/nvim/option_defs.h13
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/os/input.c20
-rw-r--r--src/nvim/os/pty_process_unix.c2
-rw-r--r--src/nvim/os/pty_process_unix.h1
-rw-r--r--src/nvim/os/pty_process_win.h1
-rw-r--r--src/nvim/path.c2
-rw-r--r--src/nvim/pos.h7
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/screen.c146
-rw-r--r--src/nvim/search.c10
-rw-r--r--src/nvim/sign_defs.h4
-rw-r--r--src/nvim/spell.c11
-rw-r--r--src/nvim/spellfile.c3
-rw-r--r--src/nvim/state.c28
-rw-r--r--src/nvim/syntax.c8
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c59
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_arglist.vim18
-rw-r--r--src/nvim/testdir/test_autocmd.vim11
-rw-r--r--src/nvim/testdir/test_breakindent.vim9
-rw-r--r--src/nvim/testdir/test_buffer.vim33
-rw-r--r--src/nvim/testdir/test_bufline.vim11
-rw-r--r--src/nvim/testdir/test_cmdline.vim1
-rw-r--r--src/nvim/testdir/test_command_count.vim4
-rw-r--r--src/nvim/testdir/test_conceal.vim282
-rw-r--r--src/nvim/testdir/test_diffmode.vim84
-rw-r--r--src/nvim/testdir/test_digraph.vim2
-rw-r--r--src/nvim/testdir/test_excmd.vim8
-rw-r--r--src/nvim/testdir/test_exit.vim29
-rw-r--r--src/nvim/testdir/test_expr.vim6
-rw-r--r--src/nvim/testdir/test_filetype.vim2
-rw-r--r--src/nvim/testdir/test_functions.vim37
-rw-r--r--src/nvim/testdir/test_matchadd_conceal.vim91
-rw-r--r--src/nvim/testdir/test_messages.vim2
-rw-r--r--src/nvim/testdir/test_mksession.vim115
-rw-r--r--src/nvim/testdir/test_options.vim46
-rw-r--r--src/nvim/testdir/test_statusline.vim16
-rw-r--r--src/nvim/testdir/test_user_func.vim53
-rw-r--r--src/nvim/testdir/test_version.vim14
-rw-r--r--src/nvim/tui/input.c4
-rw-r--r--src/nvim/undo.c128
-rw-r--r--src/nvim/undo_defs.h7
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c12
93 files changed, 2175 insertions, 596 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index db77931c16..2c9d655a15 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -284,7 +284,7 @@ foreach(sfile ${NVIM_SOURCES}
endif()
add_custom_command(
OUTPUT "${gf_c_h}" "${gf_h_h}"
- COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} ${C_FLAGS_ARRAY}
+ COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags}
COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}"
DEPENDS ${depends})
list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}")
@@ -513,8 +513,44 @@ if(WIN32)
tidy.exe
win32yank.exe
winpty-agent.exe
+ winpty.dll
xxd.exe
+ # Dependencies for neovim-qt
+ bearer/qgenericbearer.dll
+ iconengines/qsvgicon.dll
+ imageformats/qgif.dll
+ imageformats/qicns.dll
+ imageformats/qico.dll
+ imageformats/qjpeg.dll
+ imageformats/qsvg.dll
+ imageformats/qtga.dll
+ imageformats/qtiff.dll
+ imageformats/qwbmp.dll
+ imageformats/qwebp.dll
+ platforms/qwindows.dll
+ styles/qwindowsvistastyle.dll
+ translations/qt_ar.qm
+ translations/qt_bg.qm
+ translations/qt_ca.qm
+ translations/qt_cs.qm
+ translations/qt_da.qm
+ translations/qt_de.qm
+ translations/qt_en.qm
+ translations/qt_es.qm
+ translations/qt_fi.qm
+ translations/qt_fr.qm
+ translations/qt_gd.qm
+ translations/qt_he.qm
+ translations/qt_hu.qm
+ translations/qt_it.qm
+ translations/qt_ja.qm
+ translations/qt_ko.qm
+ translations/qt_lv.qm
+ translations/qt_pl.qm
+ translations/qt_ru.qm
+ translations/qt_sk.qm
+ translations/qt_uk.qm
D3Dcompiler_47.dll
libEGL.dll
libgcc_s_dw2-1.dll
@@ -522,14 +558,13 @@ if(WIN32)
libstdc++-6.dll
libwinpthread-1.dll
nvim-qt.exe
+ opengl32sw.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Network.dll
Qt5Svg.dll
Qt5Widgets.dll
- winpty.dll
- platforms/qwindows.dll
)
get_filename_component(DEP_FILE_DIR ${DEP_FILE} DIRECTORY)
set(EXTERNAL_BLOBS_SCRIPT "${EXTERNAL_BLOBS_SCRIPT}\n"
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 67f4f92bf6..2c2e8a024f 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -111,6 +111,24 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - byte count of previous contents
/// - deleted_codepoints (if `utf_sizes` is true)
/// - deleted_codeunits (if `utf_sizes` is true)
+/// - on_bytes: lua callback invoked on change.
+/// This callback receives more granular information about the
+/// change compared to on_lines.
+/// Return `true` to detach.
+/// Args:
+/// - the string "bytes"
+/// - buffer handle
+/// - b:changedtick
+/// - start row of the changed text (zero-indexed)
+/// - start column of the changed text
+/// - byte offset of the changed text (from the start of
+/// the buffer)
+/// - old end row of the changed text
+/// - old end column of the changed text
+/// - old end byte length of the changed text
+/// - new end row of the changed text
+/// - new end column of the changed text
+/// - new end byte length of the changed text
/// - on_changedtick: Lua callback invoked on changedtick
/// increment without text change. Args:
/// - the string "changedtick"
@@ -119,6 +137,10 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - on_detach: Lua callback invoked on detach. Args:
/// - the string "detach"
/// - buffer handle
+/// - on_reload: Lua callback invoked on reload. The entire buffer
+/// content should be considered changed. Args:
+/// - the string "detach"
+/// - 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')
@@ -141,50 +163,57 @@ Boolean nvim_buf_attach(uint64_t channel_id,
bool is_lua = (channel_id == LUA_INTERNAL_CALL);
BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT;
+ struct {
+ const char *name;
+ LuaRef *dest;
+ } cbs[] = {
+ { "on_lines", &cb.on_lines },
+ { "on_bytes", &cb.on_bytes },
+ { "on_changedtick", &cb.on_changedtick },
+ { "on_detach", &cb.on_detach },
+ { "on_reload", &cb.on_reload },
+ { NULL, NULL },
+ };
+
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
- if (is_lua && strequal("on_lines", k.data)) {
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation, "callback is not a function");
- goto error;
- }
- cb.on_lines = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- } else if (is_lua && strequal("on_bytes", k.data)) {
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation, "callback is not a function");
- goto error;
- }
- cb.on_bytes = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- } else if (is_lua && strequal("on_changedtick", k.data)) {
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation, "callback is not a function");
- goto error;
- }
- cb.on_changedtick = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- } else if (is_lua && strequal("on_detach", k.data)) {
- if (v->type != kObjectTypeLuaRef) {
- api_set_error(err, kErrorTypeValidation, "callback is not a function");
- goto error;
- }
- cb.on_detach = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- } else if (is_lua && strequal("utf_sizes", k.data)) {
- if (v->type != kObjectTypeBoolean) {
- api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean");
- goto error;
+ bool key_used = false;
+ if (is_lua) {
+ for (size_t j = 0; cbs[j].name; j++) {
+ if (strequal(cbs[j].name, k.data)) {
+ if (v->type != kObjectTypeLuaRef) {
+ api_set_error(err, kErrorTypeValidation,
+ "%s is not a function", cbs[j].name);
+ goto error;
+ }
+ *(cbs[j].dest) = v->data.luaref;
+ v->data.luaref = LUA_NOREF;
+ key_used = true;
+ break;
+ }
}
- 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;
+
+ if (key_used) {
+ continue;
+ } else if (strequal("utf_sizes", k.data)) {
+ if (v->type != kObjectTypeBoolean) {
+ api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean");
+ goto error;
+ }
+ cb.utf_sizes = v->data.boolean;
+ key_used = true;
+ } else if (strequal("preview", k.data)) {
+ if (v->type != kObjectTypeBoolean) {
+ api_set_error(err, kErrorTypeValidation, "preview must be boolean");
+ goto error;
+ }
+ cb.preview = v->data.boolean;
+ key_used = true;
}
- cb.preview = v->data.boolean;
- } else {
+ }
+
+ if (!key_used) {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
goto error;
}
@@ -722,7 +751,8 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer,
kExtmarkUndo);
- changed_lines((linenr_T)start_row, 0, (linenr_T)end_row, (long)extra, true);
+ changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1,
+ (long)extra, true);
// adjust cursor like an extmark ( i e it was inside last_part_len)
if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) {
@@ -1395,6 +1425,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// - hl_group : name of the highlight group used to highlight
/// this mark.
/// - virt_text : virtual text to link to this mark.
+/// - virt_text_pos : positioning of virtual text. Possible
+/// values:
+/// - "eol": right after eol character (default)
+/// - "overlay": display over the specified column, without
+/// shifting the underlying text.
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
@@ -1444,8 +1479,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
uint64_t id = 0;
int line2 = -1, hl_id = 0;
DecorPriority priority = DECOR_PRIORITY_BASE;
- colnr_T col2 = 0;
+ colnr_T col2 = -1;
VirtText virt_text = KV_INITIAL_VALUE;
+ VirtTextPos virt_text_pos = kVTEndOfLine;
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
@@ -1514,6 +1550,22 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ERROR_SET(err)) {
goto error;
}
+ } else if (strequal("virt_text_pos", k.data)) {
+ if (v->type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos is not a String");
+ goto error;
+ }
+ String str = v->data.string;
+ if (strequal("eol", str.data)) {
+ virt_text_pos = kVTEndOfLine;
+ } else if (strequal("overlay", str.data)) {
+ virt_text_pos = kVTOverlay;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos: invalid value");
+ goto error;
+ }
} else if (strequal("ephemeral", k.data)) {
ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
if (ERROR_SET(err)) {
@@ -1555,7 +1607,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
// Only error out if they try to set end_right_gravity without
// setting end_col or end_line
- if (line2 == -1 && col2 == 0 && end_gravity_set) {
+ if (line2 == -1 && col2 == -1 && end_gravity_set) {
api_set_error(err, kErrorTypeValidation,
"cannot set end_right_gravity "
"without setting end_line or end_col");
@@ -1579,30 +1631,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
col2 = 0;
}
+ Decoration *decor = NULL, tmp = { 0 };
+
+ if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) {
+ // TODO(bfredl): this is a bit sketchy. eventually we should
+ // have predefined decorations for both marks/ephemerals
+ decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor));
+ decor->hl_id = hl_id;
+ decor->virt_text = virt_text;
+ decor->priority = priority;
+ decor->virt_text_pos = virt_text_pos;
+ } else if (hl_id) {
+ decor = decor_hl(hl_id);
+ }
+
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
- int attr_id = hl_id > 0 ? syn_id2attr(hl_id) : 0;
- VirtText *vt_allocated = NULL;
- if (kv_size(virt_text)) {
- vt_allocated = xmalloc(sizeof *vt_allocated);
- *vt_allocated = virt_text;
- }
- decor_add_ephemeral(attr_id, (int)line, (colnr_T)col,
- (int)line2, (colnr_T)col2, priority, vt_allocated);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
goto error;
}
- Decoration *decor = NULL;
- if (kv_size(virt_text)) {
- decor = xcalloc(1, sizeof(*decor));
- decor->hl_id = hl_id;
- decor->virt_text = virt_text;
- } else if (hl_id) {
- decor = decor_hl(hl_id);
- decor->priority = priority;
- }
id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col,
line2, col2, decor, right_gravity,
@@ -1673,7 +1723,7 @@ Boolean nvim_buf_del_extmark(Buffer buffer,
/// @param[out] err Error details, if any
/// @return The ns_id that was used
Integer nvim_buf_add_highlight(Buffer buffer,
- Integer src_id,
+ Integer ns_id,
String hl_group,
Integer line,
Integer col_start,
@@ -1698,18 +1748,18 @@ Integer nvim_buf_add_highlight(Buffer buffer,
col_end = MAXCOL;
}
- uint64_t ns_id = src2ns(&src_id);
+ uint64_t ns = src2ns(&ns_id);
if (!(line < buf->b_ml.ml_line_count)) {
// safety check, we can't add marks outside the range
- return src_id;
+ return ns_id;
}
int hl_id = 0;
if (hl_group.size > 0) {
hl_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size);
} else {
- return src_id;
+ return ns_id;
}
int end_line = (int)line;
@@ -1718,11 +1768,11 @@ Integer nvim_buf_add_highlight(Buffer buffer,
end_line++;
}
- extmark_set(buf, ns_id, 0,
+ extmark_set(buf, ns, 0,
(int)line, (colnr_T)col_start,
end_line, (colnr_T)col_end,
decor_hl(hl_id), true, false, kExtmarkNoUndo);
- return src_id;
+ return ns_id;
}
/// Clears namespaced objects (highlights, extmarks, virtual text) from
@@ -1918,7 +1968,6 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
curwin->w_cursor.lnum += extra;
check_cursor_col();
} else if (extra < 0) {
- curwin->w_cursor.lnum = lo;
check_cursor();
} else {
check_cursor_col();
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 09895a2119..586123aac1 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -545,6 +545,26 @@ Object nvim_exec_lua(String code, Array args, Error *err)
return nlua_exec(code, args, err);
}
+/// Notify the user with a message
+///
+/// Relays the call to vim.notify . By default forwards your message in the
+/// echo area but can be overriden to trigger desktop notifications.
+///
+/// @param msg Message to display to the user
+/// @param log_level The log level
+/// @param opts Reserved for future use.
+/// @param[out] err Error details, if any
+Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ FIXED_TEMP_ARRAY(args, 3);
+ args.items[0] = STRING_OBJ(msg);
+ args.items[1] = INTEGER_OBJ(log_level);
+ args.items[2] = DICTIONARY_OBJ(opts);
+
+ return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
+}
+
/// Calls a VimL function.
///
/// @param fn Function name
@@ -1526,8 +1546,8 @@ theend:
/// - "c" |charwise| mode
/// - "l" |linewise| mode
/// - "" guess by contents, see |setreg()|
-/// @param after Insert after cursor (like |p|), or before (like |P|).
-/// @param follow Place cursor at end of inserted text.
+/// @param after If true insert after cursor (like |p|), or before (like |P|).
+/// @param follow If true place cursor at end of inserted text.
/// @param[out] err Error details, if any
void nvim_put(ArrayOf(String) lines, String type, Boolean after,
Boolean follow, Error *err)
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 140a9c6bcb..3de2e0f342 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -544,7 +544,7 @@ char_u *au_event_disable(char *what)
} else {
STRCAT(new_ei, what);
}
- set_string_option_direct((char_u *)"ei", -1, new_ei, OPT_FREE, SID_NONE);
+ set_string_option_direct("ei", -1, new_ei, OPT_FREE, SID_NONE);
xfree(new_ei);
return save_ei;
@@ -553,7 +553,7 @@ char_u *au_event_disable(char *what)
void au_event_restore(char_u *old_ei)
{
if (old_ei != NULL) {
- set_string_option_direct((char_u *)"ei", -1, old_ei, OPT_FREE, SID_NONE);
+ set_string_option_direct("ei", -1, old_ei, OPT_FREE, SID_NONE);
xfree(old_ei);
}
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ec39de14f5..c42a0e2dad 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -596,7 +596,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
// Disable buffer-updates for the current buffer.
// No need to check `unload_buf`: in that case the function returned above.
- buf_updates_unregister_all(buf);
+ buf_updates_unload(buf, false);
/*
* Remove the buffer from the list.
@@ -821,7 +821,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
XFREE_CLEAR(buf->b_start_fenc);
- buf_updates_unregister_all(buf);
+ buf_updates_unload(buf, false);
}
/*
@@ -1726,7 +1726,8 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum,
file_id_valid)) != NULL) {
xfree(ffname);
if (lnum != 0) {
- buflist_setfpos(buf, curwin, lnum, (colnr_T)0, false);
+ buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin,
+ lnum, (colnr_T)0, false);
}
if ((flags & BLN_NOOPT) == 0) {
// Copy the options now, if 'cpo' doesn't have 's' and not done already.
@@ -2308,6 +2309,10 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
*num_file = 0; // return values in case of FAIL
*file = NULL;
+ if ((options & BUF_DIFF_FILTER) && !curwin->w_p_diff) {
+ return FAIL;
+ }
+
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
if (*pat == '^') {
patc = xmalloc(STRLEN(pat) + 11);
@@ -2344,6 +2349,13 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
if (!buf->b_p_bl) { // skip unlisted buffers
continue;
}
+ if (options & BUF_DIFF_FILTER) {
+ // Skip buffers not suitable for
+ // :diffget or :diffput completion.
+ if (buf == curbuf || !diff_mode_buf(buf)) {
+ continue;
+ }
+ }
p = buflist_match(&regmatch, buf, p_wic);
if (p != NULL) {
if (round == 1) {
@@ -2486,6 +2498,7 @@ buflist_nr2name(
///
/// @param[in,out] buf Buffer for which line and column are set.
/// @param[in,out] win Window for which line and column are set.
+/// May be NULL when using :badd.
/// @param[in] lnum Line number to be set. If it is zero then only
/// options are touched.
/// @param[in] col Column number to be set.
@@ -2493,7 +2506,7 @@ buflist_nr2name(
void buflist_setfpos(buf_T *const buf, win_T *const win,
linenr_T lnum, colnr_T col,
bool copy_options)
- FUNC_ATTR_NONNULL_ALL
+ FUNC_ATTR_NONNULL_ARG(1)
{
wininfo_T *wip;
@@ -2528,7 +2541,7 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
wip->wi_fpos.lnum = lnum;
wip->wi_fpos.col = col;
}
- if (copy_options) {
+ if (copy_options && win != NULL) {
// Save the window-specific option values.
copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
wip->wi_fold_manual = win->w_fold_manual;
@@ -2566,29 +2579,39 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
return false;
}
-/*
- * Find info for the current window in buffer "buf".
- * If not found, return the info for the most recently used window.
- * When "skip_diff_buffer" is true avoid windows with 'diff' set that is in
- * another tab page.
- * Returns NULL when there isn't any info.
- */
-static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer)
+// Find info for the current window in buffer "buf".
+// If not found, return the info for the most recently used window.
+// When "need_options" is true skip entries where wi_optset is false.
+// When "skip_diff_buffer" is true avoid windows with 'diff' set that is in
+// another tab page.
+// Returns NULL when there isn't any info.
+static wininfo_T *find_wininfo(buf_T *buf, bool need_options,
+ bool skip_diff_buffer)
+ FUNC_ATTR_NONNULL_ALL
{
wininfo_T *wip;
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
if (wip->wi_win == curwin
&& (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
- )
+ && (!need_options || wip->wi_optset)) {
break;
+ }
+ }
- /* If no wininfo for curwin, use the first in the list (that doesn't have
- * 'diff' set and is in another tab page). */
+ // If no wininfo for curwin, use the first in the list (that doesn't have
+ // 'diff' set and is in another tab page).
+ // If "need_options" is true skip entries that don't have options set,
+ // unless the window is editing "buf", so we can copy from the window
+ // itself.
if (wip == NULL) {
if (skip_diff_buffer) {
for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
- if (!wininfo_other_tab_diff(wip)) {
+ if (!wininfo_other_tab_diff(wip)
+ && (!need_options
+ || wip->wi_optset
+ || (wip->wi_win != NULL
+ && wip->wi_win->w_buffer == buf))) {
break;
}
}
@@ -2607,12 +2630,10 @@ static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer)
*/
void get_winopts(buf_T *buf)
{
- wininfo_T *wip;
-
clear_winopt(&curwin->w_onebuf_opt);
clearFolding(curwin);
- wip = find_wininfo(buf, true);
+ wininfo_T *const wip = find_wininfo(buf, true, true);
if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL
&& wip->wi_win->w_buffer == buf) {
win_T *wp = wip->wi_win;
@@ -2649,7 +2670,7 @@ pos_T *buflist_findfpos(buf_T *buf)
{
static pos_T no_position = { 1, 0, 0 };
- wininfo_T *wip = find_wininfo(buf, false);
+ wininfo_T *const wip = find_wininfo(buf, false, false);
return (wip == NULL) ? &no_position : &(wip->wi_fpos);
}
@@ -3236,7 +3257,7 @@ void maketitle(void)
0, maxlen, NULL, NULL);
title_str = (char_u *)buf;
if (called_emsg) {
- set_string_option_direct((char_u *)"titlestring", -1, (char_u *)"",
+ set_string_option_direct("titlestring", -1, (char_u *)"",
OPT_FREE, SID_ERROR);
}
called_emsg |= save_called_emsg;
@@ -3346,7 +3367,7 @@ void maketitle(void)
p_iconstring, use_sandbox,
0, 0, NULL, NULL);
if (called_emsg) {
- set_string_option_direct((char_u *)"iconstring", -1,
+ set_string_option_direct("iconstring", -1,
(char_u *)"", OPT_FREE, SID_ERROR);
}
called_emsg |= save_called_emsg;
@@ -3486,7 +3507,7 @@ int build_stl_str_hl(
stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len);
- stl_tabtab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len);
+ stl_tabtab = xmalloc(sizeof(StlClickRecord) * stl_items_len);
stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
}
@@ -3940,9 +3961,9 @@ int build_stl_str_hl(
// Store the current buffer number as a string variable
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
- set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp);
+ set_internal_string_var("g:actual_curbuf", buf_tmp);
vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
- set_internal_string_var((char_u *)"g:actual_curwin", win_tmp);
+ set_internal_string_var("g:actual_curwin", win_tmp);
buf_T *const save_curbuf = curbuf;
win_T *const save_curwin = curwin;
@@ -4482,22 +4503,15 @@ int build_stl_str_hl(
int num_separators = 0;
for (int i = 0; i < itemcnt; i++) {
if (stl_items[i].type == Separate) {
+ // Create an array of the start location for each
+ // separator mark.
+ stl_separator_locations[num_separators] = i;
num_separators++;
}
}
// If we have separated groups, then we deal with it now
if (num_separators) {
- // Create an array of the start location for each
- // separator mark.
- int index = 0;
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Separate) {
- stl_separator_locations[index] = i;
- index++;
- }
- }
-
int standard_spaces = (maxwidth - width) / num_separators;
int final_spaces = (maxwidth - width) -
standard_spaces * (num_separators - 1);
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index ee3fda5f6d..ac7ead5f92 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -33,6 +33,9 @@ enum bln_values {
BLN_DUMMY = 4, // Allocating dummy buffer
BLN_NEW = 8, // create a new buffer
BLN_NOOPT = 16, // Don't copy options to existing buffer
+ // BLN_DUMMY_OK = 32, // also find an existing dummy buffer
+ // BLN_REUSE = 64, // may re-use number from buf_reuse
+ BLN_NOCURWIN = 128, // buffer is not associated with curwin
};
// Values for action argument for do_buffer()
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index cc09b7e989..a05bd6fcc7 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -296,13 +296,11 @@ typedef struct arglist {
int id; ///< id of this arglist
} alist_T;
-/*
- * For each argument remember the file name as it was given, and the buffer
- * number that contains the expanded file name (required for when ":cd" is
- * used.
- *
- * TODO: move aentry_T to another header
- */
+// For each argument remember the file name as it was given, and the buffer
+// number that contains the expanded file name (required for when ":cd" is
+// used).
+//
+// TODO(Felipe): move aentry_T to another header
typedef struct argentry {
char_u *ae_fname; // file name as specified
int ae_fnum; // buffer number with expanded file name
@@ -490,11 +488,12 @@ typedef struct {
LuaRef on_bytes;
LuaRef on_changedtick;
LuaRef on_detach;
+ LuaRef on_reload;
bool utf_sizes;
bool preview;
} BufUpdateCallbacks;
#define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, false, false }
+ LUA_NOREF, LUA_NOREF, false, false }
EXTERN int curbuf_splice_pending INIT(= 0);
@@ -1036,10 +1035,10 @@ struct matchitem {
int id; ///< match ID
int priority; ///< match priority
char_u *pattern; ///< pattern to highlight
- int hlg_id; ///< highlight group ID
regmmatch_T match; ///< regexp program for pattern
posmatch_T pos; ///< position matches
match_T hl; ///< struct for doing the actual highlighting
+ int hlg_id; ///< highlight group ID
int conceal_char; ///< cchar for Conceal highlighting
};
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 68e123896b..97562eace6 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -135,7 +135,7 @@ void buf_updates_unregister(buf_T *buf, uint64_t channelid)
}
}
-void buf_updates_unregister_all(buf_T *buf)
+void buf_updates_unload(buf_T *buf, bool can_reload)
{
size_t size = kv_size(buf->update_channels);
if (size) {
@@ -146,9 +146,20 @@ void buf_updates_unregister_all(buf_T *buf)
kv_init(buf->update_channels);
}
+ size_t j = 0;
for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) {
BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i);
- if (cb.on_detach != LUA_NOREF) {
+ LuaRef thecb = LUA_NOREF;
+
+ bool keep = false;
+ if (can_reload && cb.on_reload != LUA_NOREF) {
+ keep = true;
+ thecb = cb.on_reload;
+ } else if (cb.on_detach != LUA_NOREF) {
+ thecb = cb.on_detach;
+ }
+
+ if (thecb != LUA_NOREF) {
Array args = ARRAY_DICT_INIT;
Object items[1];
args.size = 1;
@@ -158,15 +169,24 @@ void buf_updates_unregister_all(buf_T *buf)
args.items[0] = BUFFER_OBJ(buf->handle);
textlock++;
- nlua_call_ref(cb.on_detach, "detach", args, false, NULL);
+ nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL);
textlock--;
}
- free_update_callbacks(cb);
+
+ if (keep) {
+ kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i);
+ } else {
+ free_update_callbacks(cb);
+ }
+ }
+ kv_size(buf->update_callbacks) = j;
+ if (kv_size(buf->update_callbacks) == 0) {
+ kv_destroy(buf->update_callbacks);
+ kv_init(buf->update_callbacks);
}
- kv_destroy(buf->update_callbacks);
- kv_init(buf->update_callbacks);
}
+
void buf_updates_send_changes(buf_T *buf,
linenr_T firstline,
int64_t num_added,
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 5628ede2e6..09a34ca9fe 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -292,7 +292,6 @@ static void close_cb(Stream *stream, void *data)
/// directory if `cwd` is NULL
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
/// @param[in] pty_height Height of the pty, ignored if `pty` is false
-/// @param[in] term_name `$TERM` for the pty
/// @param[in] env Nvim's configured environment is used if this is NULL,
/// otherwise defines all environment variables
/// @param[out] status_out 0 for invalid arguments, > 0 for the channel id,
@@ -304,8 +303,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
bool pty, bool rpc, bool overlapped, bool detach,
const char *cwd,
uint16_t pty_width, uint16_t pty_height,
- char *term_name, dict_T *env,
- varnumber_T *status_out)
+ dict_T *env, varnumber_T *status_out)
{
assert(cwd == NULL || os_isdir_executable(cwd));
@@ -318,7 +316,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
if (detach) {
EMSG2(_(e_invarg2), "terminal/pty job cannot be detached");
shell_free_argv(argv);
- xfree(term_name);
+ if (env) {
+ tv_dict_free(env);
+ }
channel_destroy_early(chan);
*status_out = 0;
return NULL;
@@ -330,9 +330,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
if (pty_height > 0) {
chan->stream.pty.height = pty_height;
}
- if (term_name) {
- chan->stream.pty.term_name = term_name;
- }
} else {
chan->stream.uv = libuv_process_init(&main_loop, chan);
}
@@ -362,9 +359,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
if (proc->env) {
tv_dict_free(proc->env);
}
- if (proc->type == kProcessTypePty) {
- xfree(chan->stream.pty.term_name);
- }
channel_destroy_early(chan);
*status_out = proc->status;
return NULL;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index be265e3f27..5ad1fe0dfd 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -708,7 +708,7 @@ int ptr2cells(const char_u *p)
/// @return number of character cells.
int vim_strsize(char_u *s)
{
- return vim_strnsize(s, (int)MAXCOL);
+ return vim_strnsize(s, MAXCOL);
}
/// Return the number of character cells string "s[len]" will take on the
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 1ae0510762..4162daa6ca 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -126,7 +126,7 @@ bool ctx_restore(Context *ctx, const int flags)
}
char_u *op_shada;
- get_option_value((char_u *)"shada", NULL, &op_shada, OPT_GLOBAL);
+ get_option_value("shada", NULL, &op_shada, OPT_GLOBAL);
set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL);
if (flags & kCtxRegs) {
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index f3ee42fab1..a1289f202a 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -188,20 +188,21 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
goto next_mark;
}
- int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL;
- HlRange range;
if (mark.id&MARKTREE_END_FLAG) {
- range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col,
- attr_id, decor->priority, vt, false };
+ decor_add(state, altpos.row, altpos.col, mark.row, mark.col,
+ decor, false, 0);
} else {
- range = (HlRange){ mark.row, mark.col, altpos.row,
- altpos.col, attr_id, decor->priority, vt, false };
+ if (altpos.row == -1) {
+ altpos.row = mark.row;
+ altpos.col = mark.col;
+ }
+ decor_add(state, mark.row, mark.col, altpos.row, altpos.col,
+ decor, false, 0);
}
- hlrange_activate(range, state);
next_mark:
if (marktree_itr_node_done(state->itr)) {
+ marktree_itr_next(buf->b_marktree, state->itr);
break;
}
marktree_itr_next(buf->b_marktree, state->itr);
@@ -220,41 +221,39 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
return true; // TODO(bfredl): be more precise
}
-static void hlrange_activate(HlRange range, DecorState *state)
+static void decor_add(DecorState *state, int start_row, int start_col,
+ int end_row, int end_col, Decoration *decor, bool owned,
+ DecorPriority priority)
{
- // Get size before preparing the push, to have the number of elements
- size_t s = kv_size(state->active);
-
- kv_pushp(state->active);
+ int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- size_t dest_index = 0;
+ HlRange range = { start_row, start_col, end_row, end_col,
+ attr_id, MAX(priority, decor->priority),
+ kv_size(decor->virt_text) ? &decor->virt_text : NULL,
+ decor->virt_text_pos,
+ kv_size(decor->virt_text) && owned, -1 };
- // Determine insertion dest_index
- while (dest_index < s) {
- HlRange item = kv_A(state->active, dest_index);
- if (item.priority > range.priority) {
+ kv_pushp(state->active);
+ size_t index;
+ for (index = kv_size(state->active)-1; index > 0; index--) {
+ HlRange item = kv_A(state->active, index-1);
+ if (item.priority <= range.priority) {
break;
}
-
- dest_index++;
- }
-
- // Splice
- for (size_t index = s; index > dest_index; index--) {
kv_A(state->active, index) = kv_A(state->active, index-1);
}
-
- // Insert
- kv_A(state->active, dest_index) = range;
+ kv_A(state->active, index) = range;
}
-int decor_redraw_col(buf_T *buf, int col, DecorState *state)
+int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state)
{
if (col <= state->col_until) {
return state->current;
}
state->col_until = MAXCOL;
while (true) {
+ // TODO(bfredl): check duplicate entry in "intersection"
+ // branch
mtmark_t mark = marktree_itr_current(state->itr);
if (mark.row < 0 || mark.row > state->row) {
break;
@@ -278,6 +277,11 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state)
}
Decoration *decor = item->decor;
+ if (endpos.row == -1) {
+ endpos.row = mark.row;
+ endpos.col = mark.col;
+ }
+
if (endpos.row < mark.row
|| (endpos.row == mark.row && endpos.col <= mark.col)) {
if (!kv_size(decor->virt_text)) {
@@ -285,12 +289,8 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state)
}
}
- int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL;
- hlrange_activate((HlRange){ mark.row, mark.col,
- endpos.row, endpos.col,
- attr_id, decor->priority,
- vt, false }, state);
+ decor_add(state, mark.row, mark.col, endpos.row, endpos.col,
+ decor, false, 0);
next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
@@ -310,7 +310,7 @@ next_mark:
if (item.start_row < state->row
|| (item.start_row == state->row && item.start_col <= col)) {
active = true;
- if (item.end_row == state->row) {
+ if (item.end_row == state->row && item.end_col > col) {
state->col_until = MIN(state->col_until, item.end_col-1);
}
} else {
@@ -322,8 +322,12 @@ next_mark:
if (active && item.attr_id > 0) {
attr = hl_combine_attr(attr, item.attr_id);
}
+ if ((item.start_row == state->row && item.start_col <= col)
+ && item.virt_text && item.virt_col == -1) {
+ item.virt_col = virt_col;
+ }
if (keep) {
- kv_A(state->active, j++) = kv_A(state->active, i);
+ kv_A(state->active, j++) = item;
} else if (item.virt_text_owned) {
clear_virttext(item.virt_text);
xfree(item.virt_text);
@@ -341,21 +345,20 @@ void decor_redraw_end(DecorState *state)
VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state)
{
- decor_redraw_col(buf, MAXCOL, state);
+ decor_redraw_col(buf, MAXCOL, MAXCOL, state);
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i);
- if (item.start_row == state->row && item.virt_text) {
+ if (item.start_row == state->row && item.virt_text
+ && item.virt_text_pos == kVTEndOfLine) {
return item.virt_text;
}
}
return NULL;
}
-void decor_add_ephemeral(int attr_id, int start_row, int start_col,
- int end_row, int end_col, DecorPriority priority,
- VirtText *virt_text)
+void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
+ Decoration *decor, DecorPriority priority)
{
-hlrange_activate(((HlRange){ start_row, start_col, end_row, end_col, attr_id,
- priority, virt_text, virt_text != NULL }),
- &decor_state);
+ decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true,
+ priority);
}
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 2533a641dd..47bd9abbc3 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -18,10 +18,16 @@ typedef kvec_t(VirtTextChunk) VirtText;
typedef uint16_t DecorPriority;
#define DECOR_PRIORITY_BASE 0x1000
+typedef enum {
+ kVTEndOfLine,
+ kVTOverlay,
+} VirtTextPos;
+
struct Decoration
{
int hl_id; // highlight group
VirtText virt_text;
+ VirtTextPos virt_text_pos;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
bool shared; // shared decoration, don't free
@@ -35,7 +41,9 @@ typedef struct {
int attr_id;
DecorPriority priority;
VirtText *virt_text;
+ VirtTextPos virt_text_pos;
bool virt_text_owned;
+ int virt_col;
} HlRange;
typedef struct {
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 93bc34fa4b..31b7b1bd8f 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -58,6 +58,7 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden
#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window
+#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
@@ -1361,11 +1362,12 @@ void diff_win_options(win_T *wp, int addbuf)
wp->w_p_crb_save = wp->w_p_crb;
}
wp->w_p_crb = true;
-
- if (!wp->w_p_diff) {
- wp->w_p_wrap_save = wp->w_p_wrap;
+ if (!(diff_flags & DIFF_FOLLOWWRAP)) {
+ if (!wp->w_p_diff) {
+ wp->w_p_wrap_save = wp->w_p_wrap;
+ }
+ wp->w_p_wrap = false;
}
- wp->w_p_wrap = false;
curwin = wp; // -V519
curbuf = curwin->w_buffer;
@@ -1375,7 +1377,7 @@ void diff_win_options(win_T *wp, int addbuf)
}
wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
}
- set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff",
+ set_string_option_direct("fdm", -1, (char_u *)"diff",
OPT_LOCAL | OPT_FREE, 0);
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -1437,11 +1439,11 @@ void ex_diffoff(exarg_T *eap)
if (wp->w_p_crb) {
wp->w_p_crb = wp->w_p_crb_save;
}
-
- if (!wp->w_p_wrap) {
- wp->w_p_wrap = wp->w_p_wrap_save;
+ if (!(diff_flags & DIFF_FOLLOWWRAP)) {
+ if (!wp->w_p_wrap) {
+ wp->w_p_wrap = wp->w_p_wrap_save;
+ }
}
-
free_string_option(wp->w_p_fdm);
wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
? wp->w_p_fdm_save
@@ -2158,6 +2160,9 @@ int diffopt_changed(void)
} else if (STRNCMP(p, "closeoff", 8) == 0) {
p += 8;
diff_flags_new |= DIFF_CLOSE_OFF;
+ } else if (STRNCMP(p, "followwrap", 10) == 0) {
+ p += 10;
+ diff_flags_new |= DIFF_FOLLOWWRAP;
} else if (STRNCMP(p, "indent-heuristic", 16) == 0) {
p += 16;
diff_indent_heuristic = XDF_INDENT_HEURISTIC;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 100e88e261..68c7438ea3 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1024,7 +1024,7 @@ static int insert_handle_key(InsertState *s)
break;
case K_EVENT: // some event
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
goto check_pum;
case K_COMMAND: // some command
@@ -1627,11 +1627,11 @@ static void init_prompt(int cmdchar_todo)
ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false);
}
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
changed_bytes(curbuf->b_ml.ml_line_count, 0);
}
if (cmdchar_todo == 'A') {
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
}
if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) {
curwin->w_cursor.col = STRLEN(prompt);
@@ -6246,9 +6246,10 @@ void auto_format(
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
// "cannot happen"
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance((colnr_T)MAXCOL);
- } else
+ coladvance(MAXCOL);
+ } else {
check_cursor_col();
+ }
// Insert mode: If the cursor is now after the end of the line while it
// previously wasn't, the line was broken. Because of the rule above we
@@ -8432,8 +8433,8 @@ static void ins_left(void)
// if 'whichwrap' set for cursor in insert mode may go to previous line.
// always break undo when moving upwards/downwards, else undo may break
start_arrow(&tpos);
- --(curwin->w_cursor.lnum);
- coladvance((colnr_T)MAXCOL);
+ curwin->w_cursor.lnum--;
+ coladvance(MAXCOL);
curwin->w_set_curswant = true; // so we stay at the end
} else {
vim_beep(BO_CRSR);
@@ -8467,7 +8468,7 @@ static void ins_end(int c)
tpos = curwin->w_cursor;
if (c == K_C_END)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
curwin->w_curswant = MAXCOL;
start_arrow(&tpos);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a25b140b2f..6d97310c1c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -176,7 +176,6 @@ static struct vimvar {
VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
- VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_REG, "register", VAR_STRING, VV_RO),
VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
@@ -211,13 +210,9 @@ static struct vimvar {
VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
VV(VV_ERRORS, "errors", VAR_LIST, 0),
- VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
- VV(VV_EVENT, "event", VAR_DICT, VV_RO),
VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
- VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
- VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
VV(VV_TESTING, "testing", VAR_NUMBER, 0),
VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
@@ -227,10 +222,16 @@ static struct vimvar {
VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
+ VV(VV_EVENT, "event", VAR_DICT, VV_RO),
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
+ VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
+ // Neovim
+ VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
+ VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
+ VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
+ VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
- VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
};
#undef VV
@@ -455,14 +456,15 @@ void eval_clear(void)
* Set an internal variable to a string value. Creates the variable if it does
* not already exist.
*/
-void set_internal_string_var(char_u *name, char_u *value)
+void set_internal_string_var(const char *name, char_u *value)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- const typval_T tv = {
+ typval_T tv = {
.v_type = VAR_STRING,
.vval.v_string = value,
};
- set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true);
+ set_var(name, strlen(name), &tv, true);
}
static lval_T *redir_lval = NULL;
@@ -522,9 +524,9 @@ var_redir_start(
tv.v_type = VAR_STRING;
tv.vval.v_string = (char_u *)"";
if (append) {
- set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)".");
+ set_var_lval(redir_lval, redir_endp, &tv, true, false, ".");
} else {
- set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"=");
+ set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
err = did_emsg;
@@ -584,7 +586,7 @@ void var_redir_stop(void)
redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval,
false, false, 0, FNE_CHECK_START);
if (redir_endp != NULL && redir_lval->ll_name != NULL) {
- set_var_lval(redir_lval, redir_endp, &tv, false, false, (char_u *)".");
+ set_var_lval(redir_lval, redir_endp, &tv, false, false, ".");
}
clear_lval(redir_lval);
}
@@ -1847,7 +1849,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
s = tv_get_string_chk(tv); // != NULL if number or string.
}
if (s != NULL && op != NULL && *op != '=') {
- opt_type = get_option_value(arg, &numval, (char_u **)&stringval,
+ opt_type = get_option_value((char *)arg, &numval, (char_u **)&stringval,
opt_flags);
if ((opt_type == 1 && *op == '.')
|| (opt_type == 0 && *op != '.')) {
@@ -1924,7 +1926,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) {
EMSG(_(e_letunexp));
} else {
- set_var_lval(&lv, p, tv, copy, is_const, op);
+ set_var_lval(&lv, p, tv, copy, is_const, (const char *)op);
arg_end = p;
}
}
@@ -2121,9 +2123,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
return NULL;
}
}
- lp->ll_range = TRUE;
- } else
- lp->ll_range = FALSE;
+ lp->ll_range = true;
+ } else {
+ lp->ll_range = false;
+ }
if (*p != ']') {
if (!quiet) {
@@ -2240,12 +2243,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
return NULL;
}
- /*
- * May need to find the item or absolute index for the second
- * index of a range.
- * When no index given: "lp->ll_empty2" is TRUE.
- * Otherwise "lp->ll_n2" is set to the second index.
- */
+ // May need to find the item or absolute index for the second
+ // index of a range.
+ // When no index given: "lp->ll_empty2" is true.
+ // Otherwise "lp->ll_n2" is set to the second index.
if (lp->ll_range && !lp->ll_empty2) {
lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
@@ -2299,7 +2300,7 @@ void clear_lval(lval_T *lp)
* "%" for "%=", "." for ".=" or "=" for "=".
*/
static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
- int copy, const bool is_const, const char_u *op)
+ int copy, const bool is_const, const char *op)
{
int cc;
listitem_T *ri;
@@ -2326,7 +2327,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
TV_CSTRING)
&& !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
TV_CSTRING)))
- && eexe_mod_op(&tv, rettv, (const char *)op) == OK) {
+ && eexe_mod_op(&tv, rettv, op) == OK) {
set_var(lp->ll_name, lp->ll_name_len, &tv, false);
}
tv_clear(&tv);
@@ -2369,8 +2370,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
*/
for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) {
if (op != NULL && *op != '=') {
- eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri),
- (const char *)op);
+ eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op);
} else {
tv_clear(TV_LIST_ITEM_TV(lp->ll_li));
tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li));
@@ -2428,7 +2428,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
}
if (op != NULL && *op != '=') {
- eexe_mod_op(lp->ll_tv, rettv, (const char *)op);
+ eexe_mod_op(lp->ll_tv, rettv, op);
goto notify;
} else {
tv_clear(lp->ll_tv);
@@ -4538,7 +4538,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv,
c = *option_end;
*option_end = NUL;
- opt_type = get_option_value((char_u *)(*arg), &numval,
+ opt_type = get_option_value(*arg, &numval,
rettv == NULL ? NULL : &stringval, opt_flags);
if (opt_type == -3) { // invalid name
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 06b7f9e21d..4f03d5d259 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -56,10 +56,10 @@ typedef struct lval_S {
///< isn't NULL it's the Dict to which to add the item.
listitem_T *ll_li; ///< The list item or NULL.
list_T *ll_list; ///< The list or NULL.
- int ll_range; ///< TRUE when a [i:j] range was used.
+ bool ll_range; ///< true when a [i:j] range was used.
+ bool ll_empty2; ///< Second index is empty: [i:].
long ll_n1; ///< First index for list.
long ll_n2; ///< Second index for list range.
- int ll_empty2; ///< Second index is empty: [i:].
dict_T *ll_dict; ///< The Dictionary or NULL.
dictitem_T *ll_di; ///< The dictitem or NULL.
char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL.
@@ -105,7 +105,6 @@ typedef enum {
VV_DYING,
VV_EXCEPTION,
VV_THROWPOINT,
- VV_STDERR,
VV_REG,
VV_CMDBANG,
VV_INSERTMODE,
@@ -140,13 +139,9 @@ typedef enum {
VV_OPTION_OLD,
VV_OPTION_TYPE,
VV_ERRORS,
- VV_MSGPACK_TYPES,
- VV_EVENT,
VV_FALSE,
VV_TRUE,
VV_NULL,
- VV__NULL_LIST, // List with NULL value. For test purposes only.
- VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
VV_VIM_DID_ENTER,
VV_TESTING,
VV_TYPE_NUMBER,
@@ -156,10 +151,16 @@ typedef enum {
VV_TYPE_DICT,
VV_TYPE_FLOAT,
VV_TYPE_BOOL,
+ VV_EVENT,
VV_ECHOSPACE,
+ VV_ARGV,
VV_EXITING,
+ // Neovim
+ VV_STDERR,
+ VV_MSGPACK_TYPES,
+ VV__NULL_LIST, // List with NULL value. For test purposes only.
+ VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
VV_LUA,
- VV_ARGV,
} VimVarIndex;
/// All recognized msgpack types
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 952fa35b83..eac0feafcf 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -63,6 +63,7 @@ return {
chanclose={args={1, 2}},
chansend={args=2},
char2nr={args={1, 2}},
+ charidx={args={2, 3}},
cindent={args=1},
clearmatches={args={0, 1}},
col={args=1},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 1a7bbf2d0e..60229e1ebc 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -940,6 +940,52 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
(const char_u *)tv_get_string(&argvars[0]));
}
+// "charidx()" function
+static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->vval.v_number = -1;
+
+ if (argvars[0].v_type != VAR_STRING
+ || argvars[1].v_type != VAR_NUMBER
+ || (argvars[2].v_type != VAR_UNKNOWN
+ && argvars[2].v_type != VAR_NUMBER)) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ const char *str = tv_get_string_chk(&argvars[0]);
+ varnumber_T idx = tv_get_number_chk(&argvars[1], NULL);
+ if (str == NULL || idx < 0) {
+ return;
+ }
+ int countcc = 0;
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ countcc = (int)tv_get_number(&argvars[2]);
+ }
+ if (countcc < 0 || countcc > 1) {
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ int (*ptr2len)(const char_u *);
+ if (countcc) {
+ ptr2len = utf_ptr2len;
+ } else {
+ ptr2len = utfc_ptr2len;
+ }
+
+ const char *p;
+ int len;
+ for (p = str, len = 0; p <= str + idx; len++) {
+ if (*p == NUL) {
+ return;
+ }
+ p += ptr2len((const char_u *)p);
+ }
+
+ rettv->vval.v_number = len > 0 ? len - 1 : 0;
+}
+
/*
* "cindent(lnum)" function
*/
@@ -1629,27 +1675,26 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (u_save(first - 1, last + 1) == FAIL) {
rettv->vval.v_number = 1; // FAIL
- return;
- }
-
- for (linenr_T lnum = first; lnum <= last; lnum++) {
- ml_delete(first, true);
- }
+ } else {
+ for (linenr_T lnum = first; lnum <= last; lnum++) {
+ ml_delete(first, true);
+ }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_buffer == buf) {
- if (wp->w_cursor.lnum > last) {
- wp->w_cursor.lnum -= count;
- } else if (wp->w_cursor.lnum> first) {
- wp->w_cursor.lnum = first;
- }
- if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
- wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf) {
+ if (wp->w_cursor.lnum > last) {
+ wp->w_cursor.lnum -= count;
+ } else if (wp->w_cursor.lnum> first) {
+ wp->w_cursor.lnum = first;
+ }
+ if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
+ wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count;
+ }
}
}
+ check_cursor_col();
+ deleted_lines_mark(first, count);
}
- check_cursor_col();
- deleted_lines_mark(first, count);
if (!is_curbuf) {
curbuf = curbuf_save;
@@ -2149,7 +2194,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, kListLenMayKnow);
int modes = MENU_ALL_MODES;
if (argvars[1].v_type == VAR_STRING) {
- const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
+ const char *const strmodes = tv_get_string(&argvars[1]);
modes = get_menu_cmd_modes(strmodes, false, NULL, NULL);
}
menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
@@ -2984,10 +3029,11 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_UNKNOWN) {
// getchar(): blocking wait.
+ // TODO(bfredl): deduplicate shared logic with state_enter ?
if (!(char_avail() || using_script() || input_available())) {
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
if (!multiqueue_empty(main_loop.events)) {
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
continue;
}
}
@@ -3135,6 +3181,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
| WILD_NO_BEEP;
+ if (argvars[1].v_type != VAR_STRING) {
+ EMSG2(_(e_invarg2), "type must be a string");
+ return;
+ }
+ const char *const type = tv_get_string(&argvars[1]);
+
if (argvars[2].v_type != VAR_UNKNOWN) {
filtered = (bool)tv_get_number_chk(&argvars[2], NULL);
}
@@ -3148,12 +3200,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
options |= WILD_KEEP_ALL;
}
- if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
+ if (argvars[0].v_type != VAR_STRING) {
EMSG(_(e_invarg));
return;
}
- if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) {
+ if (strcmp(type, "cmdline") == 0) {
set_one_cmd_context(&xpc, tv_get_string(&argvars[0]));
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
goto theend;
@@ -3162,15 +3214,14 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ExpandInit(&xpc);
xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]);
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_context = cmdcomplete_str_to_type(
- (char_u *)tv_get_string(&argvars[1]));
+ xpc.xp_context = cmdcomplete_str_to_type(type);
if (xpc.xp_context == EXPAND_NOTHING) {
- EMSG2(_(e_invarg2), argvars[1].vval.v_string);
+ EMSG2(_(e_invarg2), type);
return;
}
if (xpc.xp_context == EXPAND_MENUS) {
- set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, false);
+ set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false);
xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
}
@@ -4081,7 +4132,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
#ifdef _WIN64
"win64",
#endif
+#ifndef CASE_INSENSITIVE_FILENAME
"fname_case",
+#endif
#ifdef HAVE_ACL
"acl",
#endif
@@ -4912,7 +4965,8 @@ static const char *required_env_vars[] = {
static dict_T *create_environment(const dictitem_T *job_env,
const bool clear_env,
- const bool pty)
+ const bool pty,
+ const char * const pty_term_name)
{
dict_T * env = tv_dict_alloc();
@@ -4946,6 +5000,18 @@ static dict_T *create_environment(const dictitem_T *job_env,
}
}
+ // For a pty, we need a sane $TERM set. We can't rely on nvim's environment,
+ // because the child process is going to be communicating with nvim, not the
+ // parent terminal. Set a sane default, but let the user override it in the
+ // job's environment if they want.
+ if (pty) {
+ dictitem_T *dv = tv_dict_find(env, S_LEN("TERM"));
+ if (dv) {
+ tv_dict_item_remove(env, dv);
+ }
+ tv_dict_add_str(env, S_LEN("TERM"), pty_term_name);
+ }
+
if (job_env) {
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
}
@@ -5055,20 +5121,25 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- env = create_environment(job_env, clear_env, pty);
-
uint16_t width = 0, height = 0;
char *term_name = NULL;
if (pty) {
width = (uint16_t)tv_dict_get_number(job_opts, "width");
height = (uint16_t)tv_dict_get_number(job_opts, "height");
- term_name = tv_dict_get_string(job_opts, "TERM", true);
+ // Legacy method, before env option existed, to specify $TERM. No longer
+ // documented, but still usable to avoid breaking scripts.
+ term_name = tv_dict_get_string(job_opts, "TERM", false);
+ if (!term_name) {
+ term_name = "ansi";
+ }
}
+ env = create_environment(job_env, clear_env, pty, term_name);
+
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
rpc, overlapped, detach, cwd, width, height,
- term_name, env, &rettv->vval.v_number);
+ env, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
@@ -7511,7 +7582,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
CALLBACK_READER_INIT, CALLBACK_NONE,
false, true, false, false, NULL, 0, 0,
- NULL, NULL, &rettv->vval.v_number);
+ NULL, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
@@ -10618,7 +10689,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- env = create_environment(job_env, clear_env, pty);
+ env = create_environment(job_env, clear_env, pty, "xterm-256color");
const bool rpc = false;
const bool overlapped = false;
@@ -10627,8 +10698,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
pty, rpc, overlapped, detach, cwd,
term_width, curwin->w_height_inner,
- xstrdup("xterm-256color"), env,
- &rettv->vval.v_number);
+ env, &rettv->vval.v_number);
if (rettv->vval.v_number <= 0) {
return;
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index d8ede1e3ba..6fcb01aace 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -315,6 +315,7 @@ struct ufunc {
int uf_calls; ///< nr of active calls
bool uf_cleared; ///< func_clear() was already called
garray_T uf_args; ///< arguments
+ garray_T uf_def_args; ///< default argument expressions
garray_T uf_lines; ///< function lines
int uf_profiling; ///< true when func is being profiled
int uf_prof_initialized;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 70c998ef39..689d05e079 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -67,7 +67,7 @@ void func_init(void)
/// Get function arguments.
static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
- int *varargs, bool skip)
+ int *varargs, garray_T *default_args, bool skip)
{
bool mustend = false;
char_u *arg = *argp;
@@ -78,12 +78,16 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
if (newargs != NULL) {
ga_init(newargs, (int)sizeof(char_u *), 3);
}
+ if (default_args != NULL) {
+ ga_init(default_args, (int)sizeof(char_u *), 3);
+ }
if (varargs != NULL) {
*varargs = false;
}
// Isolate the arguments: "arg1, arg2, ...)"
+ bool any_default = false;
while (*p != endchar) {
if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
if (varargs != NULL) {
@@ -123,6 +127,38 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
*p = c;
}
+ if (*skipwhite(p) == '=' && default_args != NULL) {
+ typval_T rettv;
+
+ any_default = true;
+ p = skipwhite(p) + 1;
+ p = skipwhite(p);
+ char_u *expr = p;
+ if (eval1(&p, &rettv, false) != FAIL) {
+ ga_grow(default_args, 1);
+
+ // trim trailing whitespace
+ while (p > expr && ascii_iswhite(p[-1])) {
+ p--;
+ }
+ c = *p;
+ *p = NUL;
+ expr = vim_strsave(expr);
+ if (expr == NULL) {
+ *p = c;
+ goto err_ret;
+ }
+ ((char_u **)(default_args->ga_data))
+ [default_args->ga_len] = expr;
+ default_args->ga_len++;
+ *p = c;
+ } else {
+ mustend = true;
+ }
+ } else if (any_default) {
+ EMSG(_("E989: Non-default argument follows default argument"));
+ mustend = true;
+ }
if (*p == ',') {
p++;
} else {
@@ -149,6 +185,9 @@ err_ret:
if (newargs != NULL) {
ga_clear_strings(newargs);
}
+ if (default_args != NULL) {
+ ga_clear_strings(default_args);
+ }
return FAIL;
}
@@ -195,7 +234,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
bool eval_lavars = false;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args(&start, '-', NULL, NULL, true);
+ ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *start != '>') {
return NOTDONE;
}
@@ -207,7 +246,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
pnewargs = NULL;
}
*arg = skipwhite(*arg + 1);
- ret = get_function_args(arg, '-', pnewargs, &varargs, false);
+ ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, false);
if (ret == FAIL || **arg != '>') {
goto errret;
}
@@ -259,6 +298,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
+ ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
flags |= FC_CLOSURE;
@@ -715,6 +755,7 @@ static bool func_remove(ufunc_T *fp)
static void func_clear_items(ufunc_T *fp)
{
ga_clear_strings(&(fp->uf_args));
+ ga_clear_strings(&(fp->uf_def_args));
ga_clear_strings(&(fp->uf_lines));
if (fp->uf_cb_free != NULL) {
@@ -857,12 +898,13 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
}
// Init a: variables, unless none found (in lambda).
- // Set a:0 to "argcount".
+ // Set a:0 to "argcount" less number of named arguments, if >= 0.
// Set a:000 to a list with room for the "..." arguments.
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
if ((fp->uf_flags & FC_NOARGS) == 0) {
add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0",
- (varnumber_T)(argcount - fp->uf_args.ga_len));
+ (varnumber_T)(argcount >= fp->uf_args.ga_len
+ ? argcount - fp->uf_args.ga_len : 0));
}
fc->l_avars.dv_lock = VAR_FIXED;
if ((fp->uf_flags & FC_NOARGS) == 0) {
@@ -892,8 +934,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++],
"lastline", (varnumber_T)lastline);
}
- for (int i = 0; i < argcount; i++) {
+ bool default_arg_err = false;
+ for (int i = 0; i < argcount || i < fp->uf_args.ga_len; i++) {
bool addlocal = false;
+ bool isdefault = false;
+ typval_T def_rettv;
ai = i - fp->uf_args.ga_len;
if (ai < 0) {
@@ -902,6 +947,21 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
if (islambda) {
addlocal = true;
}
+
+ // evaluate named argument default expression
+ isdefault = ai + fp->uf_def_args.ga_len >= 0 && i >= argcount;
+ if (isdefault) {
+ char_u *default_expr = NULL;
+ def_rettv.v_type = VAR_NUMBER;
+ def_rettv.vval.v_number = -1;
+
+ default_expr = ((char_u **)(fp->uf_def_args.ga_data))
+ [ai + fp->uf_def_args.ga_len];
+ if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ default_arg_err = true;
+ break;
+ }
+ }
} else {
if ((fp->uf_flags & FC_NOARGS) != 0) {
// Bail out if no a: arguments used (in lambda).
@@ -922,7 +982,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// Note: the values are copied directly to avoid alloc/free.
// "argvars" must have VAR_FIXED for v_lock.
- v->di_tv = argvars[i];
+ v->di_tv = isdefault ? def_rettv : argvars[i];
v->di_tv.v_lock = VAR_FIXED;
if (addlocal) {
@@ -1046,7 +1106,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
save_did_emsg = did_emsg;
did_emsg = FALSE;
- if (islambda) {
+ if (default_arg_err && (fp->uf_flags & FC_ABORT)) {
+ did_emsg = true;
+ } else if (islambda) {
char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
// A Lambda always has the command "return {expr}". It is much faster
@@ -1490,7 +1552,7 @@ call_func(
if (fp->uf_flags & FC_RANGE) {
*doesrange = true;
}
- if (argcount < fp->uf_args.ga_len) {
+ if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
error = ERROR_TOOFEW;
} else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
error = ERROR_TOOMANY;
@@ -1573,6 +1635,11 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
msg_puts(", ");
}
msg_puts((const char *)FUNCARG(fp, j));
+ if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
+ msg_puts(" = ");
+ msg_puts(((char **)(fp->uf_def_args.ga_data))
+ [j - fp->uf_args.ga_len + fp->uf_def_args.ga_len]);
+ }
}
if (fp->uf_varargs) {
if (j) {
@@ -1836,6 +1903,7 @@ void ex_function(exarg_T *eap)
char_u *arg;
char_u *line_arg = NULL;
garray_T newargs;
+ garray_T default_args;
garray_T newlines;
int varargs = false;
int flags = 0;
@@ -2039,7 +2107,8 @@ void ex_function(exarg_T *eap)
}
}
- if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL) {
+ if (get_function_args(&p, ')', &newargs, &varargs,
+ &default_args, eap->skip) == FAIL) {
goto errret_2;
}
@@ -2458,6 +2527,7 @@ void ex_function(exarg_T *eap)
fp->uf_refcount = 1;
}
fp->uf_args = newargs;
+ fp->uf_def_args = default_args;
fp->uf_lines = newlines;
if ((flags & FC_CLOSURE) != 0) {
register_closure(fp);
@@ -2480,6 +2550,7 @@ void ex_function(exarg_T *eap)
erret:
ga_clear_strings(&newargs);
+ ga_clear_strings(&default_args);
errret_2:
ga_clear_strings(&newlines);
ret_free:
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 8e9964bd37..b93d6cc0ab 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -270,9 +270,6 @@ static void process_close_event(void **argv)
{
Process *proc = argv[0];
shell_free_argv(proc->argv);
- if (proc->type == kProcessTypePty) {
- xfree(((PtyProcess *)proc)->term_name);
- }
if (proc->cb) { // "on_exit" for jobstart(). See channel_job_start().
proc->cb(proc, proc->status, proc->data);
}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 2da8c205c1..854faf5377 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -790,7 +790,10 @@ void ex_retab(exarg_T *eap)
for (col = 0; col < len; col++) {
ptr[col] = (col < num_tabs) ? '\t' : ' ';
}
- ml_replace(lnum, new_line, false);
+ if (ml_replace(lnum, new_line, false) == OK) {
+ // "new_line" may have been copied
+ new_line = curbuf->b_ml.ml_line_ptr;
+ }
if (first_line == 0) {
first_line = lnum;
}
@@ -1404,19 +1407,20 @@ do_shell(
* For autocommands we want to get the output on the current screen, to
* avoid having to type return below.
*/
- msg_putchar('\r'); /* put cursor at start of line */
- msg_putchar('\n'); /* may shift screen one line up */
+ msg_putchar('\r'); // put cursor at start of line
+ msg_putchar('\n'); // may shift screen one line up
- /* warning message before calling the shell */
+ // warning message before calling the shell
if (p_warn
&& !autocmd_busy
- && msg_silent == 0)
+ && msg_silent == 0) {
FOR_ALL_BUFFERS(buf) {
if (bufIsChanged(buf)) {
MSG_PUTS(_("[No write since last change]\n"));
break;
}
}
+ }
// This ui_cursor_goto is required for when the '\n' resulted in a "delete line
// 1" command to the terminal.
@@ -2178,6 +2182,8 @@ theend:
/// ECMD_OLDBUF: use existing buffer if it exists
/// ECMD_FORCEIT: ! used for Ex command
/// ECMD_ADDBUF: don't edit, just add to buffer list
+/// ECMD_ALTBUF: like ECMD_ADDBUF and also set the alternate
+/// file
/// @param oldwin Should be "curwin" when editing a new buffer in the current
/// window, NULL when splitting the window first. When not NULL
/// info of the previous buffer for "oldwin" is stored.
@@ -2234,8 +2240,10 @@ int do_ecmd(
path_fix_case(sfname); // set correct case for sfname
#endif
- if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL))
+ if ((flags & (ECMD_ADDBUF | ECMD_ALTBUF))
+ && (ffname == NULL || *ffname == NUL)) {
goto theend;
+ }
if (ffname == NULL)
other_file = TRUE;
@@ -2265,15 +2273,16 @@ int do_ecmd(
// If the file was changed we may not be allowed to abandon it:
// - if we are going to re-edit the same file
// - or if we are the only window on this file and if ECMD_HIDE is FALSE
- if ( ((!other_file && !(flags & ECMD_OLDBUF))
- || (curbuf->b_nwindows == 1
- && !(flags & (ECMD_HIDE | ECMD_ADDBUF))))
- && check_changed(curbuf, (p_awa ? CCGD_AW : 0)
- | (other_file ? 0 : CCGD_MULTWIN)
- | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0)
- | (eap == NULL ? 0 : CCGD_EXCMD))) {
- if (fnum == 0 && other_file && ffname != NULL)
+ if (((!other_file && !(flags & ECMD_OLDBUF))
+ || (curbuf->b_nwindows == 1
+ && !(flags & (ECMD_HIDE | ECMD_ADDBUF | ECMD_ALTBUF))))
+ && check_changed(curbuf, (p_awa ? CCGD_AW : 0)
+ | (other_file ? 0 : CCGD_MULTWIN)
+ | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0)
+ | (eap == NULL ? 0 : CCGD_EXCMD))) {
+ if (fnum == 0 && other_file && ffname != NULL) {
(void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum);
+ }
goto theend;
}
@@ -2303,25 +2312,35 @@ int do_ecmd(
* Otherwise we re-use the current buffer.
*/
if (other_file) {
- if (!(flags & ECMD_ADDBUF)) {
- if (!cmdmod.keepalt)
+ if (!(flags & (ECMD_ADDBUF | ECMD_ALTBUF))) {
+ if (!cmdmod.keepalt) {
curwin->w_alt_fnum = curbuf->b_fnum;
- if (oldwin != NULL)
+ }
+ if (oldwin != NULL) {
buflist_altfpos(oldwin);
+ }
}
if (fnum) {
buf = buflist_findnr(fnum);
} else {
- if (flags & ECMD_ADDBUF) {
- linenr_T tlnum = 1L;
+ if (flags & (ECMD_ADDBUF | ECMD_ALTBUF)) {
+ // Default the line number to zero to avoid that a wininfo item
+ // is added for the current window.
+ linenr_T tlnum = 0;
if (command != NULL) {
tlnum = atol((char *)command);
if (tlnum <= 0)
tlnum = 1L;
}
- (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED);
+ // Add BLN_NOCURWIN to avoid a new wininfo items are associated
+ // with the current window.
+ const buf_T *const newbuf
+ = buflist_new(ffname, sfname, tlnum, BLN_LISTED | BLN_NOCURWIN);
+ if (newbuf != NULL && (flags & ECMD_ALTBUF)) {
+ curwin->w_alt_fnum = newbuf->b_fnum;
+ }
goto theend;
}
buf = buflist_new(ffname, sfname, 0L,
@@ -2413,7 +2432,10 @@ int do_ecmd(
(flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
false);
- the_curwin->w_closing = false;
+ // Autocommands may have closed the window.
+ if (win_valid(the_curwin)) {
+ the_curwin->w_closing = false;
+ }
buf->b_locked--;
// autocmds may abort script processing
@@ -2464,8 +2486,7 @@ int do_ecmd(
curwin->w_pcmark.lnum = 1;
curwin->w_pcmark.col = 0;
} else { // !other_file
- if ((flags & ECMD_ADDBUF)
- || check_fname() == FAIL) {
+ if ((flags & (ECMD_ADDBUF | ECMD_ALTBUF)) || check_fname() == FAIL) {
goto theend;
}
oldbuf = (flags & ECMD_OLDBUF);
@@ -2531,13 +2552,13 @@ int do_ecmd(
goto theend;
}
u_unchanged(curbuf);
- buf_updates_unregister_all(curbuf);
+ buf_updates_unload(curbuf, false);
buf_freeall(curbuf, BFA_KEEP_UNDO);
// Tell readfile() not to clear or reload undo info.
readfile_flags = READ_KEEP_UNDO;
} else {
- buf_updates_unregister_all(curbuf);
+ buf_updates_unload(curbuf, false);
buf_freeall(curbuf, 0); // Free all things for buffer.
}
// If autocommands deleted the buffer we were going to re-edit, give
@@ -3310,11 +3331,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
int save_b_changed = curbuf->b_changed;
bool preview = (State & CMDPREVIEW);
- // inccommand tests fail without this check
- if (!preview) {
- // Required for Undo to work for extmarks.
- u_save_cursor();
- }
+ bool did_save = false;
if (!global_busy) {
sub_nsubs = 0;
@@ -3991,6 +4008,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
int matchcols = end.col - ((end.lnum == start.lnum)
? start.col : 0);
int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0);
+ if (!did_save) {
+ // Required for Undo to work for extmarks.
+ u_save_cursor();
+ did_save = true;
+ }
extmark_splice(curbuf, lnum_start-1, start_col,
end.lnum-start.lnum, matchcols, replaced_bytes,
lnum-lnum_start, subcols, sublen-1, kExtmarkUndo);
@@ -4177,7 +4199,7 @@ skip:
// when interactive leave cursor on the match
if (!subflags.do_ask) {
if (endcolumn) {
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
} else {
beginline(BL_WHITE | BL_FIX);
}
@@ -4220,7 +4242,7 @@ skip:
size_t subsize = preview_lines.subresults.size;
if (preview && !aborting()) {
if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
- set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
+ set_string_option_direct("icm", -1, (char_u *)"", OPT_FREE,
SID_NONE);
} else if (*p_icm != NUL && pat != NULL) {
if (pre_src_id == 0) {
@@ -4521,7 +4543,7 @@ prepare_tagpreview (
RESET_BINDING(curwin); /* don't take over 'scrollbind'
and 'cursorbind' */
curwin->w_p_diff = false; // no 'diff'
- set_string_option_direct((char_u *)"fdc", -1, // no 'foldcolumn'
+ set_string_option_direct("fdc", -1, // no 'foldcolumn'
(char_u *)"0", OPT_FREE, SID_NONE);
return true;
}
@@ -4999,7 +5021,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
if (keep_lang) {
flags |= TAG_KEEP_LANG;
}
- if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK
+ if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
&& *num_matches > 0) {
/* Sort the matches found on the heuristic number that is after the
* tag name. */
@@ -5016,7 +5038,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
static void prepare_help_buffer(void)
{
curbuf->b_help = true;
- set_string_option_direct((char_u *)"buftype", -1, (char_u *)"help",
+ set_string_option_direct("buftype", -1, (char_u *)"help",
OPT_FREE|OPT_LOCAL, 0);
// Always set these options after jumping to a help tag, because the
@@ -5026,13 +5048,13 @@ static void prepare_help_buffer(void)
// Only set it when needed, buf_init_chartab() is some work.
char_u *p = (char_u *)"!-~,^*,^|,^\",192-255";
if (STRCMP(curbuf->b_p_isk, p) != 0) {
- set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
+ set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
check_buf_options(curbuf);
(void)buf_init_chartab(curbuf, FALSE);
}
// Don't use the global foldmethod.
- set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual",
+ set_string_option_direct("fdm", -1, (char_u *)"manual",
OPT_FREE|OPT_LOCAL, 0);
curbuf->b_p_ts = 8; // 'tabstop' is 8.
@@ -5652,7 +5674,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
cmdmod.tab = 0; // disable :tab modifier
cmdmod.noswapfile = true; // disable swap for preview buffer
// disable file info message
- set_string_option_direct((char_u *)"shm", -1, (char_u *)"F", OPT_FREE,
+ set_string_option_direct("shm", -1, (char_u *)"F", OPT_FREE,
SID_NONE);
bool outside_curline = (eap->line1 != old_cusr.lnum
@@ -5775,7 +5797,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
update_screen(SOME_VALID);
RedrawingDisabled = save_rd;
- set_string_option_direct((char_u *)"shm", -1, save_shm_p, OPT_FREE, SID_NONE);
+ set_string_option_direct("shm", -1, save_shm_p, OPT_FREE, SID_NONE);
xfree(save_shm_p);
cmdmod = save_cmdmod;
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index b564cde56c..1b54b3a898 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -16,7 +16,7 @@
#define ECMD_OLDBUF 0x04 // use existing buffer if it exists
#define ECMD_FORCEIT 0x08 // ! used in Ex command
#define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list
-
+#define ECMD_ALTBUF 0x20 // like ECMD_ADDBUF and set the alternate file
/* for lnum argument in do_ecmd() */
#define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index e9046da800..2965ea7496 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -176,6 +176,12 @@ module.cmds = {
func='ex_edit',
},
{
+ command='balt',
+ flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN),
+ addr_type='ADDR_NONE',
+ func='ex_edit',
+ },
+ {
command='bdelete',
flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR),
addr_type='ADDR_BUFFERS',
@@ -2515,8 +2521,8 @@ module.cmds = {
},
{
command='source',
- flags=bit.bor(BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
- addr_type='ADDR_NONE',
+ flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
+ addr_type='ADDR_LINES',
func='ex_source',
},
{
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index e49bb99aa0..c4c18c4324 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -29,6 +29,7 @@
#include "nvim/getchar.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
+#include "nvim/memline.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
#include "nvim/garray.h"
@@ -1657,9 +1658,11 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
/// AL_DEL: remove files in 'str' from the argument list.
/// @param after
/// 0 means before first one
+/// @param will_edit will edit added argument
///
/// @return FAIL for failure, OK otherwise.
-static int do_arglist(char_u *str, int what, int after)
+static int do_arglist(char_u *str, int what, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
{
garray_T new_ga;
int exp_count;
@@ -1733,10 +1736,11 @@ static int do_arglist(char_u *str, int what, int after)
}
if (what == AL_ADD) {
- (void)alist_add_list(exp_count, exp_files, after);
+ alist_add_list(exp_count, exp_files, after, will_edit);
xfree(exp_files);
- } else { // what == AL_SET
- alist_set(ALIST(curwin), exp_count, exp_files, false, NULL, 0);
+ } else {
+ assert(what == AL_SET);
+ alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
}
}
@@ -1956,7 +1960,7 @@ void ex_next(exarg_T *eap)
| (eap->forceit ? CCGD_FORCEIT : 0)
| CCGD_EXCMD)) {
if (*eap->arg != NUL) { // redefine file list
- if (do_arglist(eap->arg, AL_SET, 0) == FAIL) {
+ if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) {
return;
}
i = 0;
@@ -1974,7 +1978,7 @@ void ex_argedit(exarg_T *eap)
// Whether curbuf will be reused, curbuf->b_ffname will be set.
bool curbuf_is_reusable = curbuf_reusable();
- if (do_arglist(eap->arg, AL_ADD, i) == FAIL) {
+ if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) {
return;
}
maketitle();
@@ -1994,7 +1998,8 @@ void ex_argedit(exarg_T *eap)
void ex_argadd(exarg_T *eap)
{
do_arglist(eap->arg, AL_ADD,
- eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
+ eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
+ false);
maketitle();
}
@@ -2041,7 +2046,7 @@ void ex_argdelete(exarg_T *eap)
}
}
} else {
- do_arglist(eap->arg, AL_DEL, 0);
+ do_arglist(eap->arg, AL_DEL, 0, false);
}
maketitle();
}
@@ -2292,9 +2297,9 @@ void ex_listdo(exarg_T *eap)
/// Files[] itself is not taken over.
///
/// @param after: where to add: 0 = before first one
-///
-/// @return index of first added argument
-static int alist_add_list(int count, char_u **files, int after)
+/// @param will_edit will edit adding argument
+static void alist_add_list(int count, char_u **files, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
{
int old_argcount = ARGCOUNT;
ga_grow(&ALIST(curwin)->al_ga, count);
@@ -2310,15 +2315,15 @@ static int alist_add_list(int count, char_u **files, int after)
(size_t)(ARGCOUNT - after) * sizeof(aentry_T));
}
for (int i = 0; i < count; i++) {
+ const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
ARGLIST[after + i].ae_fname = files[i];
- ARGLIST[after + i].ae_fnum = buflist_add(files[i],
- BLN_LISTED | BLN_CURBUF);
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
curwin->w_arg_idx += count;
}
- return after;
+ return;
}
}
@@ -2375,13 +2380,13 @@ void ex_compiler(exarg_T *eap)
// Set "b:current_compiler" from "current_compiler".
p = get_var_value("g:current_compiler");
if (p != NULL) {
- set_internal_string_var((char_u *)"b:current_compiler", p);
+ set_internal_string_var("b:current_compiler", p);
}
// Restore "current_compiler" for ":compiler {name}".
if (!eap->forceit) {
if (old_cur_comp != NULL) {
- set_internal_string_var((char_u *)"g:current_compiler",
+ set_internal_string_var("g:current_compiler",
old_cur_comp);
xfree(old_cur_comp);
} else {
@@ -2522,7 +2527,7 @@ void ex_pyxdo(exarg_T *eap)
}
}
-/// ":source {fname}"
+/// ":source [{fname}]"
void ex_source(exarg_T *eap)
{
cmd_source(eap->arg, eap);
@@ -2531,7 +2536,7 @@ void ex_source(exarg_T *eap)
static void cmd_source(char_u *fname, exarg_T *eap)
{
if (*fname == NUL) {
- EMSG(_(e_argreq));
+ cmd_source_buffer(eap);
} else if (eap != NULL && eap->forceit) {
// ":source!": read Normal mode commands
// Need to execute the commands directly. This is required at least
@@ -2549,6 +2554,37 @@ static void cmd_source(char_u *fname, exarg_T *eap)
}
}
+typedef struct {
+ linenr_T curr_lnum;
+ const linenr_T final_lnum;
+} GetBufferLineCookie;
+
+/// Get one line from the current selection in the buffer.
+/// Called by do_cmdline() when it's called from cmd_source_buffer().
+///
+/// @return pointer to allocated line, or NULL for end-of-file or
+/// some error.
+static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat)
+{
+ GetBufferLineCookie *p = cookie;
+ if (p->curr_lnum > p->final_lnum) {
+ return NULL;
+ }
+ char_u *curr_line = ml_get(p->curr_lnum);
+ p->curr_lnum++;
+ return (char_u *)xstrdup((const char *)curr_line);
+}
+
+static void cmd_source_buffer(exarg_T *eap)
+{
+ GetBufferLineCookie cookie = {
+ .curr_lnum = eap->line1,
+ .final_lnum = eap->line2,
+ };
+ source_using_linegetter((void *)&cookie, get_buffer_line,
+ ":source (no file)");
+}
+
/// ":source" and associated commands.
///
/// @return address holding the next breakpoint line for a source cookie
@@ -2620,10 +2656,9 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
return (char_u *)xstrdup(buf);
}
-/// Executes lines in `src` as Ex commands.
-///
-/// @see do_source()
-int do_source_str(const char *cmd, const char *traceback_name)
+static int source_using_linegetter(void *cookie,
+ LineGetter fgetline,
+ const char *traceback_name)
{
char_u *save_sourcing_name = sourcing_name;
linenr_T save_sourcing_lnum = sourcing_lnum;
@@ -2638,22 +2673,33 @@ int do_source_str(const char *cmd, const char *traceback_name)
}
sourcing_lnum = 0;
- GetStrLineCookie cookie = {
- .buf = (char_u *)cmd,
- .offset = 0,
- };
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = save_sourcing_lnum;
- int retval = do_cmdline(NULL, get_str_line, (void *)&cookie,
+ funccal_entry_T entry;
+ save_funccal(&entry);
+ int retval = do_cmdline(NULL, fgetline, cookie,
DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
- current_sctx = save_current_sctx;
sourcing_lnum = save_sourcing_lnum;
sourcing_name = save_sourcing_name;
+ current_sctx = save_current_sctx;
+ restore_funccal();
return retval;
}
+/// Executes lines in `src` as Ex commands.
+///
+/// @see do_source()
+int do_source_str(const char *cmd, const char *traceback_name)
+{
+ GetStrLineCookie cookie = {
+ .buf = (char_u *)cmd,
+ .offset = 0,
+ };
+ return source_using_linegetter((void *)&cookie, get_str_line, traceback_name);
+}
+
/// Reads the file `fname` and executes its lines as Ex commands.
///
/// This function may be called recursively!
@@ -2978,6 +3024,7 @@ void scriptnames_slash_adjust(void)
# endif
/// Get a pointer to a script name. Used for ":verbose set".
+/// Message appended to "Last set from "
char_u *get_scriptname(LastSet last_set, bool *should_free)
{
*should_free = false;
@@ -2993,6 +3040,8 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
return (char_u *)_("environment variable");
case SID_ERROR:
return (char_u *)_("error handler");
+ case SID_WINLAYOUT:
+ return (char_u *)_("changed window size");
case SID_LUA:
return (char_u *)_("Lua");
case SID_API_CLIENT:
@@ -3766,7 +3815,7 @@ void ex_drop(exarg_T *eap)
// and mostly only one file is dropped.
// This also ignores wildcards, since it is very unlikely the user is
// editing a file name with a wildcard character.
- do_arglist(eap->arg, AL_SET, 0);
+ do_arglist(eap->arg, AL_SET, 0, false);
// Expanding wildcards may result in an empty argument list. E.g. when
// editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index ca84d375ce..f928c61ea4 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -188,8 +188,8 @@ struct exarg {
// used for completion on the command line
struct expand {
- int xp_context; // type of expansion
char_u *xp_pattern; // start of item to expand
+ int xp_context; // type of expansion
size_t xp_pattern_len; // bytes in xp_pattern before cursor
char_u *xp_arg; // completion function
sctx_T xp_script_ctx; // SCTX for completion function
@@ -199,9 +199,9 @@ struct expand {
// characters need to be escaped
#endif
int xp_numfiles; // number of files found by file name completion
+ int xp_col; // cursor position in line
char_u **xp_files; // list of files
char_u *xp_line; // text being completed
- int xp_col; // cursor position in line
};
// values for xp_backslash
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 52da7fe4f6..bc3d29a03f 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2179,7 +2179,7 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only)
// Set 'eventignore' to "all". Restore the
// existing option value later.
cmdmod.save_ei = vim_strsave(p_ei);
- set_string_option_direct((char_u *)"ei", -1,
+ set_string_option_direct("ei", -1,
(char_u *)"all", OPT_FREE, SID_NONE);
}
continue;
@@ -2291,9 +2291,8 @@ static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
}
if (cmdmod.save_ei != NULL) {
- /* Restore 'eventignore' to the value before ":noautocmd". */
- set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
- OPT_FREE, SID_NONE);
+ // Restore 'eventignore' to the value before ":noautocmd".
+ set_string_option_direct("ei", -1, cmdmod.save_ei, OPT_FREE, SID_NONE);
free_string_option(cmdmod.save_ei);
}
@@ -3513,13 +3512,20 @@ const char * set_one_cmd_context(
xp->xp_context = EXPAND_BUFFERS;
xp->xp_pattern = (char_u *)arg;
break;
+ case CMD_diffget:
+ case CMD_diffput:
+ // If current buffer is in diff mode, complete buffer names
+ // which are in diff mode, and different than current buffer.
+ xp->xp_context = EXPAND_DIFF_BUFFERS;
+ xp->xp_pattern = (char_u *)arg;
+ break;
case CMD_USER:
case CMD_USER_BUF:
if (context != EXPAND_NOTHING) {
// EX_XFILE: file names are handled above.
if (!(ea.argt & EX_XFILE)) {
if (context == EXPAND_MENUS) {
- return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd,
+ return (const char *)set_context_in_menu_cmd(xp, cmd,
(char_u *)arg, forceit);
} else if (context == EXPAND_COMMANDS) {
return arg;
@@ -3599,7 +3605,7 @@ const char * set_one_cmd_context(
case CMD_tmenu: case CMD_tunmenu:
case CMD_popup: case CMD_emenu:
return (const char *)set_context_in_menu_cmd(
- xp, (char_u *)cmd, (char_u *)arg, forceit);
+ xp, cmd, (char_u *)arg, forceit);
case CMD_colorscheme:
xp->xp_context = EXPAND_COLORS;
@@ -5175,6 +5181,7 @@ static const char *command_complete[] =
[EXPAND_CSCOPE] = "cscope",
[EXPAND_USER_DEFINED] = "custom",
[EXPAND_USER_LIST] = "customlist",
+ [EXPAND_DIFF_BUFFERS] = "diff_buffer",
[EXPAND_DIRECTORIES] = "dir",
[EXPAND_ENV_VARS] = "environment",
[EXPAND_EVENTS] = "event",
@@ -6275,14 +6282,14 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp,
return OK;
}
-int cmdcomplete_str_to_type(char_u *complete_str)
+int cmdcomplete_str_to_type(const char *complete_str)
{
for (int i = 0; i < (int)(ARRAY_SIZE(command_complete)); i++) {
char *cmd_compl = get_command_complete(i);
if (cmd_compl == NULL) {
continue;
}
- if (STRCMP(complete_str, command_complete[i]) == 0) {
+ if (strcmp(complete_str, command_complete[i]) == 0) {
return i;
}
}
@@ -7272,9 +7279,7 @@ static void ex_find(exarg_T *eap)
}
}
-/*
- * ":edit", ":badd", ":visual".
- */
+/// ":edit", ":badd", ":balt", ":visual".
static void ex_edit(exarg_T *eap)
{
do_exedit(eap, NULL);
@@ -7348,7 +7353,9 @@ do_exedit(
else if (eap->cmdidx == CMD_enew)
readonlymode = FALSE; /* 'readonly' doesn't make sense in an
empty buffer */
- setpcmark();
+ if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) {
+ setpcmark();
+ }
if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg),
NULL, eap, eap->do_ecmd_lnum,
(buf_hide(curbuf) ? ECMD_HIDE : 0)
@@ -7356,6 +7363,7 @@ do_exedit(
// After a split we can use an existing buffer.
+ (old_curwin != NULL ? ECMD_OLDBUF : 0)
+ (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0)
+ + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0)
, old_curwin == NULL ? curwin : NULL) == FAIL) {
// Editing the file failed. If the window was split, close it.
if (old_curwin != NULL) {
@@ -7756,6 +7764,11 @@ static void do_exmap(exarg_T *eap, int isabbrev)
static void ex_winsize(exarg_T *eap)
{
char_u *arg = eap->arg;
+
+ if (!ascii_isdigit(*arg)) {
+ EMSG2(_(e_invarg2), arg);
+ return;
+ }
int w = getdigits_int(&arg, false, 10);
arg = skipwhite(arg);
char_u *p = arg;
@@ -8721,7 +8734,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
* Evaluate cmdline variables.
*
* change '%' to curbuf->b_ffname
- * '#' to curwin->w_altfile
+ * '#' to curwin->w_alt_fnum
* '<cword>' to word under the cursor
* '<cWORD>' to WORD under the cursor
* '<cexpr>' to C-expression under the cursor
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index cf6bb6d492..5979f4d3a0 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -886,7 +886,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
need_wait_return = false;
}
- set_string_option_direct((char_u *)"icm", -1, s->save_p_icm, OPT_FREE,
+ set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE,
SID_NONE);
State = s->save_State;
setmouse();
@@ -935,7 +935,7 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_EVENT || s->c == K_COMMAND) {
if (s->c == K_EVENT) {
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
} else {
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
}
@@ -5084,9 +5084,13 @@ ExpandFromContext (
}
if (xp->xp_context == EXPAND_BUFFERS)
return ExpandBufnames(pat, num_file, file, options);
+ if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
+ return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
+ }
if (xp->xp_context == EXPAND_TAGS
- || xp->xp_context == EXPAND_TAGS_LISTFILES)
+ || xp->xp_context == EXPAND_TAGS_LISTFILES) {
return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
+ }
if (xp->xp_context == EXPAND_COLORS) {
char *directories[] = { "colors", NULL };
return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories);
@@ -6441,7 +6445,7 @@ static int open_cmdwin(void)
cmdwin_level = ccline.level;
// Create empty command-line buffer.
- buf_open_scratch(0, "[Command Line]");
+ buf_open_scratch(0, _("[Command Line]"));
// Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
set_option_value("bh", 0L, "wipe", OPT_LOCAL);
curwin->w_p_rl = cmdmsg_rl;
@@ -6770,6 +6774,6 @@ static void set_search_match(pos_T *t)
t->col = search_match_endcol;
if (t->lnum > curbuf->b_ml.ml_line_count) {
t->lnum = curbuf->b_ml.ml_line_count;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
}
}
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index dc4395e081..3727aa5e62 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -32,6 +32,7 @@
#define WILD_IGNORE_COMPLETESLASH 0x400
#define WILD_NOERROR 0x800 // sets EW_NOERROR
#define WILD_BUFLASTUSED 0x1000
+#define BUF_DIFF_FILTER 0x2000
/// Present history tables
typedef enum {
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 14dac9a126..63789b3981 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -384,6 +384,18 @@ static int put_view(
xfree(fname_esc);
}
+ if (wp->w_alt_fnum) {
+ buf_T *const alt = buflist_findnr(wp->w_alt_fnum);
+
+ // Set the alternate file.
+ if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL
+ && *alt->b_fname != NUL
+ && (fputs("balt ", fd) < 0
+ || ses_fname(fd, alt, flagp, true) == FAIL)) {
+ return FAIL;
+ }
+ }
+
//
// Local mappings and abbreviations.
//
@@ -438,9 +450,9 @@ static int put_view(
"let s:l = %" PRId64 " - ((%" PRId64
" * winheight(0) + %" PRId64 ") / %" PRId64 ")\n"
"if s:l < 1 | let s:l = 1 | endif\n"
- "exe s:l\n"
+ "keepjumps exe s:l\n"
"normal! zt\n"
- "%" PRId64 "\n",
+ "keepjumps %" PRId64 "\n",
(int64_t)wp->w_cursor.lnum,
(int64_t)(wp->w_cursor.lnum - wp->w_topline),
(int64_t)(wp->w_height_inner / 2),
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index a542bb19dd..714bbb5780 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1644,8 +1644,7 @@ failed:
save_file_ff(curbuf);
// If editing a new file: set 'fenc' for the current buffer.
// Also for ":read ++edit file".
- set_string_option_direct((char_u *)"fenc", -1, fenc,
- OPT_FREE | OPT_LOCAL, 0);
+ set_string_option_direct("fenc", -1, fenc, OPT_FREE | OPT_LOCAL, 0);
}
if (fenc_alloced)
xfree(fenc);
@@ -2002,7 +2001,7 @@ void set_forced_fenc(exarg_T *eap)
{
if (eap->force_enc != 0) {
char_u *fenc = enc_canonize(eap->cmd + eap->force_enc);
- set_string_option_direct((char_u *)"fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0);
+ set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0);
xfree(fenc);
}
}
@@ -5077,7 +5076,8 @@ void buf_reload(buf_T *buf, int orig_mode)
// Mark all undo states as changed.
u_unchanged(curbuf);
}
- buf_updates_unregister_all(curbuf);
+ buf_updates_unload(curbuf, true);
+ curbuf->b_mod_set = true;
}
}
xfree(ea.cmd);
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 0593c16999..5032646d7e 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -2999,7 +2999,6 @@ static void foldlevelDiff(fline_T *flp)
static void foldlevelExpr(fline_T *flp)
{
win_T *win;
- int n;
int c;
linenr_T lnum = flp->lnum + flp->off;
@@ -3017,7 +3016,7 @@ static void foldlevelExpr(fline_T *flp)
/* KeyTyped may be reset to 0 when calling a function which invokes
* do_cmdline(). To make 'foldopen' work correctly restore KeyTyped. */
const bool save_keytyped = KeyTyped;
- n = (int)eval_foldexpr(flp->wp->w_p_fde, &c);
+ const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -3202,8 +3201,10 @@ int put_folds(FILE *fd, win_T *wp)
{
if (foldmethodIsManual(wp)) {
if (put_line(fd, "silent! normal! zE") == FAIL
- || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL)
+ || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL
+ || put_line(fd, "let &fdl = &fdl") == FAIL) {
return FAIL;
+ }
}
/* If some folds are manually opened/closed, need to restore that. */
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 22f06941aa..7b8e809de7 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -180,6 +180,11 @@ EXTERN int compl_cont_status INIT(= 0);
# define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
// expansion, (eg use complete=.)
+EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
+EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
+EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
+EXTERN hlf_T edit_submode_highl; // highl. method for extra info
+
// state for putting characters in the message area
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
EXTERN int msg_col;
@@ -328,9 +333,10 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_ENV -4 // for sourcing environment variable
#define SID_ERROR -5 // option was reset because of an error
#define SID_NONE -6 // don't set scriptID
-#define SID_LUA -7 // for Lua scripts/chunks
-#define SID_API_CLIENT -8 // for API clients
-#define SID_STR -9 // for sourcing a string
+#define SID_WINLAYOUT -7 // changing window size
+#define SID_LUA -8 // for Lua scripts/chunks
+#define SID_API_CLIENT -9 // for API clients
+#define SID_STR -10 // for sourcing a string
// Script CTX being sourced or was sourced to define the current function.
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
@@ -639,10 +645,6 @@ EXTERN int arrow_used; // Normally false, set to true after
// to call u_sync()
EXTERN bool ins_at_eol INIT(= false); // put cursor after eol when
// restarting edit after CTRL-O
-EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
-EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
-EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
-EXTERN hlf_T edit_submode_highl; // highl. method for extra info
EXTERN int no_abbr INIT(= true); // true when no abbreviations loaded
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 2dad8fb781..31615e744a 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -1865,7 +1865,7 @@ static void cs_release_csp(size_t i, bool freefnpp)
alive = false; // cscope process no longer exists
break;
}
- os_delay(50L, false); // sleep 50ms
+ os_delay(50L, false); // sleep 50 ms
}
}
if (alive)
diff --git a/src/nvim/log.h b/src/nvim/log.h
index f2e74df031..17d754c033 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -17,10 +17,11 @@
#endif
-#define DEBUG_LOG_LEVEL 0
-#define INFO_LOG_LEVEL 1
-#define WARN_LOG_LEVEL 2
-#define ERROR_LOG_LEVEL 3
+#define TRACE_LOG_LEVEL 0
+#define DEBUG_LOG_LEVEL 1
+#define INFO_LOG_LEVEL 2
+#define WARN_LOG_LEVEL 3
+#define ERROR_LOG_LEVEL 4
#define DLOG(...)
#define DLOGN(...)
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index fd53bffe94..eb54ff28ee 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -39,6 +39,16 @@ assert(vim)
vim.inspect = package.loaded['vim.inspect']
assert(vim.inspect)
+vim.log = {
+ levels = {
+ TRACE = 0;
+ DEBUG = 1;
+ INFO = 2;
+ WARN = 3;
+ ERROR = 4;
+ }
+}
+
-- Internal-only until comments in #8107 are addressed.
-- Returns:
-- {errcode}, {output}
@@ -485,6 +495,23 @@ function vim.defer_fn(fn, timeout)
return timer
end
+
+--- Notification provider
+--- without a runtime, writes to :Messages
+-- see :help nvim_notify
+--@param msg Content of the notification to show to the user
+--@param log_level Optional log level
+--@param opts Dictionary with optional options (timeout, etc)
+function vim.notify(msg, log_level, _opts)
+
+ if log_level == vim.log.levels.ERROR then
+ vim.api.nvim_err_writeln(msg)
+ else
+ vim.api.nvim_echo({{msg}}, true, {})
+ end
+end
+
+
local on_keystroke_callbacks = {}
--- Register a lua {fn} with an {id} to be run after every keystroke.
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 9f71df3a46..7064f2a068 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1404,9 +1404,9 @@ static void load_plugins(void)
static void handle_quickfix(mparm_T *paramp)
{
if (paramp->edit_type == EDIT_QF) {
- if (paramp->use_ef != NULL)
- set_string_option_direct((char_u *)"ef", -1,
- paramp->use_ef, OPT_FREE, SID_CARG);
+ if (paramp->use_ef != NULL) {
+ set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG);
+ }
vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
msg_putchar('\n');
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 31dc6b3649..293a4d01db 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -4142,7 +4142,7 @@ void goto_byte(long cnt)
if (lnum < 1) { // past the end
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
curwin->w_curswant = MAXCOL;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
} else {
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = (colnr_T)boff;
diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h
index 9a6f29a908..dc4755f83d 100644
--- a/src/nvim/memline_defs.h
+++ b/src/nvim/memline_defs.h
@@ -45,16 +45,16 @@ typedef struct memline {
memfile_T *ml_mfp; // pointer to associated memfile
+ infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs)
+ int ml_stack_top; // current top of ml_stack
+ int ml_stack_size; // total number of entries in ml_stack
+
#define ML_EMPTY 1 // empty buffer
#define ML_LINE_DIRTY 2 // cached line was changed and allocated
#define ML_LOCKED_DIRTY 4 // ml_locked was changed
#define ML_LOCKED_POS 8 // ml_locked needs positive block number
int ml_flags;
- infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs)
- int ml_stack_top; // current top of ml_stack
- int ml_stack_size; // total number of entries in ml_stack
-
linenr_T ml_line_lnum; // line number of cached line, 0 if not valid
char_u *ml_line_ptr; // pointer to cached line
size_t ml_line_offset; // cached byte offset of ml_line_lnum
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 7094d3be90..ac3b7768e6 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -81,7 +81,7 @@ ex_menu(exarg_T *eap)
// kFalse for "menu disable
vimmenu_T menuarg;
- modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu);
+ modes = get_menu_cmd_modes((char *)eap->cmd, eap->forceit, &noremap, &unmenu);
arg = eap->arg;
for (;; ) {
@@ -912,7 +912,9 @@ static int expand_emenu; /* TRUE for ":emenu" command */
/*
* Work out what to complete when doing command line completion of menu names.
*/
-char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forceit)
+char_u *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char_u *arg,
+ bool forceit)
+ FUNC_ATTR_NONNULL_ALL
{
char_u *after_dot;
char_u *p;
@@ -1178,7 +1180,7 @@ static bool menu_namecmp(const char_u *const name, const char_u *const mname)
/// to whether the command is an "unmenu" command.
int
get_menu_cmd_modes(
- const char_u * cmd,
+ const char *cmd,
bool forceit,
int *noremap,
int *unmenu
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index fcffe64595..34c43da0f7 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -412,7 +412,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
s = ml_get_buf(wp->w_buffer, lnum, FALSE);
if (*s == NUL) /* empty line */
return 1;
- col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
+ col = win_linetabsize(wp, s, MAXCOL);
// If list mode is on, then the '$' at the end of the line may take up one
// extra column.
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index ff471ea978..f28658aa29 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -72,9 +72,7 @@ int jump_to_mouse(int flags,
int row = mouse_row;
int col = mouse_col;
int grid = mouse_grid;
- int mouse_char;
int fdc = 0;
- ScreenGrid *gp = &default_grid;
mouse_past_bottom = false;
mouse_past_eol = false;
@@ -303,25 +301,6 @@ retnomove:
}
}
- // Remember the character under the mouse, might be one of foldclose or
- // foldopen fillchars in the fold column.
- if (ui_has(kUIMultigrid)) {
- gp = &curwin->w_grid;
- }
- if (row >= 0 && row < Rows && col >= 0 && col <= Columns
- && gp->chars != NULL) {
- mouse_char = utf_ptr2char(gp->chars[gp->line_offset[row]
- + (unsigned)col]);
- } else {
- mouse_char = ' ';
- }
-
- // Check for position outside of the fold column.
- if (curwin->w_p_rl ? col < curwin->w_width_inner - fdc :
- col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
- mouse_char = ' ';
- }
-
// compute the position in the buffer line from the posn on the screen
if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum)) {
mouse_past_bottom = true;
@@ -362,11 +341,7 @@ retnomove:
count |= CURSOR_MOVED; // Cursor has moved
}
- if (mouse_char == curwin->w_p_fcs_chars.foldclosed) {
- count |= MOUSE_FOLD_OPEN;
- } else if (mouse_char != ' ') {
- count |= MOUSE_FOLD_CLOSE;
- }
+ count |= mouse_check_fold();
return count;
}
@@ -738,3 +713,47 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
return col + nudge;
}
+
+// Check clicked cell is foldcolumn
+int mouse_check_fold(void)
+{
+ int grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+ int mouse_char = ' ';
+
+ win_T *wp;
+
+ wp = mouse_find_win(&grid, &row, &col);
+
+ if (wp && mouse_row >= 0 && mouse_row < Rows
+ && mouse_col >= 0 && mouse_col <= Columns) {
+ int multigrid = ui_has(kUIMultigrid);
+ ScreenGrid *gp = multigrid ? &wp->w_grid : &default_grid;
+ int fdc = win_fdccol_count(wp);
+
+ row = multigrid && mouse_grid == 0 ? row : mouse_row;
+ col = multigrid && mouse_grid == 0 ? col : mouse_col;
+
+ // Remember the character under the mouse, might be one of foldclose or
+ // foldopen fillchars in the fold column.
+ if (gp->chars != NULL) {
+ mouse_char = utf_ptr2char(gp->chars[gp->line_offset[row]
+ + (unsigned)col]);
+ }
+
+ // Check for position outside of the fold column.
+ if (wp->w_p_rl ? col < wp->w_width_inner - fdc :
+ col >= fdc + (cmdwin_type == 0 ? 0 : 1)) {
+ mouse_char = ' ';
+ }
+ }
+
+ if (mouse_char == wp->w_p_fcs_chars.foldclosed) {
+ return MOUSE_FOLD_OPEN;
+ } else if (mouse_char != ' ') {
+ return MOUSE_FOLD_CLOSE;
+ }
+
+ return 0;
+}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index a6afdc27d9..1210a3365a 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -888,11 +888,11 @@ void curs_columns(
} else {
n = plines;
}
- if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width) {
+ if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width - so) {
extra += 2;
}
- if (extra == 3 || plines < so * 2) {
+ if (extra == 3 || plines <= so * 2) {
// not enough room for 'scrolloff', put cursor in the middle
n = wp->w_virtcol / width;
if (n > wp->w_height_inner / 2) {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 8f22243348..0b4e2e1f23 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2404,8 +2404,8 @@ do_mouse (
start_visual.lnum = 0;
- /* Check for clicking in the tab page line. */
- if (mouse_row == 0 && firstwin->w_winrow > 0) {
+ // Check for clicking in the tab page line.
+ if (mouse_grid <= 1 && mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
move_tab_to_mouse();
@@ -5225,16 +5225,13 @@ static void nv_left(cmdarg_T *cap)
* 'h' wraps to previous line if 'whichwrap' has 'h'.
* CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
*/
- if ( (((cap->cmdchar == K_BS
- || cap->cmdchar == Ctrl_H)
- && vim_strchr(p_ww, 'b') != NULL)
- || (cap->cmdchar == 'h'
- && vim_strchr(p_ww, 'h') != NULL)
- || (cap->cmdchar == K_LEFT
- && vim_strchr(p_ww, '<') != NULL))
- && curwin->w_cursor.lnum > 1) {
- --(curwin->w_cursor.lnum);
- coladvance((colnr_T)MAXCOL);
+ if ((((cap->cmdchar == K_BS || cap->cmdchar == Ctrl_H)
+ && vim_strchr(p_ww, 'b') != NULL)
+ || (cap->cmdchar == 'h' && vim_strchr(p_ww, 'h') != NULL)
+ || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL))
+ && curwin->w_cursor.lnum > 1) {
+ curwin->w_cursor.lnum--;
+ coladvance(MAXCOL);
curwin->w_set_curswant = true;
// When the NL before the first char has to be deleted we
@@ -5792,16 +5789,20 @@ static void nv_percent(cmdarg_T *cap)
} else {
cap->oap->motion_type = kMTLineWise;
setpcmark();
- /* Round up, so CTRL-G will give same value. Watch out for a
- * large line count, the line number must not go negative! */
- if (curbuf->b_ml.ml_line_count > 1000000)
+ // Round up, so 'normal 100%' always jumps at the line line.
+ // Beyond 21474836 lines, (ml_line_count * 100 + 99) would
+ // overflow on 32-bits, so use a formula with less accuracy
+ // to avoid overflows.
+ if (curbuf->b_ml.ml_line_count >= 21474836) {
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99L)
/ 100L * cap->count0;
- else
+ } else {
curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
cap->count0 + 99L) / 100L;
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ }
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
beginline(BL_SOL | BL_FIX);
}
} else { // "%" : go to matching paren
@@ -6467,7 +6468,7 @@ static void nv_visual(cmdarg_T *cap)
}
if (resel_VIsual_vcol == MAXCOL) {
curwin->w_curswant = MAXCOL;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
} else if (VIsual_mode == Ctrl_V) {
validate_virtcol();
assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
@@ -7570,6 +7571,12 @@ static void nv_esc(cmdarg_T *cap)
got_int = false; /* don't stop executing autocommands et al. */
return;
}
+ } else if (cmdwin_type != 0 && ex_normal_busy) {
+ // When :normal runs out of characters while in the command line window
+ // vgetorpeek() will return ESC. Exit the cmdline window to break the
+ // loop.
+ cmdwin_result = K_IGNORE;
+ return;
}
if (VIsual_active) {
@@ -7600,7 +7607,7 @@ void set_cursor_for_append_to_line(void)
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
State = INSERT;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
State = save_State;
} else {
curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr());
@@ -7988,7 +7995,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
* line. */
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance((colnr_T)MAXCOL);
+ coladvance(MAXCOL);
}
}
auto_format(false, true);
@@ -8096,7 +8103,7 @@ static void nv_event(cmdarg_T *cap)
// lists or dicts being used.
may_garbage_collect = false;
bool may_restart = (restart_edit != 0);
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
finish_op = false;
if (may_restart) {
// Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 87d092281a..3038fad894 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -3422,15 +3422,11 @@ error:
if (dir == FORWARD)
curbuf->b_op_start.lnum++;
}
- // Skip mark_adjust when adding lines after the last one, there
- // can't be marks there.
- if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
- < curbuf->b_ml.ml_line_count) {
- ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT))
- ? kExtmarkUndo : kExtmarkNOOP;
- mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
- (linenr_T)MAXLNUM, nr_lines, 0L, kind);
- }
+
+ ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT))
+ ? kExtmarkUndo : kExtmarkNOOP;
+ mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
+ (linenr_T)MAXLNUM, nr_lines, 0L, kind);
// note changed text for displaying and folding
if (y_type == kMTCharWise) {
@@ -4309,11 +4305,12 @@ format_lines(
* tabs and spaces, according to current options */
(void)set_indent(get_indent(), SIN_CHANGED);
- /* put cursor on last non-space */
- State = NORMAL; /* don't go past end-of-line */
- coladvance((colnr_T)MAXCOL);
- while (curwin->w_cursor.col && ascii_isspace(gchar_cursor()))
+ // put cursor on last non-space
+ State = NORMAL; // don't go past end-of-line
+ coladvance(MAXCOL);
+ while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
dec_cursor();
+ }
/* do the formatting, without 'showmode' */
State = INSERT; /* for open_line() */
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 74bf6f0590..ac25c86b5f 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1942,6 +1942,7 @@ static void didset_options(void)
(void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
(void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
(void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
+ (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
(void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
(void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
(void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
@@ -2119,9 +2120,9 @@ static int shada_idx = -1;
// "set_sid".
void
set_string_option_direct(
- char_u *name,
+ const char *name,
int opt_idx,
- char_u *val,
+ const char_u *val,
int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
int set_sid
)
@@ -2132,7 +2133,7 @@ set_string_option_direct(
int idx = opt_idx;
if (idx == -1) { // Use name.
- idx = findoption((const char *)name);
+ idx = findoption(name);
if (idx < 0) { // Not found (should not happen).
internal_error("set_string_option_direct()");
IEMSG2(_("For option %s"), name);
@@ -3077,6 +3078,10 @@ ambw_end:
if (!parse_winhl_opt(curwin)) {
errmsg = e_invarg;
}
+ } else if (varp == &p_tpf) {
+ if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
} else {
// Options that are a list of flags.
p = NULL;
@@ -3786,7 +3791,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
if (p_terse && p == NULL) {
STRCPY(IObuff, p_shm);
STRCAT(IObuff, "s");
- set_string_option_direct((char_u *)"shm", -1, IObuff, OPT_FREE, 0);
+ set_string_option_direct("shm", -1, IObuff, OPT_FREE, 0);
} else if (!p_terse && p != NULL) { // remove 's' from p_shm
STRMOVE(p, p + 1);
}
@@ -4526,7 +4531,7 @@ bool is_tty_option(const char *name)
#define TCO_BUFFER_SIZE 8
/// @param name TUI-related option
/// @param[out,allocated] value option string value
-bool get_tty_option(char *name, char **value)
+bool get_tty_option(const char *name, char **value)
{
if (strequal(name, "t_Co")) {
if (value) {
@@ -4592,6 +4597,7 @@ bool set_tty_option(const char *name, char *value)
///
/// @return Option index or -1 if option was not found.
static int findoption(const char *const arg)
+ FUNC_ATTR_NONNULL_ALL
{
return findoption_len(arg, strlen(arg));
}
@@ -4605,17 +4611,17 @@ static int findoption(const char *const arg)
/// hidden String option: -2.
/// unknown option: -3.
int get_option_value(
- char_u *name,
+ const char *name,
long *numval,
char_u **stringval, ///< NULL when only checking existence
int opt_flags
)
{
- if (get_tty_option((char *)name, (char **)stringval)) {
+ if (get_tty_option(name, (char **)stringval)) {
return 0;
}
- int opt_idx = findoption((const char *)name);
+ int opt_idx = findoption(name);
if (opt_idx < 0) { // Unknown option.
return -3;
}
@@ -7049,7 +7055,7 @@ void set_fileformat(int eol_style, int opt_flags)
// p is NULL if "eol_style" is EOL_UNKNOWN.
if (p != NULL) {
- set_string_option_direct((char_u *)"ff",
+ set_string_option_direct("ff",
-1,
(char_u *)p,
OPT_FREE | opt_flags,
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 683afc670e..43b0107800 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -621,6 +621,19 @@ EXTERN int p_sta; // 'smarttab'
EXTERN int p_sb; // 'splitbelow'
EXTERN long p_tpm; // 'tabpagemax'
EXTERN char_u *p_tal; // 'tabline'
+EXTERN char_u *p_tpf; // 'termpastefilter'
+EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter'
+#ifdef IN_OPTION_C
+static char *(p_tpf_values[]) =
+ { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
+#endif
+# define TPF_BS 0x001
+# define TPF_HT 0x002
+# define TPF_FF 0x004
+# define TPF_ESC 0x008
+# define TPF_DEL 0x010
+# define TPF_C0 0x020
+# define TPF_C1 0x040
EXTERN char_u *p_sps; // 'spellsuggest'
EXTERN int p_spr; // 'splitright'
EXTERN int p_sol; // 'startofline'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index df2bfbce34..fe108ef1cc 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2821,6 +2821,14 @@ return {
defaults={if_true={vi=false}}
},
{
+ full_name='termpastefilter', abbreviation='tpf',
+ type='string', list='onecomma', scope={'global'},
+ deny_duplicates=true,
+ vim=true,
+ varname='p_tpf',
+ defaults={if_true={vi="", vim="BS,HT,ESC,DEL"}}
+ },
+ {
full_name='terse',
short_desc=N_("hides notification of search wrap"),
type='bool', scope={'global'},
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 9d6518841a..eca245650a 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -159,16 +159,28 @@ bool os_char_avail(void)
return inbuf_poll(0, NULL) == kInputAvail;
}
-// Check for CTRL-C typed by reading all available characters.
+/// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
+///
+/// This invokes a full libuv loop iteration which can be quite costly.
+/// Prefer `line_breakcheck()` if called in a busy inner loop.
+///
+/// Caller must at least check `got_int` before calling this function again.
+/// checking for other low-level input state like `input_available()` might
+/// also be relevant (i e to throttle idle processing when user input is
+/// available)
void os_breakcheck(void)
{
+ if (got_int) {
+ return;
+ }
+
int save_us = updating_screen;
// We do not want screen_resize() to redraw here.
+ // TODO(bfredl): we are already special casing redraw events, is this
+ // hack still needed?
updating_screen++;
- if (!got_int) {
- loop_poll_events(&main_loop, 0);
- }
+ loop_poll_events(&main_loop, 0);
updating_screen = save_us;
}
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index 348a139e79..d794969ab5 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -182,8 +182,6 @@ static void init_child(PtyProcess *ptyproc)
char *prog = ptyproc->process.argv[0];
assert(proc->env);
- tv_dict_add_str(proc->env, S_LEN("TERM"),
- ptyproc->term_name ? ptyproc->term_name : "ansi");
environ = tv_dict_to_env(proc->env);
execvp(prog, proc->argv);
ELOG("execvp failed: %s: %s", strerror(errno), prog);
diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h
index f7c57b3839..8c822eafad 100644
--- a/src/nvim/os/pty_process_unix.h
+++ b/src/nvim/os/pty_process_unix.h
@@ -17,7 +17,6 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data)
{
PtyProcess rv;
rv.process = process_init(loop, kProcessTypePty, data);
- rv.term_name = NULL;
rv.width = 80;
rv.height = 24;
rv.tty_fd = -1;
diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h
index 8ad5ba7286..f8ec79a3d6 100644
--- a/src/nvim/os/pty_process_win.h
+++ b/src/nvim/os/pty_process_win.h
@@ -37,7 +37,6 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data)
{
PtyProcess rv;
rv.process = process_init(loop, kProcessTypePty, data);
- rv.term_name = NULL;
rv.width = 80;
rv.height = 24;
rv.object.winpty = NULL;
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 2de7e00ddb..3e1713fbdd 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -342,7 +342,7 @@ int path_fnamencmp(const char *const fname1, const char *const fname2,
p1 += utfc_ptr2len((const char_u *)p1);
p2 += utfc_ptr2len((const char_u *)p2);
}
- return c1 - c2;
+ return p_fic ? CH_FOLD(c1) - CH_FOLD(c2) : c1 - c2;
#else
if (p_fic) {
return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len);
diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index 8e86ea08c5..b7c4b6ef92 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -1,6 +1,9 @@
#ifndef NVIM_POS_H
#define NVIM_POS_H
+// for INT_MAX, LONG_MAX et al.
+#include <limits.h>
+
typedef long linenr_T; // line number type
/// Format used to print values which have linenr_T type
#define PRIdLINENR "ld"
@@ -12,8 +15,8 @@ typedef int colnr_T;
/// Maximal (invalid) line number
enum { MAXLNUM = 0x7fffffff };
-/// Maximal column number, 31 bits
-enum { MAXCOL = 0x7fffffff };
+/// Maximal column number
+enum { MAXCOL = INT_MAX };
// Minimum line number
enum { MINLNUM = 1 };
// minimum column number
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index e074f73d04..dfd38a6eca 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3665,7 +3665,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height)
static void qf_set_title_var(qf_list_T *qfl)
{
if (qfl->qf_title != NULL) {
- set_internal_string_var((char_u *)"w:quickfix_title", qfl->qf_title);
+ set_internal_string_var("w:quickfix_title", qfl->qf_title);
}
}
@@ -4951,7 +4951,7 @@ void ex_cfile(exarg_T *eap)
}
}
if (*eap->arg != NUL) {
- set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0);
+ set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0);
}
char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a78f905a70..20e3cc0a2e 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -143,7 +143,7 @@ long tab_page_click_defs_size = 0;
// for line_putchar. Contains the state that needs to be remembered from
// putting one character to the next.
typedef struct {
- const char_u *p;
+ const char *p;
int prev_c; // previous Arabic character
int prev_c1; // first composing char for prev_c
} LineState;
@@ -1857,7 +1857,7 @@ static int compute_foldcolumn(win_T *wp, int col)
/// Handles composing chars and arabic shaping state.
static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
{
- const char_u *p = s->p;
+ const char_u *p = (char_u *)s->p;
int cells = utf_ptr2cells(p);
int c_len = utfc_ptr2len(p);
int u8c, u8cc[MAX_MCO];
@@ -2082,7 +2082,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
int line_attr_lowprio = 0; // low-priority attribute for the line
matchitem_T *cur; // points to the match list
match_T *shl; // points to search_hl or a match
- int shl_flag; // flag to indicate whether search_hl
+ bool shl_flag; // flag to indicate whether search_hl
// has been processed or not
bool prevcol_hl_flag; // flag to indicate whether prevcol
// equals startcol of search_hl or one
@@ -2870,6 +2870,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& vcol >= (long)wp->w_virtcol)
|| (number_only && draw_state > WL_NR))
&& filler_todo <= 0) {
+ draw_virt_text(buf, &col, grid->Columns);
grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
// Pretend we have finished updating the window. Except when
@@ -2950,16 +2951,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
*/
v = (long)(ptr - line);
cur = wp->w_match_head;
- shl_flag = FALSE;
- while (cur != NULL || shl_flag == FALSE) {
- if (shl_flag == FALSE
- && ((cur != NULL
- && cur->priority > SEARCH_HL_PRIORITY)
- || cur == NULL)) {
+ shl_flag = false;
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
- shl_flag = TRUE;
- } else
+ shl_flag = true;
+ } else {
shl = &cur->hl;
+ }
if (cur != NULL) {
cur->pos.cur = 0;
}
@@ -2984,7 +2984,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
has_match_conc = v == (long)shl->startcol ? 2 : 1;
match_conc = cur->conceal_char;
} else {
- has_match_conc = match_conc = 0;
+ has_match_conc = 0;
}
} else if (v == (long)shl->endcol) {
shl->attr_cur = 0;
@@ -3026,16 +3026,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
search_attr_from_match = false;
search_attr = search_hl.attr_cur;
cur = wp->w_match_head;
- shl_flag = FALSE;
- while (cur != NULL || shl_flag == FALSE) {
- if (shl_flag == FALSE
- && ((cur != NULL
- && cur->priority > SEARCH_HL_PRIORITY)
- || cur == NULL)) {
+ shl_flag = false;
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
- shl_flag = TRUE;
- } else
+ shl_flag = true;
+ } else {
shl = &cur->hl;
+ }
if (shl->attr_cur != 0) {
search_attr = shl->attr_cur;
search_attr_from_match = shl != &search_hl;
@@ -3048,6 +3047,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& (wp->w_p_list && lcs_eol_one == -1)) {
search_attr = 0;
}
+
+ // Do not allow a conceal over EOL otherwise EOL will be missed
+ // and bad things happen.
+ if (*ptr == NUL) {
+ has_match_conc = 0;
+ }
}
if (diff_hlf != (hlf_T)0) {
@@ -3393,7 +3398,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
if (has_decor && v > 0) {
- int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1,
+ int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off,
&decor_state);
if (extmark_attr != 0) {
if (!attr_pri) {
@@ -3672,12 +3677,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
char_attr = conceal_attr;
if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1)
- && (syn_get_sub_char() != NUL || match_conc
+ && (syn_get_sub_char() != NUL
+ || (has_match_conc && match_conc)
|| wp->w_p_cole == 1)
&& wp->w_p_cole != 3) {
// First time at this concealed item: display one
// character.
- if (match_conc) {
+ if (has_match_conc && match_conc) {
c = match_conc;
} else if (syn_get_sub_char() != NUL) {
c = syn_get_sub_char();
@@ -3837,16 +3843,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// 'search_hl' and the match list.
char_attr = search_hl.attr;
cur = wp->w_match_head;
- shl_flag = FALSE;
- while (cur != NULL || shl_flag == FALSE) {
- if (shl_flag == FALSE
- && ((cur != NULL
- && cur->priority > SEARCH_HL_PRIORITY)
- || cur == NULL)) {
+ shl_flag = false;
+ while (cur != NULL || !shl_flag) {
+ if (!shl_flag
+ && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) {
shl = &search_hl;
- shl_flag = TRUE;
- } else
+ shl_flag = true;
+ } else {
shl = &cur->hl;
+ }
if ((ptr - line) - 1 == (long)shl->startcol
&& (shl == &search_hl || !shl->is_addpos)) {
char_attr = shl->attr;
@@ -3915,7 +3920,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
int i;
size_t virt_pos = 0;
- LineState s = LINE_STATE((char_u *)"");
+ LineState s = LINE_STATE("");
int virt_attr = 0;
// Make sure alignment is the same regardless
@@ -3959,7 +3964,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
if (do_virttext && !delay_virttext) {
if (*s.p == NUL) {
if (virt_pos < virt_text.size) {
- s.p = (char_u *)kv_A(virt_text, virt_pos).text;
+ s.p = kv_A(virt_text, virt_pos).text;
int hl_id = kv_A(virt_text, virt_pos).hl_id;
virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
virt_pos++;
@@ -4025,6 +4030,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
col += n;
}
}
+
+ draw_virt_text(buf, &col, grid->Columns);
grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
row++;
@@ -4243,7 +4250,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& (grid->Columns == Columns // Window spans the width of the screen,
|| ui_has(kUIMultigrid)) // or has dedicated grid.
&& !wp->w_p_rl; // Not right-to-left.
- grid_put_linebuf(grid, row, 0, col - boguscols, grid->Columns, wp->w_p_rl,
+
+ int draw_col = col - boguscols;
+ draw_virt_text(buf, &draw_col, grid->Columns);
+ grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
if (wrap) {
ScreenGrid *current_grid = grid;
@@ -4319,6 +4329,43 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
return row;
}
+void draw_virt_text(buf_T *buf, int *end_col, int max_col)
+{
+ DecorState *state = &decor_state;
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ HlRange *item = &kv_A(state->active, i);
+ if (item->start_row == state->row && item->virt_text
+ && item->virt_text_pos == kVTOverlay
+ && item->virt_col >= 0) {
+ VirtText vt = *item->virt_text;
+ LineState s = LINE_STATE("");
+ int virt_attr = 0;
+ int col = item->virt_col;
+ size_t virt_pos = 0;
+ item->virt_col = -2; // deactivate
+
+ while (col < max_col) {
+ if (!*s.p) {
+ if (virt_pos == kv_size(vt)) {
+ break;
+ }
+ s.p = kv_A(vt, virt_pos).text;
+ int hl_id = kv_A(vt, virt_pos).hl_id;
+ virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
+ virt_pos++;
+ continue;
+ }
+ int cells = line_putchar(&s, &linebuf_char[col], 2, false);
+ linebuf_attr[col++] = virt_attr;
+ if (cells > 1) {
+ linebuf_attr[col++] = virt_attr;
+ }
+ }
+ *end_col = MAX(*end_col, col);
+ }
+ }
+}
+
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the
@@ -5092,9 +5139,9 @@ static void redraw_custom_statusline(win_T *wp)
// When there is an error disable the statusline, otherwise the
// display is messed up with errors and a redraw triggers the problem
// again and again.
- set_string_option_direct((char_u *)"statusline", -1,
- (char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ set_string_option_direct("statusline", -1, (char_u *)"",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
}
did_emsg |= saved_did_emsg;
entered = false;
@@ -5175,7 +5222,7 @@ get_keymap_str (
static void
win_redr_custom (
win_T *wp,
- int draw_ruler /* TRUE or FALSE */
+ bool draw_ruler
)
{
static int entered = FALSE;
@@ -6852,7 +6899,7 @@ void draw_tabline(void)
did_emsg = false;
win_redr_custom(NULL, false);
if (did_emsg) {
- set_string_option_direct((char_u *)"tabline", -1,
+ set_string_option_direct("tabline", -1,
(char_u *)"", OPT_FREE, SID_ERROR);
}
did_emsg |= saved_did_emsg;
@@ -7105,20 +7152,23 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
return;
- /* Don't draw the ruler while doing insert-completion, it might overwrite
- * the (long) mode message. */
- if (wp == lastwin && lastwin->w_status_height == 0)
- if (edit_submode != NULL)
+ // Don't draw the ruler while doing insert-completion, it might overwrite
+ // the (long) mode message.
+ if (wp == lastwin && lastwin->w_status_height == 0) {
+ if (edit_submode != NULL) {
return;
+ }
+ }
if (*p_ruf) {
int save_called_emsg = called_emsg;
- called_emsg = FALSE;
- win_redr_custom(wp, TRUE);
- if (called_emsg)
- set_string_option_direct((char_u *)"rulerformat", -1,
- (char_u *)"", OPT_FREE, SID_ERROR);
+ called_emsg = false;
+ win_redr_custom(wp, true);
+ if (called_emsg) {
+ set_string_option_direct("rulerformat", -1, (char_u *)"",
+ OPT_FREE, SID_ERROR);
+ }
called_emsg |= save_called_emsg;
return;
}
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 2802da6f7f..84b71d56a0 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -2685,8 +2685,9 @@ fwd_word(
while (--count >= 0) {
/* When inside a range of folded lines, move to the last char of the
* last line. */
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
- coladvance((colnr_T)MAXCOL);
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
sclass = cls();
/*
@@ -2803,8 +2804,9 @@ int end_word(long count, int bigword, int stop, int empty)
while (--count >= 0) {
/* When inside a range of folded lines, move to the last char of the
* last line. */
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
- coladvance((colnr_T)MAXCOL);
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
sclass = cls();
if (inc_cursor() == -1)
return FAIL;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index c898dba890..19c0263cf1 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -24,11 +24,11 @@ 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
+ int priority; // priority for highlighting
bool has_text_or_icon; // has text or icon
+ linenr_T lnum; // line number which has this 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
};
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 6425c9fed5..55f9594de2 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -112,6 +112,7 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/undo.h"
+#include "nvim/ui.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
@@ -2889,8 +2890,14 @@ void spell_suggest(int count)
msg_col = 0;
// Ask for choice.
selected = prompt_for_number(&mouse_used);
- if (mouse_used)
+
+ if (ui_has(kUIMessages)) {
+ ui_call_msg_clear();
+ }
+
+ if (mouse_used) {
selected -= lines_left;
+ }
lines_left = Rows; // avoid more prompt
// don't delay for 'smd' in normal_cmd()
msg_scroll = msg_scroll_save;
@@ -6624,7 +6631,7 @@ void ex_spelldump(exarg_T *eap)
if (no_spell_checking(curwin)) {
return;
}
- get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL);
+ get_option_value("spl", &dummy, &spl, OPT_LOCAL);
// Create a new empty buffer in a new window.
do_cmdline_cmd("new");
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 90af010164..3c125959a9 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -5387,7 +5387,8 @@ spell_add_word (
len, word, NameBuff);
}
}
- if (fseek(fd, fpos_next, SEEK_SET) <= 0) {
+ if (fseek(fd, fpos_next, SEEK_SET) != 0) {
+ PERROR(_("Seek error in spellfile"));
break;
}
}
diff --git a/src/nvim/state.c b/src/nvim/state.c
index b195c1d96b..a3c74789d1 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -75,6 +75,34 @@ getkey:
}
}
+/// process events on main_loop, but interrupt if input is available
+///
+/// This should be used to handle K_EVENT in states accepting input
+/// otherwise bursts of events can block break checking indefinitely.
+void state_handle_k_event(void)
+{
+ while (true) {
+ Event event = multiqueue_get(main_loop.events);
+ if (event.handler) {
+ event.handler(event.argv);
+ }
+
+ if (multiqueue_empty(main_loop.events)) {
+ // don't breakcheck before return, caller should return to main-loop
+ // and handle input already.
+ return;
+ }
+
+ // TODO(bfredl): as an further micro-optimization, we could check whether
+ // event.handler already checked input.
+ os_breakcheck();
+ if (input_available() || got_int) {
+ return;
+ }
+ }
+}
+
+
/// Return true if in the current mode we need to use virtual.
bool virtual_active(void)
{
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index f99eca7953..547d953be9 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -3419,7 +3419,7 @@ static void syn_cmd_on(exarg_T *eap, int syncing)
*/
static void syn_cmd_enable(exarg_T *eap, int syncing)
{
- set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
+ set_internal_string_var("syntax_cmd", (char_u *)"enable");
syn_cmd_onoff(eap, "syntax");
do_unlet(S_LEN("g:syntax_cmd"), true);
}
@@ -3432,7 +3432,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing)
{
eap->nextcmd = check_nextcmd(eap->arg);
if (!eap->skip) {
- set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
+ set_internal_string_var("syntax_cmd", (char_u *)"reset");
do_cmdline_cmd("runtime! syntax/syncolor.vim");
do_unlet(S_LEN("g:syntax_cmd"), true);
}
@@ -5614,14 +5614,14 @@ void ex_ownsyntax(exarg_T *eap)
// Move value of b:current_syntax to w:current_syntax.
new_value = get_var_value("b:current_syntax");
if (new_value != NULL) {
- set_internal_string_var((char_u *)"w:current_syntax", new_value);
+ set_internal_string_var("w:current_syntax", new_value);
}
// Restore value of b:current_syntax.
if (old_value == NULL) {
do_unlet(S_LEN("b:current_syntax"), true);
} else {
- set_internal_string_var((char_u *)"b:current_syntax", old_value);
+ set_internal_string_var("b:current_syntax", old_value);
xfree(old_value);
}
}
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 84ca240734..4ea298fba9 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -3152,7 +3152,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
bool is_static;
ret = find_tags(pat, &num_matches, &matches,
- TAG_REGEXP | TAG_NOIC, (int)MAXCOL, buf_fname);
+ TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname);
if (ret == OK && num_matches > 0) {
for (i = 0; i < num_matches; ++i) {
int parse_result = parse_match(matches[i], &tp);
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 39e2ca6171..f6995cddb6 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -457,7 +457,7 @@ static int terminal_execute(VimState *state, int key)
case K_EVENT:
// We cannot let an event free the terminal yet. It is still needed.
s->term->refcount++;
- multiqueue_process_events(main_loop.events);
+ state_handle_k_event();
s->term->refcount--;
if (s->term->buf_handle == 0) {
s->close = true;
@@ -535,8 +535,44 @@ void terminal_send(Terminal *term, char *data, size_t size)
term->opts.write_cb(data, size, term->opts.data);
}
+static bool is_filter_char(int c)
+{
+ unsigned int flag = 0;
+ switch (c) {
+ case 0x08:
+ flag = TPF_BS;
+ break;
+ case 0x09:
+ flag = TPF_HT;
+ break;
+ case 0x0A:
+ case 0x0D:
+ break;
+ case 0x0C:
+ flag = TPF_FF;
+ break;
+ case 0x1b:
+ flag = TPF_ESC;
+ break;
+ case 0x7F:
+ flag = TPF_DEL;
+ break;
+ default:
+ if (c < ' ') {
+ flag = TPF_C0;
+ } else if (c >= 0x80 && c <= 0x9F) {
+ flag = TPF_C1;
+ }
+ }
+ return !!(tpf_flags & flag);
+}
+
void terminal_paste(long count, char_u **y_array, size_t y_size)
{
+ vterm_keyboard_start_paste(curbuf->terminal->vt);
+ terminal_flush_output(curbuf->terminal);
+ size_t buff_len = STRLEN(y_array[0]);
+ char_u *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
// feed the lines to the terminal
for (size_t j = 0; j < y_size; j++) {
@@ -544,9 +580,28 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
// terminate the previous line
terminal_send(curbuf->terminal, "\n", 1);
}
- terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j]));
+ size_t len = STRLEN(y_array[j]);
+ if (len > buff_len) {
+ buff = xrealloc(buff, len);
+ buff_len = len;
+ }
+ char_u *dst = buff;
+ char_u *src = y_array[j];
+ while (*src != '\0') {
+ len = (size_t)utf_ptr2len(src);
+ int c = utf_ptr2char(src);
+ if (!is_filter_char(c)) {
+ memcpy(dst, src, len);
+ dst += len;
+ }
+ src += len;
+ }
+ terminal_send(curbuf->terminal, (char *)buff, (size_t)(dst - buff));
}
}
+ xfree(buff);
+ vterm_keyboard_end_paste(curbuf->terminal->vt);
+ terminal_flush_output(curbuf->terminal);
}
void terminal_flush_output(Terminal *term)
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 4f056abdc0..daf3c9c110 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -37,7 +37,6 @@ source test_recover.vim
source test_scroll_opt.vim
source test_sort.vim
source test_sha256.vim
-source test_statusline.vim
source test_suspend.vim
source test_syn_attr.vim
source test_tabline.vim
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index 92fedf9bfb..a1ef8325ec 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -80,6 +80,24 @@ func Test_argadd()
call assert_equal(0, len(argv()))
endfunc
+func Test_argadd_empty_curbuf()
+ new
+ let curbuf = bufnr('%')
+ call writefile(['test', 'Xargadd'], 'Xargadd')
+ " must not re-use the current buffer.
+ argadd Xargadd
+ call assert_equal(curbuf, bufnr('%'))
+ call assert_equal('', bufname('%'))
+ call assert_equal(1, line('$'))
+ rew
+ call assert_notequal(curbuf, bufnr('%'))
+ call assert_equal('Xargadd', bufname('%'))
+ call assert_equal(2, line('$'))
+
+ %argd
+ bwipe!
+endfunc
+
func Init_abc()
args a b c
next
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 374ad65aa9..1f3a45a9ab 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1939,4 +1939,15 @@ func Test_autocmd_window()
%bw!
endfunc
+func Test_autocmd_closes_window()
+ au BufNew,BufWinLeave * e %e
+ file yyy
+ au BufNew,BufWinLeave * ball
+ call assert_fails('n xxx', 'E143:')
+
+ bwipe %
+ au! BufNew
+ au! BufWinLeave
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim
index a4c1f62a43..d53acb77d7 100644
--- a/src/nvim/testdir/test_breakindent.vim
+++ b/src/nvim/testdir/test_breakindent.vim
@@ -353,14 +353,19 @@ func Test_breakindent19_sbr_nextpage()
" Scroll down one screen line
setl scrolloff=5
norm! 5gj
- redraw!
let lines = s:screen_lines(1, 20)
let expect = [
- \ "> aaaaaaaaaaaaaaaaaa",
+ \ "aaaaaaaaaaaaaaaaaaaa",
\ "> aaaaaaaaaaaaaaaaaa",
\ "> aaaaaaaaaaaaaaaaaa",
\ ]
call s:compare_lines(expect, lines)
+ redraw!
+ " moving the cursor doesn't change the text offset
+ norm! l
+ redraw!
+ let lines = s:screen_lines(1, 20)
+ call s:compare_lines(expect, lines)
setl breakindent briopt=min:18 sbr=>
norm! 5gj
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
new file mode 100644
index 0000000000..40111fdf06
--- /dev/null
+++ b/src/nvim/testdir/test_buffer.vim
@@ -0,0 +1,33 @@
+" Tests for Vim buffer
+
+func Test_buffer_error()
+ new foo1
+ new foo2
+
+ call assert_fails('buffer foo', 'E93:')
+ call assert_fails('buffer bar', 'E94:')
+ call assert_fails('buffer 0', 'E939:')
+
+ %bwipe
+endfunc
+
+func Test_badd_options()
+ new SomeNewBuffer
+ setlocal numberwidth=3
+ wincmd p
+ badd +1 SomeNewBuffer
+ new SomeNewBuffer
+ call assert_equal(3, &numberwidth)
+ close
+ close
+ bwipe! SomeNewBuffer
+endfunc
+
+func Test_balt()
+ new SomeNewBuffer
+ balt +3 OtherBuffer
+ e #
+ call assert_equal('OtherBuffer', bufname())
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index 076f03fdd8..e038bce08e 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -112,6 +112,17 @@ func Test_deletebufline()
call assert_equal(0, deletebufline(b, 1))
call assert_equal(['b', 'c'], getbufline(b, 1, 2))
exe "bwipe! " . b
+
+ edit XbufOne
+ let one = bufnr()
+ call setline(1, ['a', 'b', 'c'])
+ setlocal nomodifiable
+ split XbufTwo
+ let two = bufnr()
+ call assert_fails('call deletebufline(one, 1)', 'E21:')
+ call assert_equal(two, bufnr())
+ bwipe! XbufTwo
+ bwipe! XbufOne
endfunc
func Test_appendbufline_redraw()
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 39f865144a..a66aee5e02 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -442,6 +442,7 @@ func Test_getcompletion()
set tags&
call assert_fails('call getcompletion("", "burp")', 'E475:')
+ call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
func Test_shellcmd_completion()
diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim
index 7262789ab4..55b230373f 100644
--- a/src/nvim/testdir/test_command_count.vim
+++ b/src/nvim/testdir/test_command_count.vim
@@ -158,7 +158,9 @@ endfunc
func Test_command_count_4()
%argd
let bufnr = bufnr('$')
- arga aa bb cc dd ee ff
+ next aa bb cc dd ee ff
+ call assert_equal(bufnr, bufnr('%'))
+
3argu
let args = []
.,$-argdo call add(args, expand('%'))
diff --git a/src/nvim/testdir/test_conceal.vim b/src/nvim/testdir/test_conceal.vim
new file mode 100644
index 0000000000..1306dbe5cf
--- /dev/null
+++ b/src/nvim/testdir/test_conceal.vim
@@ -0,0 +1,282 @@
+" Tests for 'conceal'.
+
+source check.vim
+CheckFeature conceal
+
+source screendump.vim
+" CheckScreendump
+
+func Test_conceal_two_windows()
+ CheckScreendump
+ let code =<< trim [CODE]
+ let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"]
+ call setline(1, lines)
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2
+ set concealcursor=
+ exe "normal /here\r"
+ new
+ call setline(1, lines)
+ call setline(4, "Second window")
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2
+ set concealcursor=nc
+ exe "normal /here\r"
+ [CODE]
+
+ call writefile(code, 'XTest_conceal')
+ " Check that cursor line is concealed
+ let buf = RunVimInTerminal('-S XTest_conceal', {})
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_01', {})
+
+ " Check that with concealed text vertical cursor movement is correct.
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_02', {})
+
+ " Check that with cursor line is not concealed
+ call term_sendkeys(buf, "j")
+ call term_sendkeys(buf, ":set concealcursor=\r")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_03', {})
+
+ " Check that with cursor line is not concealed when moving cursor down
+ call term_sendkeys(buf, "j")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_04', {})
+
+ " Check that with cursor line is not concealed when switching windows
+ call term_sendkeys(buf, "\<C-W>\<C-W>")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_05', {})
+
+ " Check that with cursor line is only concealed in Normal mode
+ call term_sendkeys(buf, ":set concealcursor=n\r")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_06n', {})
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_06i', {})
+ call term_sendkeys(buf, "\<Esc>/e")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_06c', {})
+ call term_sendkeys(buf, "\<Esc>v")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_06v', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Check that with cursor line is only concealed in Insert mode
+ call term_sendkeys(buf, ":set concealcursor=i\r")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_07n', {})
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_07i', {})
+ call term_sendkeys(buf, "\<Esc>/e")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_07c', {})
+ call term_sendkeys(buf, "\<Esc>v")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_07v', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Check that with cursor line is only concealed in Command mode
+ call term_sendkeys(buf, ":set concealcursor=c\r")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_08n', {})
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_08i', {})
+ call term_sendkeys(buf, "\<Esc>/e")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_08c', {})
+ call term_sendkeys(buf, "\<Esc>v")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_08v', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Check that with cursor line is only concealed in Visual mode
+ call term_sendkeys(buf, ":set concealcursor=v\r")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_09n', {})
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_09i', {})
+ call term_sendkeys(buf, "\<Esc>/e")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_09c', {})
+ call term_sendkeys(buf, "\<Esc>v")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_09v', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Check moving the cursor while in insert mode.
+ call term_sendkeys(buf, ":set concealcursor=\r")
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_10', {})
+ call term_sendkeys(buf, "\<Down>")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_11', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Check the "o" command
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_12', {})
+ call term_sendkeys(buf, "o")
+ call VerifyScreenDump(buf, 'Test_conceal_two_windows_13', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_conceal')
+endfunc
+
+func Test_conceal_with_cursorline()
+ CheckScreendump
+ " Opens a help window, where 'conceal' is set, switches to the other window
+ " where 'cursorline' needs to be updated when the cursor moves.
+ let code =<< trim [CODE]
+ set cursorline
+ normal othis is a test
+ new
+ call setline(1, ["one", "two", "three", "four", "five"])
+ set ft=help
+ normal M
+ [CODE]
+
+ call writefile(code, 'XTest_conceal_cul')
+ let buf = RunVimInTerminal('-S XTest_conceal_cul', {})
+ call VerifyScreenDump(buf, 'Test_conceal_cul_01', {})
+
+ call term_sendkeys(buf, ":wincmd w\r")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_02', {})
+
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_03', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_conceal_cul')
+endfunc
+
+func Test_conceal_resize_term()
+ CheckScreendump
+ let code =<< trim [CODE]
+ call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed')
+ setl cocu=n cole=3
+ syn region CommentCodeSpan matchgroup=Comment start=/`/ end=/`/ concealends
+ normal fb
+ [CODE]
+ call writefile(code, 'XTest_conceal_resize')
+ let buf = RunVimInTerminal('-S XTest_conceal_resize', {'rows': 6})
+ call VerifyScreenDump(buf, 'Test_conceal_resize_01', {})
+
+ call win_execute(buf->win_findbuf()[0], 'wincmd +')
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_conceal_resize_02', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_conceal_resize')
+endfunc
+
+" Tests for correct display (cursor column position) with +conceal and
+" tabulators. Need to run this test in a separate Vim instance. Otherwise the
+" screen is not updated (lazy redraw) and the cursor position is wrong.
+func Test_conceal_cursor_pos()
+ let code =<< trim [CODE]
+ :let l = ['start:', '.concealed. text', "|concealed|\ttext"]
+ :let l += ['', "\t.concealed.\ttext", "\t|concealed|\ttext", '']
+ :let l += [".a.\t.b.\t.c.\t.d.", "|a|\t|b|\t|c|\t|d|"]
+ :call append(0, l)
+ :call cursor(1, 1)
+ :" Conceal settings.
+ :set conceallevel=2
+ :set concealcursor=nc
+ :syntax match test /|/ conceal
+ :" Save current cursor position. Only works in <expr> mode, can't be used
+ :" with :normal because it moves the cursor to the command line. Thanks
+ :" to ZyX <zyx.vim@gmail.com> for the idea to use an <expr> mapping.
+ :let curpos = []
+ :nnoremap <expr> GG ":let curpos += ['".screenrow().":".screencol()."']\n"
+ :normal ztj
+ GGk
+ :" We should end up in the same column when running these commands on the
+ :" two lines.
+ :normal ft
+ GGk
+ :normal $
+ GGk
+ :normal 0j
+ GGk
+ :normal ft
+ GGk
+ :normal $
+ GGk
+ :normal 0j0j
+ GGk
+ :" Same for next test block.
+ :normal ft
+ GGk
+ :normal $
+ GGk
+ :normal 0j
+ GGk
+ :normal ft
+ GGk
+ :normal $
+ GGk
+ :normal 0j0j
+ GGk
+ :" And check W with multiple tabs and conceals in a line.
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal $
+ GGk
+ :normal 0j
+ GGk
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal $
+ GGk
+ :set lbr
+ :normal $
+ GGk
+ :set list listchars=tab:>-
+ :normal 0
+ GGk
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal W
+ GGk
+ :normal $
+ GGk
+ :call writefile(curpos, 'Xconceal_curpos.out')
+ :q!
+
+ [CODE]
+ call writefile(code, 'XTest_conceal_curpos')
+
+ if RunVim([], [], '-s XTest_conceal_curpos')
+ call assert_equal([
+ \ '2:1', '2:17', '2:20', '3:1', '3:17', '3:20', '5:8', '5:25',
+ \ '5:28', '6:8', '6:25', '6:28', '8:1', '8:9', '8:17', '8:25',
+ \ '8:27', '9:1', '9:9', '9:17', '9:25', '9:26', '9:26', '9:1',
+ \ '9:9', '9:17', '9:25', '9:26'], readfile('Xconceal_curpos.out'))
+ endif
+
+ call delete('Xconceal_curpos.out')
+ call delete('XTest_conceal_curpos')
+endfunc
+
+func Test_conceal_eol()
+ new!
+ setlocal concealcursor=n conceallevel=1
+ call setline(1, ["x", ""])
+ call matchaddpos('Conceal', [[2, 1, 1]], 2, -1, {'conceal': 1})
+ redraw!
+
+ call assert_notequal(screenchar(1, 1), screenchar(2, 2))
+ call assert_equal(screenattr(1, 1), screenattr(1, 2))
+ call assert_equal(screenattr(1, 2), screenattr(2, 2))
+ call assert_equal(screenattr(2, 1), screenattr(2, 2))
+
+ set list
+ redraw!
+
+ call assert_equal(screenattr(1, 1), screenattr(2, 2))
+ call assert_notequal(screenattr(1, 1), screenattr(1, 2))
+ call assert_notequal(screenattr(1, 2), screenattr(2, 1))
+
+ set nolist
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index f09a64c329..21c1f98283 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -242,6 +242,63 @@ func Test_diffput_two()
bwipe! b
endfunc
+" :diffput and :diffget completes names of buffers which
+" are in diff mode and which are different then current buffer.
+" No completion when the current window is not in diff mode.
+func Test_diffget_diffput_completion()
+ e Xdiff1 | diffthis
+ botright new Xdiff2
+ botright new Xdiff3 | split | diffthis
+ botright new Xdiff4 | diffthis
+
+ wincmd t
+ call assert_equal('Xdiff1', bufname('%'))
+ call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput Xdiff3 Xdiff4', @:)
+ call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget Xdiff3 Xdiff4', @:)
+ call assert_equal(['Xdiff3', 'Xdiff4'], getcompletion('', 'diff_buffer'))
+
+ " Xdiff2 is not in diff mode, so no completion for :diffput, :diffget
+ wincmd j
+ call assert_equal('Xdiff2', bufname('%'))
+ call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput ', @:)
+ call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget ', @:)
+ call assert_equal([], getcompletion('', 'diff_buffer'))
+
+ " Xdiff3 is split in 2 windows, only the top one is in diff mode.
+ " So completion of :diffput :diffget only happens in the top window.
+ wincmd j
+ call assert_equal('Xdiff3', bufname('%'))
+ call assert_equal(1, &diff)
+ call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput Xdiff1 Xdiff4', @:)
+ call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget Xdiff1 Xdiff4', @:)
+ call assert_equal(['Xdiff1', 'Xdiff4'], getcompletion('', 'diff_buffer'))
+
+ wincmd j
+ call assert_equal('Xdiff3', bufname('%'))
+ call assert_equal(0, &diff)
+ call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput ', @:)
+ call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget ', @:)
+ call assert_equal([], getcompletion('', 'diff_buffer'))
+
+ wincmd j
+ call assert_equal('Xdiff4', bufname('%'))
+ call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffput Xdiff1 Xdiff3', @:)
+ call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"diffget Xdiff1 Xdiff3', @:)
+ call assert_equal(['Xdiff1', 'Xdiff3'], getcompletion('', 'diff_buffer'))
+
+ %bwipe
+endfunc
+
func Test_dp_do_buffer()
e! one
let bn1=bufnr('%')
@@ -964,6 +1021,21 @@ func Test_diff_closeoff()
enew!
endfunc
+func Test_diff_followwrap()
+ new
+ set diffopt+=followwrap
+ set wrap
+ diffthis
+ call assert_equal(1, &wrap)
+ diffoff
+ set nowrap
+ diffthis
+ call assert_equal(0, &wrap)
+ diffoff
+ set diffopt&
+ bwipe!
+endfunc
+
func Test_diff_rnu()
CheckScreendump
@@ -992,6 +1064,18 @@ func Test_diff_rnu()
call delete('Xtest_diff_rnu')
endfunc
+func Test_diff_multilineconceal()
+ new
+ diffthis
+
+ new
+ call matchadd('Conceal', 'a\nb', 9, -1, {'conceal': 'Y'})
+ set cole=2 cocu=n
+ call setline(1, ["a", "b"])
+ diffthis
+ redraw
+endfunc
+
func Test_diff_and_scroll()
" this was causing an ml_get error
set ls=2
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index b6d9687560..d23748a3e3 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -211,6 +211,8 @@ func Test_digraphs()
call Put_Dig("00")
call Put_Dig("el")
call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
+ call assert_fails('digraph xy z', 'E39:')
+ call assert_fails('digraph x', 'E474:')
bw!
endfunc
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index 20508b12d3..98a3e60368 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -131,3 +131,11 @@ func Test_confirm_cmd_cancel()
\ term_getline(buf, 20))}, 1000)
call StopVimInTerminal(buf)
endfunc
+
+" Test for the :winsize command
+func Test_winsize_cmd()
+ call assert_fails('winsize 1', 'E465:')
+ call assert_fails('winsize 1 x', 'E465:')
+ call assert_fails('win_getid(1)', 'E475: Invalid argument: _getid(1)')
+ " Actually changing the window size would be flaky.
+endfunc
diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim
index 99a401d4a4..bd3e9eb4d4 100644
--- a/src/nvim/testdir/test_exit.vim
+++ b/src/nvim/testdir/test_exit.vim
@@ -81,3 +81,32 @@ func Test_exiting()
endif
call delete('Xtestout')
endfunc
+
+" Test for getting the Vim exit code from v:exiting
+func Test_exit_code()
+ call assert_equal(v:null, v:exiting)
+
+ let before =<< trim [CODE]
+ au QuitPre * call writefile(['qp = ' .. v:exiting], 'Xtestout', 'a')
+ au ExitPre * call writefile(['ep = ' .. v:exiting], 'Xtestout', 'a')
+ au VimLeavePre * call writefile(['lp = ' .. v:exiting], 'Xtestout', 'a')
+ au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout', 'a')
+ [CODE]
+
+ if RunVim(before, ['quit'], '')
+ call assert_equal(['qp = null', 'ep = null', 'lp = 0', 'l = 0'], readfile('Xtestout'))
+ endif
+ call delete('Xtestout')
+
+ if RunVim(before, ['cquit'], '')
+ call assert_equal(['lp = 1', 'l = 1'], readfile('Xtestout'))
+ endif
+ call delete('Xtestout')
+
+ if RunVim(before, ['cquit 4'], '')
+ call assert_equal(['lp = 4', 'l = 4'], readfile('Xtestout'))
+ endif
+ call delete('Xtestout')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 7b90ba56e0..09d79979ce 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -399,7 +399,11 @@ function Test_printf_errors()
call assert_fails('echo printf("%d", [])', 'E745:')
call assert_fails('echo printf("%d", 1, 2)', 'E767:')
call assert_fails('echo printf("%*d", 1)', 'E766:')
- call assert_fails('echo printf("%d", 1.2)', 'E805:')
+ call assert_fails('echo printf("%s")', 'E766:')
+ if has('float')
+ call assert_fails('echo printf("%d", 1.2)', 'E805:')
+ call assert_fails('echo printf("%f")')
+ endif
endfunc
function Test_max_min_errors()
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index f9f0ade1f6..6bc5fba5db 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -79,6 +79,7 @@ let s:filename_checks = {
\ 'bc': ['file.bc'],
\ 'bdf': ['file.bdf'],
\ 'bib': ['file.bib'],
+ \ 'beancount': ['file.beancount'],
\ 'bindzone': ['named.root'],
\ 'blank': ['file.bl'],
\ 'bsdl': ['file.bsd', 'file.bsdl'],
@@ -427,6 +428,7 @@ let s:filename_checks = {
\ 'slrnrc': ['.slrnrc'],
\ 'slrnsc': ['file.score'],
\ 'sm': ['sendmail.cf'],
+ \ 'svelte': ['file.svelte'],
\ 'smarty': ['file.tpl'],
\ 'smcl': ['file.hlp', 'file.ihlp', 'file.smcl'],
\ 'smith': ['file.smt', 'file.smith'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 917a5e8eca..5dae8d681a 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -833,6 +833,31 @@ func Test_byte2line_line2byte()
bw!
endfunc
+" Test for charidx()
+func Test_charidx()
+ let a = 'xáb́y'
+ call assert_equal(0, charidx(a, 0))
+ call assert_equal(1, charidx(a, 3))
+ call assert_equal(2, charidx(a, 4))
+ call assert_equal(3, charidx(a, 7))
+ call assert_equal(-1, charidx(a, 8))
+ call assert_equal(-1, charidx('', 0))
+
+ " count composing characters
+ call assert_equal(0, charidx(a, 0, 1))
+ call assert_equal(2, charidx(a, 2, 1))
+ call assert_equal(3, charidx(a, 4, 1))
+ call assert_equal(5, charidx(a, 7, 1))
+ call assert_equal(-1, charidx(a, 8, 1))
+ call assert_equal(-1, charidx('', 0, 1))
+
+ call assert_fails('let x = charidx([], 1)', 'E474:')
+ call assert_fails('let x = charidx("abc", [])', 'E474:')
+ call assert_fails('let x = charidx("abc", 1, [])', 'E474:')
+ call assert_fails('let x = charidx("abc", 1, -1)', 'E474:')
+ call assert_fails('let x = charidx("abc", 1, 2)', 'E474:')
+endfunc
+
func Test_count()
let l = ['a', 'a', 'A', 'b']
call assert_equal(2, count(l, 'a'))
@@ -930,10 +955,20 @@ func Test_Executable()
" get "cat" path and remove the leading /
let catcmd = exepath('cat')[1:]
new
+ " check that the relative path works in /
lcd /
call assert_equal(1, executable(catcmd))
- call assert_equal('/' .. catcmd, exepath(catcmd))
+ " let result = catcmd->exepath()
+ let result = exepath(catcmd)
+ " when using chroot looking for sbin/cat can return bin/cat, that is OK
+ if catcmd =~ '\<sbin\>' && result =~ '\<bin\>'
+ call assert_equal('/' .. substitute(catcmd, '\<sbin\>', 'bin', ''), result)
+ else
+ call assert_equal('/' .. catcmd, result)
+ endif
bwipe
+ else
+ throw 'Skipped: does not work on this platform'
endif
endfunc
diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim
index f9e40a9b43..2cbaf5cb76 100644
--- a/src/nvim/testdir/test_matchadd_conceal.vim
+++ b/src/nvim/testdir/test_matchadd_conceal.vim
@@ -59,9 +59,9 @@ func Test_matchadd_and_conceallevel_3()
setlocal filetype=conf
syntax on
- 1put='# This is a Test'
- " 1234567890123456
- let expect = '#ThisisaTest'
+ 1put='# This is a Test $'
+ " 1234567890123
+ let expect = '#ThisisaTest$'
call cursor(1, 1)
call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'})
@@ -69,22 +69,25 @@ func Test_matchadd_and_conceallevel_3()
let lnum = 2
call assert_equal(expect, Screenline(lnum))
call assert_equal(screenattr(lnum, 1), screenattr(lnum, 2))
- call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7))
- call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10))
- call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 7))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 10))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 12))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 13))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 14))
call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 16))
" more matchadd()
- " 1234567890123456
- let expect = '#Thisisa Test'
+ " 12345678901234
+ let expect = '#Thisisa Test$'
call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'})
redraw!
call assert_equal(expect, Screenline(lnum))
call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 2))
- call assert_equal(screenattr(lnum, 2) , screenattr(lnum, 7))
+ call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 7))
call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 10))
- call assert_equal(screenattr(lnum, 10), screenattr(lnum, 12))
+ call assert_equal(screenattr(lnum, 10), screenattr(lnum, 13))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 14))
call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 16))
call assert_notequal(screenattr(lnum, 10), screenattr(lnum, 16))
@@ -132,15 +135,29 @@ func Test_syn_and_match_conceal()
new
setlocal concealcursor=n conceallevel=1
- 1put='# This is a Test'
- " 1234567890123456
- let expect = '#ZThisZisZaZTest'
+ 1put='# This is a Test '
+ let lnum = 2
call cursor(1, 1)
+
+ " 123456789012345678
+ let expect = '#ZThisZisZaZTestZZ'
call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'})
+ syntax match MyConceal /\%2l / conceal containedin=ALL
+ hi MyConceal ctermbg=4 ctermfg=2
+ redraw!
+
+ call assert_equal(expect, Screenline(lnum))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16))
+
+ syntax clear MyConceal
syntax match MyConceal /\%2l / conceal containedin=ALL cchar=*
redraw!
- let lnum = 2
+
call assert_equal(expect, Screenline(lnum))
call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2))
call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7))
@@ -148,8 +165,8 @@ func Test_syn_and_match_conceal()
call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16))
- " 1234567890123456
- let expect = '#*This*is*a*Test'
+ " 123456789012345678
+ let expect = '#*This*is*a*Test**'
call clearmatches()
redraw!
@@ -160,6 +177,48 @@ func Test_syn_and_match_conceal()
call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16))
+ " 123456789012345678
+ let expect = '#*ThisXis*a*Test**'
+ call matchadd('Conceal', '\%2l\%7c ', 10, -1, {'conceal': 'X'})
+ redraw!
+
+ call assert_equal(expect, Screenline(lnum))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
+ call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16))
+
+ " 123456789012345678
+ let expect = '#*ThisXis*a*Test**'
+ call matchadd('ErrorMsg', '\%2l Test', 20, -1)
+ redraw!
+
+ call assert_equal(expect, Screenline(lnum))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
+ call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13))
+ call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18))
+ call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19))
+
+ " 123456789012345678
+ let expect = '# ThisXis a Test'
+ syntax clear MyConceal
+ syntax match MyConceal /\%2l / conceal containedin=ALL
+ redraw!
+
+ call assert_equal(expect, Screenline(lnum))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12))
+ call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 12))
+ call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13))
+ call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17))
+ call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18))
+ call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19))
+
syntax off
quit!
endfunc
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 30239a90c2..3ebd048f46 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -31,6 +31,8 @@ func Test_messages()
finally
let &more = oldmore
endtry
+
+ call assert_fails('message 1', 'E474:')
endfunc
" Patch 7.4.1696 defined the "clearmode()" command for clearing the mode
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index f71da92bf8..7c7804212b 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -291,6 +291,46 @@ endfunc
endif
+func Test_mkview_open_folds()
+ enew!
+
+ call append(0, ['a', 'b', 'c'])
+ 1,3fold
+ " zR affects 'foldlevel', make sure the option is applied after the folds
+ " have been recreated.
+ normal zR
+ write! Xtestfile
+
+ call assert_equal(-1, foldclosed(1))
+ call assert_equal(-1, foldclosed(2))
+ call assert_equal(-1, foldclosed(3))
+
+ mkview! Xtestview
+ source Xtestview
+
+ call assert_equal(-1, foldclosed(1))
+ call assert_equal(-1, foldclosed(2))
+ call assert_equal(-1, foldclosed(3))
+
+ call delete('Xtestview')
+ call delete('Xtestfile')
+ %bwipe
+endfunc
+
+func Test_mkview_no_balt()
+ edit Xtestfile1
+ edit Xtestfile2
+
+ mkview! Xtestview
+ bdelete Xtestfile1
+
+ source Xtestview
+ call assert_equal(0, buflisted('Xtestfile1'))
+
+ call delete('Xtestview')
+ %bwipe
+endfunc
+
" Test :mkview with a file argument.
func Test_mkview_file()
" Create a view with line number and a fold.
@@ -373,6 +413,58 @@ func Test_mkview_no_file_name()
%bwipe
endfunc
+func Test_mkview_loadview_jumplist()
+ set viewdir=Xviewdir
+ au BufWinLeave * silent mkview
+ " au BufWinEnter * silent loadview
+
+ edit Xfile1
+ call setline(1, ['a', 'bbbbbbb', 'c'])
+ normal j3l
+ call assert_equal([2, 4], getcurpos()[1:2])
+ write
+
+ edit Xfile2
+ call setline(1, ['d', 'eeeeeee', 'f'])
+ normal j5l
+ call assert_equal([2, 6], getcurpos()[1:2])
+ write
+
+ edit Xfile3
+ call setline(1, ['g', 'h', 'iiiii'])
+ normal jj3l
+ call assert_equal([3, 4], getcurpos()[1:2])
+ write
+
+ " The commented :au above was moved here so that :mkview (on BufWinLeave) can
+ " run before :loadview. This is needed because Nvim's :loadview raises E484 if
+ " the view can't be opened, while Vim's silently fails instead.
+ au BufWinEnter * silent loadview
+
+ edit Xfile1
+ call assert_equal([2, 4], getcurpos()[1:2])
+ edit Xfile2
+ call assert_equal([2, 6], getcurpos()[1:2])
+ edit Xfile3
+ call assert_equal([3, 4], getcurpos()[1:2])
+
+ exe "normal \<C-O>"
+ call assert_equal('Xfile2', expand('%'))
+ call assert_equal([2, 6], getcurpos()[1:2])
+ exe "normal \<C-O>"
+ call assert_equal('Xfile1', expand('%'))
+ call assert_equal([2, 4], getcurpos()[1:2])
+
+ au! BufWinLeave
+ au! BufWinEnter
+ bwipe!
+ call delete('Xviewdir', 'rf')
+ call delete('Xfile1')
+ call delete('Xfile2')
+ call delete('Xfile3')
+ set viewdir&
+endfunc
+
" A clean session (one empty buffer, one window, and one tab) should not
" set any error messages when sourced because no commands should fail.
func Test_mksession_no_errmsg()
@@ -663,4 +755,27 @@ func Test_scrolloff()
set sessionoptions&
endfunc
+func Test_altfile()
+ edit Xone
+ split Xtwo
+ edit Xtwoalt
+ edit #
+ wincmd w
+ edit Xonealt
+ edit #
+ mksession! Xtest_altfile
+ only
+ bwipe Xonealt
+ bwipe Xtwoalt
+ bwipe!
+ source Xtest_altfile
+ call assert_equal('Xone', bufname())
+ call assert_equal('Xonealt', bufname('#'))
+ wincmd w
+ call assert_equal('Xtwo', bufname())
+ call assert_equal('Xtwoalt', bufname('#'))
+ only
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 84d99ebb74..1202b842fd 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -1,5 +1,7 @@
" Test for options
+source check.vim
+
func Test_whichwrap()
set whichwrap=b,s
call assert_equal('b,s', &whichwrap)
@@ -330,12 +332,10 @@ func Test_set_ttytype()
set ttytype=xterm
call assert_equal('xterm', &ttytype)
call assert_equal(&ttytype, &term)
- " "set ttytype=" gives E522 instead of E529
- " in travis on some builds. Why? Catch both for now
try
set ttytype=
call assert_report('set ttytype= did not fail')
- catch /E529\|E522/
+ catch /E529/
endtry
" Some systems accept any terminal name and return dumb settings,
@@ -606,6 +606,27 @@ func Test_opt_boolean()
set number&
endfunc
+func Test_opt_winminheight_term()
+ " See test/functional/legacy/options_spec.lua
+ CheckRunVimInTerminal
+
+ " The tabline should be taken into account.
+ let lines =<< trim END
+ set wmh=0 stal=2
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp | wincmd _
+ below sp
+ END
+ call writefile(lines, 'Xwinminheight')
+ let buf = RunVimInTerminal('-S Xwinminheight', #{rows: 11})
+ call term_sendkeys(buf, ":set wmh=1\n")
+ call WaitForAssert({-> assert_match('E36: Not enough room', term_getline(buf, 11))})
+
+ call StopVimInTerminal(buf)
+ call delete('Xwinminheight')
+endfunc
+
" Test for setting option value containing spaces with isfname+=32
func Test_isfname_with_options()
set isfname+=32
@@ -615,4 +636,23 @@ func Test_isfname_with_options()
setlocal keywordprg&
endfunc
+" Test that resetting laststatus does change scroll option
+func Test_opt_reset_scroll()
+ " See test/functional/legacy/options_spec.lua
+ CheckRunVimInTerminal
+ let vimrc =<< trim [CODE]
+ set scroll=2
+ set laststatus=2
+ [CODE]
+ call writefile(vimrc, 'Xscroll')
+ let buf = RunVimInTerminal('-S Xscroll', {'rows': 16, 'cols': 45})
+ call term_sendkeys(buf, ":verbose set scroll?\n")
+ call WaitForAssert({-> assert_match('Last set.*window size', term_getline(buf, 15))})
+ call assert_match('^\s*scroll=7$', term_getline(buf, 14))
+ call StopVimInTerminal(buf)
+
+ " clean up
+ call delete('Xscroll')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 4e38f7ebd8..ce2ef4dcd8 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -1,12 +1,12 @@
" Test 'statusline'
"
" Not tested yet:
-" %a
" %N
" %T
" %X
source view_util.vim
+source check.vim
source term_util.vim
func s:get_statusline()
@@ -61,7 +61,19 @@ func Test_statusline_will_be_disabled_with_error()
endfunc
func Test_statusline()
- new Xstatusline
+ CheckFeature quickfix
+
+ " %a: Argument list ({current} of {max})
+ set statusline=%a
+ call assert_match('^\s*$', s:get_statusline())
+ arglocal a1 a2
+ rewind
+ call assert_match('^ (1 of 2)\s*$', s:get_statusline())
+ next
+ call assert_match('^ (2 of 2)\s*$', s:get_statusline())
+ e Xstatusline
+ call assert_match('^ ((2) of 2)\s*$', s:get_statusline())
+
only
set laststatus=2
set splitbelow
diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim
index 67701ee3ca..e9e181ce3d 100644
--- a/src/nvim/testdir/test_user_func.vim
+++ b/src/nvim/testdir/test_user_func.vim
@@ -95,6 +95,59 @@ func Test_user_func()
enew!
endfunc
+func Log(val, base = 10)
+ return log(a:val) / log(a:base)
+endfunc
+
+func Args(mandatory, optional = v:null, ...)
+ return deepcopy(a:)
+endfunc
+
+func Args2(a = 1, b = 2, c = 3)
+ return deepcopy(a:)
+endfunc
+
+func MakeBadFunc()
+ func s:fcn(a, b=1, c)
+ endfunc
+endfunc
+
+func Test_default_arg()
+ call assert_equal(1.0, Log(10))
+ call assert_equal(log(10), Log(10, exp(1)))
+ call assert_fails("call Log(1,2,3)", 'E118')
+
+ let res = Args(1)
+ call assert_equal(res.mandatory, 1)
+ call assert_equal(res.optional, v:null)
+ call assert_equal(res['0'], 0)
+
+ let res = Args(1,2)
+ call assert_equal(res.mandatory, 1)
+ call assert_equal(res.optional, 2)
+ call assert_equal(res['0'], 0)
+
+ let res = Args(1,2,3)
+ call assert_equal(res.mandatory, 1)
+ call assert_equal(res.optional, 2)
+ call assert_equal(res['0'], 1)
+
+ call assert_fails("call MakeBadFunc()", 'E989')
+ call assert_fails("fu F(a=1 ,) | endf", 'E475')
+
+ " Since neovim does not have v:none, the ability to use the default
+ " argument with the intermediate argument set to v:none has been omitted.
+ " Therefore, this test is not performed.
+ " let d = Args2(7, v:none, 9)
+ " call assert_equal([7, 2, 9], [d.a, d.b, d.c])
+
+ call assert_equal("\n"
+ \ .. " function Args2(a = 1, b = 2, c = 3)\n"
+ \ .. "1 return deepcopy(a:)\n"
+ \ .. " endfunction",
+ \ execute('func Args2'))
+endfunc
+
func Test_failed_call_in_try()
try | call UnknownFunc() | catch | endtry
endfunc
diff --git a/src/nvim/testdir/test_version.vim b/src/nvim/testdir/test_version.vim
index 46cf34979f..5fd38f7cdc 100644
--- a/src/nvim/testdir/test_version.vim
+++ b/src/nvim/testdir/test_version.vim
@@ -1,5 +1,8 @@
" Test :version Ex command
+so check.vim
+so shared.vim
+
func Test_version()
" version should always return the same string.
let v1 = execute('version')
@@ -9,4 +12,15 @@ func Test_version()
call assert_match("^\n\nNVIM v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+.*", v1)
endfunc
+func Test_version_redirect()
+ CheckNotGui
+ CheckCanRunGui
+ CheckUnix
+
+ call RunVim([], [], '--clean -g --version >Xversion 2>&1')
+ call assert_match('Features included', readfile('Xversion')->join())
+
+ call delete('Xversion')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 3e683a4926..6705ab98c2 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -557,8 +557,8 @@ HandleState ut_handle_background_color(TermInput *input)
static void handle_raw_buffer(TermInput *input, bool force)
{
- HandleState is_paste;
- HandleState is_bc;
+ HandleState is_paste = kNotApplicable;
+ HandleState is_bc = kNotApplicable;
do {
if (!force
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index da464c56dc..f52850f6f3 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -580,6 +580,10 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
uep->ue_array = NULL;
uep->ue_next = curbuf->b_u_newhead->uh_entry;
curbuf->b_u_newhead->uh_entry = uep;
+ if (reload) {
+ // buffer was reloaded, notify text change subscribers
+ curbuf->b_u_newhead->uh_flags |= UH_RELOAD;
+ }
curbuf->b_u_synced = false;
undo_undoes = false;
@@ -590,13 +594,20 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
}
-# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
+// magic at start of undofile
+# define UF_START_MAGIC "Vim\237UnDo\345"
# define UF_START_MAGIC_LEN 9
-# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
-# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
-# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
-# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
-# define UF_VERSION 2 /* 2-byte undofile version number */
+// magic at start of header
+# define UF_HEADER_MAGIC 0x5fd0
+// magic after last header
+# define UF_HEADER_END_MAGIC 0xe7aa
+// magic at start of entry
+# define UF_ENTRY_MAGIC 0xf518
+// magic after last entry
+# define UF_ENTRY_END_MAGIC 0x3581
+
+// 2-byte undofile version number
+# define UF_VERSION 3
/* extra fields for header */
# define UF_LAST_SAVE_NR 1
@@ -843,6 +854,15 @@ static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
}
}
undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2);
+
+ // Write all extmark undo objects
+ for (size_t i = 0; i < kv_size(uhp->uh_extmark); i++) {
+ if (!serialize_extmark(bi, kv_A(uhp->uh_extmark, i))) {
+ return false;
+ }
+ }
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2);
+
return true;
}
@@ -924,9 +944,95 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi,
return NULL;
}
+ // Unserialize all extmark undo information
+ ExtmarkUndoObject *extup;
+ kv_init(uhp->uh_extmark);
+
+ while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) {
+ bool error = false;
+ extup = unserialize_extmark(bi, &error, file_name);
+ if (error) {
+ kv_destroy(uhp->uh_extmark);
+ xfree(extup);
+ return NULL;
+ }
+ kv_push(uhp->uh_extmark, *extup);
+ xfree(extup);
+ }
+ if (c != UF_ENTRY_END_MAGIC) {
+ corruption_error("entry end", file_name);
+ u_free_uhp(uhp);
+ return NULL;
+ }
+
return uhp;
}
+static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup)
+{
+ if (extup.type == kExtmarkSplice) {
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2);
+ undo_write_bytes(bi, (uintmax_t)extup.type, 4);
+ if (!undo_write(bi, (uint8_t *)&(extup.data.splice),
+ sizeof(ExtmarkSplice))) {
+ return false;
+ }
+ } else if (extup.type == kExtmarkMove) {
+ undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2);
+ undo_write_bytes(bi, (uintmax_t)extup.type, 4);
+ if (!undo_write(bi, (uint8_t *)&(extup.data.move), sizeof(ExtmarkMove))) {
+ return false;
+ }
+ }
+ // Note: We do not serialize ExtmarkSavePos information, since
+ // buffer marktrees are not retained when closing/reopening a file
+ return true;
+}
+
+static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error,
+ const char *filename)
+{
+ UndoObjectType type;
+ uint8_t *buf = NULL;
+ size_t n_elems;
+
+ ExtmarkUndoObject *extup = xmalloc(sizeof(ExtmarkUndoObject));
+
+ type = (UndoObjectType)undo_read_4c(bi);
+ extup->type = type;
+ if (type == kExtmarkSplice) {
+ n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
+ buf = xcalloc(sizeof(uint8_t), n_elems);
+ if (!undo_read(bi, buf, n_elems)) {
+ goto error;
+ }
+ extup->data.splice = *(ExtmarkSplice *)buf;
+ } else if (type == kExtmarkMove) {
+ n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t);
+ buf = xcalloc(sizeof(uint8_t), n_elems);
+ if (!undo_read(bi, buf, n_elems)) {
+ goto error;
+ }
+ extup->data.move = *(ExtmarkMove *)buf;
+ } else {
+ goto error;
+ }
+
+ if (buf) {
+ xfree(buf);
+ }
+
+ return extup;
+
+error:
+ xfree(extup);
+ if (buf) {
+ xfree(buf);
+ }
+ *error = true;
+ return NULL;
+}
+
/// Serializes "uep".
///
/// @param bi The buffer information
@@ -2157,8 +2263,9 @@ static void u_undoredo(int undo, bool do_buf_event)
u_check(FALSE);
#endif
old_flags = curhead->uh_flags;
- new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
- ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
+ new_flags = (curbuf->b_changed ? UH_CHANGED : 0)
+ | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0)
+ | (old_flags & UH_RELOAD);
setpcmark();
/*
@@ -2299,6 +2406,11 @@ static void u_undoredo(int undo, bool do_buf_event)
extmark_apply_undo(undo_info, undo);
}
}
+ if (curhead->uh_flags & UH_RELOAD) {
+ // TODO(bfredl): this is a bit crude. When 'undoreload' is used we
+ // should have all info to send a buffer-reloaing on_lines/on_bytes event
+ buf_updates_unload(curbuf, true);
+ }
// finish Adjusting extmarks
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index cc2c39a711..b46295a15d 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -69,9 +69,10 @@ struct u_header {
#endif
};
-/* values for uh_flags */
-#define UH_CHANGED 0x01 /* b_changed flag before undo/after redo */
-#define UH_EMPTYBUF 0x02 /* buffer was empty */
+// values for uh_flags
+#define UH_CHANGED 0x01 // b_changed flag before undo/after redo
+#define UH_EMPTYBUF 0x02 // buffer was empty
+#define UH_RELOAD 0x04 // buffer was reloaded
/// Structure passed around between undofile functions.
typedef struct {
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index e70749795b..0245c472ef 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -158,6 +158,7 @@ enum {
EXPAND_MESSAGES,
EXPAND_MAPCLEAR,
EXPAND_ARGLIST,
+ EXPAND_DIFF_BUFFERS,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 00f49724b6..0f717a2f90 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5501,7 +5501,7 @@ void win_setminheight(void)
// loop until there is a 'winminheight' that is possible
while (p_wmh > 0) {
- const int room = Rows - p_ch;
+ const int room = Rows - p_ch - tabline_height();
const int needed = frame_minheight(topframe, NULL);
if (room >= needed) {
break;
@@ -5960,9 +5960,17 @@ void win_new_width(win_T *wp, int width)
void win_comp_scroll(win_T *wp)
{
+ const long old_w_p_scr = wp->w_p_scr;
+
wp->w_p_scr = wp->w_height / 2;
- if (wp->w_p_scr == 0)
+ if (wp->w_p_scr == 0) {
wp->w_p_scr = 1;
+ }
+ if (wp->w_p_scr != old_w_p_scr) {
+ // Used by "verbose set scroll".
+ wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_sid = SID_WINLAYOUT;
+ wp->w_p_script_ctx[WV_SCROLL].script_ctx.sc_lnum = 0;
+ }
}
/*