aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c49
-rw-r--r--src/nvim/api/private/helpers.c38
-rw-r--r--src/nvim/api/private/helpers.h3
-rw-r--r--src/nvim/api/vim.c114
-rw-r--r--src/nvim/api/window.c4
-rw-r--r--src/nvim/auevents.lua3
-rw-r--r--src/nvim/buffer.c235
-rw-r--r--src/nvim/buffer_defs.h32
-rw-r--r--src/nvim/change.c9
-rw-r--r--src/nvim/cursor.c2
-rw-r--r--src/nvim/diff.c4
-rw-r--r--src/nvim/edit.c112
-rw-r--r--src/nvim/eval.c11
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c47
-rw-r--r--src/nvim/eval/userfunc.c8
-rw-r--r--src/nvim/ex_cmds.c6
-rw-r--r--src/nvim/ex_cmds2.c3
-rw-r--r--src/nvim/ex_docmd.c69
-rw-r--r--src/nvim/ex_getln.c21
-rw-r--r--src/nvim/ex_session.c6
-rw-r--r--src/nvim/extmark_defs.h5
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/generators/gen_options.lua9
-rw-r--r--src/nvim/globals.h10
-rw-r--r--src/nvim/hashtab.h1
-rw-r--r--src/nvim/highlight.c213
-rw-r--r--src/nvim/highlight_defs.h15
-rw-r--r--src/nvim/indent.c17
-rw-r--r--src/nvim/indent_c.c16
-rw-r--r--src/nvim/lua/executor.c27
-rw-r--r--src/nvim/lua/treesitter.c32
-rw-r--r--src/nvim/lua/vim.lua93
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/map.c18
-rw-r--r--src/nvim/map.h2
-rw-r--r--src/nvim/mbyte.c140
-rw-r--r--src/nvim/menu.c2
-rw-r--r--src/nvim/message.c4
-rw-r--r--src/nvim/mouse.c6
-rw-r--r--src/nvim/move.c41
-rw-r--r--src/nvim/msgpack_rpc/channel.c6
-rw-r--r--src/nvim/normal.c22
-rw-r--r--src/nvim/ops.c115
-rw-r--r--src/nvim/option.c42
-rw-r--r--src/nvim/option_defs.h10
-rw-r--r--src/nvim/options.lua9
-rw-r--r--src/nvim/os/os_defs.h5
-rw-r--r--src/nvim/popupmnu.c86
-rw-r--r--src/nvim/quickfix.c3
-rw-r--r--src/nvim/screen.c67
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/spell.c4
-rw-r--r--src/nvim/spell_defs.h1
-rw-r--r--src/nvim/spellfile.c35
-rw-r--r--src/nvim/syntax.c94
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c4
-rw-r--r--src/nvim/testdir/test_autocmd.vim13
-rw-r--r--src/nvim/testdir/test_cindent.vim36
-rw-r--r--src/nvim/testdir/test_cjk_linebreak.vim97
-rw-r--r--src/nvim/testdir/test_cmdline.vim21
-rw-r--r--src/nvim/testdir/test_edit.vim18
-rw-r--r--src/nvim/testdir/test_filetype.vim2
-rw-r--r--src/nvim/testdir/test_ins_complete.vim86
-rw-r--r--src/nvim/testdir/test_listdict.vim4
-rw-r--r--src/nvim/testdir/test_messages.vim24
-rw-r--r--src/nvim/testdir/test_mksession.vim13
-rw-r--r--src/nvim/testdir/test_options.vim1
-rw-r--r--src/nvim/testdir/test_quickfix.vim4
-rw-r--r--src/nvim/testdir/test_registers.vim69
-rw-r--r--src/nvim/testdir/test_ruby.vim197
-rw-r--r--src/nvim/testdir/test_statusline.vim15
-rw-r--r--src/nvim/tui/tui.c17
-rw-r--r--src/nvim/types.h2
-rw-r--r--src/nvim/ui_compositor.c1
-rw-r--r--src/nvim/undo.c2
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/window.c68
79 files changed, 1882 insertions, 748 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index c8dd85b39d..4569ebc713 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -953,6 +953,53 @@ Boolean nvim_buf_is_loaded(Buffer buffer)
return buf && buf->b_ml.ml_mfp != NULL;
}
+/// Deletes the buffer. See |:bwipeout|
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param opts Optional parameters. Keys:
+/// - force: Force deletion and ignore unsaved changes.
+/// - unload: Unloaded only, do not delete. See |:bunload|
+void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+
+ if (ERROR_SET(err)) {
+ return;
+ }
+
+ bool force = false;
+ bool unload = false;
+ for (size_t i = 0; i < opts.size; i++) {
+ String k = opts.items[i].key;
+ Object v = opts.items[i].value;
+ if (strequal("force", k.data)) {
+ force = api_object_to_bool(v, "force", false, err);
+ } else if (strequal("unload", k.data)) {
+ unload = api_object_to_bool(v, "unload", false, err);
+ } else {
+ api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
+ return;
+ }
+ }
+
+ if (ERROR_SET(err)) {
+ return;
+ }
+
+ int result = do_buffer(
+ unload ? DOBUF_UNLOAD : DOBUF_WIPE,
+ DOBUF_FIRST,
+ FORWARD,
+ buf->handle,
+ force);
+
+ if (result == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to unload buffer.");
+ return;
+ }
+}
+
/// Checks if a buffer is valid.
///
/// @note Even if a buffer is valid it may have been unloaded. See |api-buffer|
@@ -1394,7 +1441,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
goto error;
}
} else if (strequal("ephemeral", k.data)) {
- ephemeral = api_is_truthy(*v, "ephemeral", false, err);
+ ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
if (ERROR_SET(err)) {
goto error;
}
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 84517c99fc..a9b1676879 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1619,14 +1619,21 @@ free_exit:
return virt_text;
}
-bool api_is_truthy(Object obj, const char *what, bool nil_truthy, Error *err)
+/// Force obj to bool.
+/// If it fails, returns false and sets err
+/// @param obj The object to coerce to a boolean
+/// @param what The name of the object, used for error message
+/// @param nil_value What to return if the type is nil.
+/// @param err Set if there was an error in converting to a bool
+bool api_object_to_bool(Object obj, const char *what,
+ bool nil_value, Error *err)
{
if (obj.type == kObjectTypeBoolean) {
return obj.data.boolean;
} else if (obj.type == kObjectTypeInteger) {
return obj.data.integer; // C semantics: non-zero int is true
} else if (obj.type == kObjectTypeNil) {
- return nil_truthy; // caller decides what NIL (missing retval in lua) means
+ return nil_value; // caller decides what NIL (missing retval in lua) means
} else {
api_set_error(err, kErrorTypeValidation, "%s is not an boolean", what);
return false;
@@ -1644,3 +1651,30 @@ const char *describe_ns(NS ns_id)
})
return "(UNKNOWN PLUGIN)";
}
+
+DecorationProvider *get_provider(NS ns_id, bool force)
+{
+ ssize_t i;
+ for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) {
+ DecorationProvider *item = &kv_A(decoration_providers, i);
+ if (item->ns_id == ns_id) {
+ return item;
+ } else if (item->ns_id > ns_id) {
+ break;
+ }
+ }
+
+ if (!force) {
+ return NULL;
+ }
+
+ for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) {
+ // allocates if needed:
+ (void)kv_a(decoration_providers, (size_t)j+1);
+ kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j);
+ }
+ DecorationProvider *item = &kv_a(decoration_providers, (size_t)i);
+ *item = DECORATION_PROVIDER_INIT(ns_id);
+
+ return item;
+}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index df3a263dcf..7c6f07402b 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -52,7 +52,8 @@
.type = kObjectTypeLuaRef, \
.data.luaref = r })
-#define NIL ((Object) {.type = kObjectTypeNil})
+#define NIL ((Object)OBJECT_INIT)
+#define NULL_STRING ((String)STRING_INIT)
#define PUT(dict, k, v) \
kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 876b052a8e..725847886a 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -199,6 +199,69 @@ Integer nvim_get_hl_id_by_name(String name)
return syn_check_group((const char_u *)name.data, (int)name.size);
}
+Dictionary nvim__get_hl_defs(Integer ns_id, Error *err)
+{
+ if (ns_id == 0) {
+ return get_global_hl_defs();
+ }
+ abort();
+}
+
+/// Set a highlight group.
+///
+/// @param ns_id number of namespace for this highlight
+/// @param name highlight group name, like ErrorMsg
+/// @param val highlight definiton map, like |nvim_get_hl_by_name|.
+/// @param[out] err Error details, if any
+///
+/// TODO: ns_id = 0, should modify :highlight namespace
+/// TODO val should take update vs reset flag
+void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
+ FUNC_API_SINCE(7)
+{
+ int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size);
+ int link_id = -1;
+
+ HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
+ if (!ERROR_SET(err)) {
+ ns_hl_def((NS)ns_id, hl_id, attrs, link_id);
+ }
+}
+
+/// Set active namespace for highlights.
+///
+/// NB: this function can be called from async contexts, but the
+/// semantics are not yet well-defined. To start with
+/// |nvim_set_decoration_provider| on_win and on_line callbacks
+/// are explicitly allowed to change the namespace during a redraw cycle.
+///
+/// @param ns_id the namespace to activate
+/// @param[out] err Error details, if any
+void nvim_set_hl_ns(Integer ns_id, Error *err)
+ FUNC_API_SINCE(7)
+ FUNC_API_FAST
+{
+ if (ns_id >= 0) {
+ ns_hl_active = (NS)ns_id;
+ }
+
+ // TODO(bfredl): this is a little bit hackish. Eventually we want a standard
+ // event path for redraws caused by "fast" events. This could tie in with
+ // better throttling of async events causing redraws, such as non-batched
+ // nvim_buf_set_extmark calls from async contexts.
+ if (!updating_screen && !ns_hl_changed) {
+ multiqueue_put(main_loop.events, on_redraw_event, 0);
+ }
+ ns_hl_changed = true;
+}
+
+static void on_redraw_event(void **argv)
+ FUNC_API_NOEXPORT
+{
+ redraw_all_later(NOT_VALID);
+}
+
+
/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
/// flags. This is a blocking call, unlike |nvim_input()|.
///
@@ -678,7 +741,11 @@ Integer nvim_strwidth(String text, Error *err)
ArrayOf(String) nvim_list_runtime_paths(void)
FUNC_API_SINCE(1)
{
+ // TODO(bfredl): this should just work:
+ // return nvim_get_runtime_file(NULL_STRING, true);
+
Array rv = ARRAY_DICT_INIT;
+
char_u *rtp = p_rtp;
if (*rtp == NUL) {
@@ -725,22 +792,29 @@ ArrayOf(String) nvim_list_runtime_paths(void)
/// @param name pattern of files to search for
/// @param all whether to return all matches or only the first
/// @return list of absolute paths to the found files
-ArrayOf(String) nvim_get_runtime_file(String name, Boolean all)
+ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
- if (!name.data) {
+
+ // TODO(bfredl):
+ if (name.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "not yet implemented");
return rv;
}
+
int flags = DIP_START | (all ? DIP_ALL : 0);
- do_in_runtimepath((char_u *)name.data, flags, find_runtime_cb, &rv);
+ do_in_runtimepath(name.size ? (char_u *)name.data : NULL,
+ flags, find_runtime_cb, &rv);
return rv;
}
static void find_runtime_cb(char_u *fname, void *cookie)
{
Array *rv = (Array *)cookie;
- ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname)));
+ if (fname != NULL) {
+ ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname)));
+ }
}
String nvim__get_lib_dir(void)
@@ -1477,7 +1551,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1)
{
- return name_to_color((char_u *)name.data);
+ return name_to_color(name.data);
}
/// Returns a map of color names and RGB values.
@@ -2610,33 +2684,6 @@ void nvim__screenshot(String path)
ui_call_screenshot(path);
}
-static DecorationProvider *get_provider(NS ns_id, bool force)
-{
- ssize_t i;
- for (i = 0; i < (ssize_t)kv_size(decoration_providers); i++) {
- DecorationProvider *item = &kv_A(decoration_providers, i);
- if (item->ns_id == ns_id) {
- return item;
- } else if (item->ns_id > ns_id) {
- break;
- }
- }
-
- if (!force) {
- return NULL;
- }
-
- for (ssize_t j = (ssize_t)kv_size(decoration_providers)-1; j >= i; j++) {
- // allocates if needed:
- (void)kv_a(decoration_providers, (size_t)j+1);
- kv_A(decoration_providers, (size_t)j+1) = kv_A(decoration_providers, j);
- }
- DecorationProvider *item = &kv_a(decoration_providers, (size_t)i);
- *item = DECORATION_PROVIDER_INIT(ns_id);
-
- return item;
-}
-
static void clear_provider(DecorationProvider *p)
{
NLUA_CLEAR_REF(p->redraw_start);
@@ -2695,7 +2742,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts,
clear_provider(p);
// regardless of what happens, it seems good idea to redraw
- redraw_later(NOT_VALID); // TODO(bfredl): too soon?
+ redraw_all_later(NOT_VALID); // TODO(bfredl): too soon?
struct {
const char *name;
@@ -2706,6 +2753,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts,
{ "on_win", &p->redraw_win },
{ "on_line", &p->redraw_line },
{ "on_end", &p->redraw_end },
+ { "_on_hl_def", &p->hl_def },
{ NULL, NULL },
};
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index ba43bc6cb2..f09a03f592 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -142,7 +142,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
// make sure cursor is in visible range even if win != curwin
update_topline_win(win);
- redraw_win_later(win, VALID);
+ redraw_later(win, VALID);
}
/// Gets the window height
@@ -471,7 +471,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err)
if (!win_new_float(win, fconfig, err)) {
return;
}
- redraw_later(NOT_VALID);
+ redraw_later(win, NOT_VALID);
} else {
win_config_float(win, fconfig);
win->w_pos_changed = true;
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index ea2db41668..9c28398f5b 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -65,7 +65,8 @@ return {
'InsertChange', -- when changing Insert/Replace mode
'InsertCharPre', -- before inserting a char
'InsertEnter', -- when entering Insert mode
- 'InsertLeave', -- when leaving Insert mode
+ 'InsertLeave', -- just after leaving Insert mode
+ 'InsertLeavePre', -- just before leaving Insert mode
'MenuPopup', -- just before popup menu is displayed
'OptionSet', -- after setting any option
'QuickFixCmdPost', -- after :make, :grep etc.
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8f631ae13b..469542ec9f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1623,7 +1623,7 @@ void enter_buffer(buf_T *buf)
}
curbuf->b_last_used = time(NULL);
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
// Change to the directory of the current buffer.
@@ -3440,31 +3440,17 @@ int build_stl_str_hl(
int use_sandbox,
char_u fillchar,
int maxwidth,
- struct stl_hlrec *hltab,
- StlClickRecord *tabtab
+ stl_hlrec_t **hltab,
+ StlClickRecord **tabtab
)
{
- int groupitems[STL_MAX_ITEM];
- struct stl_item {
- // Where the item starts in the status line output buffer
- char_u *start;
- // Function to run for ClickFunc items.
- char *cmd;
- // The minimum width of the item
- int minwid;
- // The maximum width of the item
- int maxwid;
- enum {
- Normal,
- Empty,
- Group,
- Separate,
- Highlight,
- TabPage,
- ClickFunc,
- Trunc
- } type;
- } items[STL_MAX_ITEM];
+ static size_t stl_items_len = 20; // Initial value, grows as needed.
+ static stl_item_t *stl_items = NULL;
+ static int *stl_groupitems = NULL;
+ static stl_hlrec_t *stl_hltab = NULL;
+ static StlClickRecord *stl_tabtab = NULL;
+ static int *stl_separator_locations = NULL;
+
#define TMPLEN 70
char_u buf_tmp[TMPLEN];
char_u win_tmp[TMPLEN];
@@ -3472,6 +3458,14 @@ int build_stl_str_hl(
const int save_must_redraw = must_redraw;
const int save_redr_type = curwin->w_redr_type;
+ if (stl_items == NULL) {
+ 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_separator_locations = xmalloc(sizeof(int) * stl_items_len);
+ }
+
// When the format starts with "%!" then evaluate it as an expression and
// use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') {
@@ -3540,14 +3534,17 @@ int build_stl_str_hl(
// Proceed character by character through the statusline format string
// fmt_p is the current positon in the input buffer
for (char_u *fmt_p = usefmt; *fmt_p; ) {
- if (curitem == STL_MAX_ITEM) {
- // There are too many items. Add the error code to the statusline
- // to give the user a hint about what went wrong.
- if (out_p + 5 < out_end_p) {
- memmove(out_p, " E541", (size_t)5);
- out_p += 5;
- }
- break;
+ if (curitem == (int)stl_items_len) {
+ size_t new_len = stl_items_len * 3 / 2;
+
+ stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
+ stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
+ stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * new_len);
+ stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * new_len);
+ stl_separator_locations =
+ xrealloc(stl_separator_locations, sizeof(int) * new_len);
+
+ stl_items_len = new_len;
}
if (*fmt_p != NUL && *fmt_p != '%') {
@@ -3591,16 +3588,16 @@ int build_stl_str_hl(
if (groupdepth > 0) {
continue;
}
- items[curitem].type = Separate;
- items[curitem++].start = out_p;
+ stl_items[curitem].type = Separate;
+ stl_items[curitem++].start = out_p;
continue;
}
// STL_TRUNCMARK: Where to begin truncating if the statusline is too long.
if (*fmt_p == STL_TRUNCMARK) {
fmt_p++;
- items[curitem].type = Trunc;
- items[curitem++].start = out_p;
+ stl_items[curitem].type = Trunc;
+ stl_items[curitem++].start = out_p;
continue;
}
@@ -3616,7 +3613,7 @@ int build_stl_str_hl(
// Determine how long the group is.
// Note: We set the current output position to null
// so `vim_strsize` will work.
- char_u *t = items[groupitems[groupdepth]].start;
+ char_u *t = stl_items[stl_groupitems[groupdepth]].start;
*out_p = NUL;
long group_len = vim_strsize(t);
@@ -3626,40 +3623,40 @@ int build_stl_str_hl(
// move the output pointer back to where the group started.
// Note: This erases any non-item characters that were in the group.
// Otherwise there would be no reason to do this step.
- if (curitem > groupitems[groupdepth] + 1
- && items[groupitems[groupdepth]].minwid == 0) {
+ if (curitem > stl_groupitems[groupdepth] + 1
+ && stl_items[stl_groupitems[groupdepth]].minwid == 0) {
// remove group if all items are empty and highlight group
// doesn't change
int group_start_userhl = 0;
int group_end_userhl = 0;
int n;
- for (n = groupitems[groupdepth] - 1; n >= 0; n--) {
- if (items[n].type == Highlight) {
- group_start_userhl = group_end_userhl = items[n].minwid;
+ for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
+ if (stl_items[n].type == Highlight) {
+ group_start_userhl = group_end_userhl = stl_items[n].minwid;
break;
}
}
- for (n = groupitems[groupdepth] + 1; n < curitem; n++) {
- if (items[n].type == Normal) {
+ for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
+ if (stl_items[n].type == Normal) {
break;
}
- if (items[n].type == Highlight) {
- group_end_userhl = items[n].minwid;
+ if (stl_items[n].type == Highlight) {
+ group_end_userhl = stl_items[n].minwid;
}
}
if (n == curitem && group_start_userhl == group_end_userhl) {
// empty group
out_p = t;
group_len = 0;
- for (n = groupitems[groupdepth] + 1; n < curitem; n++) {
+ for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
// do not use the highlighting from the removed group
- if (items[n].type == Highlight) {
- items[n].type = Empty;
+ if (stl_items[n].type == Highlight) {
+ stl_items[n].type = Empty;
}
// adjust the start position of TabPage to the next
// item position
- if (items[n].type == TabPage) {
- items[n].start = out_p;
+ if (stl_items[n].type == TabPage) {
+ stl_items[n].start = out_p;
}
}
}
@@ -3667,18 +3664,19 @@ int build_stl_str_hl(
// If the group is longer than it is allowed to be
// truncate by removing bytes from the start of the group text.
- if (group_len > items[groupitems[groupdepth]].maxwid) {
+ if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) {
// { Determine the number of bytes to remove
long n;
if (has_mbyte) {
// Find the first character that should be included.
n = 0;
- while (group_len >= items[groupitems[groupdepth]].maxwid) {
+ while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
group_len -= ptr2cells(t + n);
n += (*mb_ptr2len)(t + n);
}
} else {
- n = (long)(out_p - t) - items[groupitems[groupdepth]].maxwid + 1;
+ n = (long)(out_p - t)
+ - stl_items[stl_groupitems[groupdepth]].maxwid + 1;
}
// }
@@ -3689,25 +3687,26 @@ int build_stl_str_hl(
memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
out_p = out_p - n + 1;
// Fill up space left over by half a double-wide char.
- while (++group_len < items[groupitems[groupdepth]].minwid) {
+ while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
*out_p++ = fillchar;
}
// }
// correct the start of the items for the truncation
- for (int idx = groupitems[groupdepth] + 1; idx < curitem; idx++) {
+ for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
// Shift everything back by the number of removed bytes
- items[idx].start -= n;
+ stl_items[idx].start -= n;
// If the item was partially or completely truncated, set its
// start to the start of the group
- if (items[idx].start < t) {
- items[idx].start = t;
+ if (stl_items[idx].start < t) {
+ stl_items[idx].start = t;
}
}
// If the group is shorter than the minimum width, add padding characters.
- } else if (abs(items[groupitems[groupdepth]].minwid) > group_len) {
- long min_group_width = items[groupitems[groupdepth]].minwid;
+ } else if (
+ abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
+ long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
// If the group is left-aligned, add characters to the right.
if (min_group_width < 0) {
min_group_width = 0 - min_group_width;
@@ -3726,8 +3725,8 @@ int build_stl_str_hl(
// }
// Adjust item start positions
- for (int n = groupitems[groupdepth] + 1; n < curitem; n++) {
- items[n].start += group_len;
+ for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
+ stl_items[n].start += group_len;
}
// Prepend the fill characters
@@ -3763,9 +3762,9 @@ int build_stl_str_hl(
// User highlight groups override the min width field
// to denote the styling to use.
if (*fmt_p == STL_USER_HL) {
- items[curitem].type = Highlight;
- items[curitem].start = out_p;
- items[curitem].minwid = minwid > 9 ? 1 : minwid;
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid > 9 ? 1 : minwid;
fmt_p++;
curitem++;
continue;
@@ -3799,8 +3798,8 @@ int build_stl_str_hl(
if (minwid == 0) {
// %X ends the close label, go back to the previous tab label nr.
for (long n = curitem - 1; n >= 0; n--) {
- if (items[n].type == TabPage && items[n].minwid >= 0) {
- minwid = items[n].minwid;
+ if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) {
+ minwid = stl_items[n].minwid;
break;
}
}
@@ -3809,9 +3808,9 @@ int build_stl_str_hl(
minwid = -minwid;
}
}
- items[curitem].type = TabPage;
- items[curitem].start = out_p;
- items[curitem].minwid = minwid;
+ stl_items[curitem].type = TabPage;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid;
fmt_p++;
curitem++;
continue;
@@ -3826,10 +3825,10 @@ int build_stl_str_hl(
if (*fmt_p != STL_CLICK_FUNC) {
break;
}
- items[curitem].type = ClickFunc;
- items[curitem].start = out_p;
- items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t)));
- items[curitem].minwid = minwid;
+ stl_items[curitem].type = ClickFunc;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t)));
+ stl_items[curitem].minwid = minwid;
fmt_p++;
curitem++;
continue;
@@ -3850,11 +3849,11 @@ int build_stl_str_hl(
// Denotes the start of a new group
if (*fmt_p == '(') {
- groupitems[groupdepth++] = curitem;
- items[curitem].type = Group;
- items[curitem].start = out_p;
- items[curitem].minwid = minwid;
- items[curitem].maxwid = maxwid;
+ stl_groupitems[groupdepth++] = curitem;
+ stl_items[curitem].type = Group;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid;
+ stl_items[curitem].maxwid = maxwid;
fmt_p++;
curitem++;
continue;
@@ -4149,9 +4148,9 @@ int build_stl_str_hl(
// Create a highlight item based on the name
if (*fmt_p == '#') {
- items[curitem].type = Highlight;
- items[curitem].start = out_p;
- items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t));
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t));
curitem++;
fmt_p++;
}
@@ -4162,8 +4161,8 @@ int build_stl_str_hl(
// If we made it this far, the item is normal and starts at
// our current position in the output buffer.
// Non-normal items would have `continued`.
- items[curitem].start = out_p;
- items[curitem].type = Normal;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].type = Normal;
// Copy the item string into the output buffer
if (str != NULL && *str) {
@@ -4321,7 +4320,7 @@ int build_stl_str_hl(
// Otherwise, there was nothing to print so mark the item as empty
} else {
- items[curitem].type = Empty;
+ stl_items[curitem].type = Empty;
}
// Only free the string buffer if we allocated it.
@@ -4362,13 +4361,13 @@ int build_stl_str_hl(
// Otherwise, look for the truncation item
} else {
// Default to truncating at the first item
- trunc_p = items[0].start;
+ trunc_p = stl_items[0].start;
item_idx = 0;
for (int i = 0; i < itemcnt; i++) {
- if (items[i].type == Trunc) {
- // Truncate at %< items.
- trunc_p = items[i].start;
+ if (stl_items[i].type == Trunc) {
+ // Truncate at %< stl_items.
+ trunc_p = stl_items[i].start;
item_idx = i;
break;
}
@@ -4403,7 +4402,7 @@ int build_stl_str_hl(
// Ignore any items in the statusline that occur after
// the truncation point
for (int i = 0; i < itemcnt; i++) {
- if (items[i].start > trunc_p) {
+ if (stl_items[i].start > trunc_p) {
itemcnt = i;
break;
}
@@ -4458,12 +4457,12 @@ int build_stl_str_hl(
for (int i = item_idx; i < itemcnt; i++) {
// Items starting at or after the end of the truncated section need
// to be moved backwards.
- if (items[i].start >= trunc_end_p) {
- items[i].start -= item_offset;
+ if (stl_items[i].start >= trunc_end_p) {
+ stl_items[i].start -= item_offset;
// Anything inside the truncated area is set to start
// at the `<` truncation character.
} else {
- items[i].start = trunc_p;
+ stl_items[i].start = trunc_p;
}
}
// }
@@ -4479,7 +4478,7 @@ int build_stl_str_hl(
// figuring out how many groups there are.
int num_separators = 0;
for (int i = 0; i < itemcnt; i++) {
- if (items[i].type == Separate) {
+ if (stl_items[i].type == Separate) {
num_separators++;
}
}
@@ -4488,11 +4487,10 @@ int build_stl_str_hl(
if (num_separators) {
// Create an array of the start location for each
// separator mark.
- int separator_locations[STL_MAX_ITEM];
int index = 0;
for (int i = 0; i < itemcnt; i++) {
- if (items[i].type == Separate) {
- separator_locations[index] = i;
+ if (stl_items[i].type == Separate) {
+ stl_separator_locations[index] = i;
index++;
}
}
@@ -4504,16 +4502,17 @@ int build_stl_str_hl(
for (int i = 0; i < num_separators; i++) {
int dislocation = (i == (num_separators - 1))
? final_spaces : standard_spaces;
- char_u *seploc = items[separator_locations[i]].start + dislocation;
- STRMOVE(seploc, items[separator_locations[i]].start);
- for (char_u *s = items[separator_locations[i]].start; s < seploc; s++) {
+ char_u *start = stl_items[stl_separator_locations[i]].start;
+ char_u *seploc = start + dislocation;
+ STRMOVE(seploc, start);
+ for (char_u *s = start; s < seploc; s++) {
*s = fillchar;
}
- for (int item_idx = separator_locations[i] + 1;
+ for (int item_idx = stl_separator_locations[i] + 1;
item_idx < itemcnt;
item_idx++) {
- items[item_idx].start += dislocation;
+ stl_items[item_idx].start += dislocation;
}
}
@@ -4523,11 +4522,12 @@ int build_stl_str_hl(
// Store the info about highlighting.
if (hltab != NULL) {
- struct stl_hlrec *sp = hltab;
+ *hltab = stl_hltab;
+ stl_hlrec_t *sp = stl_hltab;
for (long l = 0; l < itemcnt; l++) {
- if (items[l].type == Highlight) {
- sp->start = items[l].start;
- sp->userhl = items[l].minwid;
+ if (stl_items[l].type == Highlight) {
+ sp->start = stl_items[l].start;
+ sp->userhl = stl_items[l].minwid;
sp++;
}
}
@@ -4537,16 +4537,17 @@ int build_stl_str_hl(
// Store the info about tab pages labels.
if (tabtab != NULL) {
- StlClickRecord *cur_tab_rec = tabtab;
+ *tabtab = stl_tabtab;
+ StlClickRecord *cur_tab_rec = stl_tabtab;
for (long l = 0; l < itemcnt; l++) {
- if (items[l].type == TabPage) {
- cur_tab_rec->start = (char *)items[l].start;
- if (items[l].minwid == 0) {
+ if (stl_items[l].type == TabPage) {
+ cur_tab_rec->start = (char *)stl_items[l].start;
+ if (stl_items[l].minwid == 0) {
cur_tab_rec->def.type = kStlClickDisabled;
cur_tab_rec->def.tabnr = 0;
} else {
- int tabnr = items[l].minwid;
- if (items[l].minwid > 0) {
+ int tabnr = stl_items[l].minwid;
+ if (stl_items[l].minwid > 0) {
cur_tab_rec->def.type = kStlClickTabSwitch;
} else {
cur_tab_rec->def.type = kStlClickTabClose;
@@ -4556,11 +4557,11 @@ int build_stl_str_hl(
}
cur_tab_rec->def.func = NULL;
cur_tab_rec++;
- } else if (items[l].type == ClickFunc) {
- cur_tab_rec->start = (char *)items[l].start;
+ } else if (stl_items[l].type == ClickFunc) {
+ cur_tab_rec->start = (char *)stl_items[l].start;
cur_tab_rec->def.type = kStlClickFuncRun;
- cur_tab_rec->def.tabnr = items[l].minwid;
- cur_tab_rec->def.func = items[l].cmd;
+ cur_tab_rec->def.tabnr = stl_items[l].minwid;
+ cur_tab_rec->def.func = stl_items[l].cmd;
cur_tab_rec++;
}
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index ba71ac6b2b..540542f409 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -361,14 +361,36 @@ struct mapblock {
sctx_T m_script_ctx; // SCTX where map was defined
};
-/*
- * Used for highlighting in the status line.
- */
+/// Used for highlighting in the status line.
+typedef struct stl_hlrec stl_hlrec_t;
struct stl_hlrec {
char_u *start;
int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID
};
+/// Used for building the status line.
+typedef struct stl_item stl_item_t;
+struct stl_item {
+ // Where the item starts in the status line output buffer
+ char_u *start;
+ // Function to run for ClickFunc items.
+ char *cmd;
+ // The minimum width of the item
+ int minwid;
+ // The maximum width of the item
+ int maxwid;
+ enum {
+ Normal,
+ Empty,
+ Group,
+ Separate,
+ Highlight,
+ TabPage,
+ ClickFunc,
+ Trunc
+ } type;
+};
+
// values for b_syn_spell: what to do with toplevel text
#define SYNSPL_DEFAULT 0 // spell check if @Spell not defined
#define SYNSPL_TOP 1 // spell check toplevel text
@@ -661,6 +683,9 @@ struct file_buffer {
char_u *b_p_com; ///< 'comments'
char_u *b_p_cms; ///< 'commentstring'
char_u *b_p_cpt; ///< 'complete'
+#ifdef BACKSLASH_IN_FILENAME
+ char_u *b_p_csl; ///< 'completeslash'
+#endif
char_u *b_p_cfu; ///< 'completefunc'
char_u *b_p_ofu; ///< 'omnifunc'
char_u *b_p_tfu; ///< 'tagfunc'
@@ -770,6 +795,7 @@ struct file_buffer {
int b_ind_cpp_namespace;
int b_ind_if_for_while;
int b_ind_cpp_extern_c;
+ int b_ind_pragma;
linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary
* write should not have an end-of-line */
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 71614363d2..9ee987b45d 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -142,7 +142,6 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume,
long xtra)
{
int i;
- int cols;
pos_T *p;
int add;
@@ -170,7 +169,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume,
if (p->lnum != lnum) {
add = true;
} else {
- cols = comp_textwidth(false);
+ int cols = comp_textwidth(false);
if (cols == 0) {
cols = 79;
}
@@ -295,7 +294,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume,
// change.
if (wp->w_p_rnu
|| (wp->w_p_cul && lnum <= wp->w_last_cursorline)) {
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
}
}
@@ -349,7 +348,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changedOneline(wp->w_buffer, wlnum);
@@ -476,7 +475,7 @@ changed_lines(
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changed_lines_buf(wp->w_buffer, wlnum,
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 036ae32589..d3ffab1759 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -482,7 +482,7 @@ bool leftcol_changed(void)
if (retval)
curwin->w_set_curswant = true;
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
return retval;
}
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 3de5fc49bd..b9c293f6c8 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -643,7 +643,7 @@ void diff_redraw(bool dofold)
if (!wp->w_p_diff) {
continue;
}
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
if (dofold && foldmethodIsDiff(wp)) {
foldUpdateAll(wp);
}
@@ -1415,7 +1415,7 @@ void diff_win_options(win_T *wp, int addbuf)
if (addbuf) {
diff_buf_add(wp->w_buffer);
}
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
/// Set options not to show diffs. For the current window or all windows.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 5ea6716c0a..d7cca9ba36 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4177,6 +4177,21 @@ static int ins_compl_get_exp(pos_T *ini)
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) {
// May change home directory back to "~".
tilde_replace(compl_pattern, num_matches, matches);
+#ifdef BACKSLASH_IN_FILENAME
+ if (curbuf->b_p_csl[0] != NUL) {
+ for (int i = 0; i < num_matches; i++) {
+ char_u *ptr = matches[i];
+ while (*ptr != NUL) {
+ if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
}
break;
@@ -5555,13 +5570,11 @@ void insertchar(
int second_indent // indent for second line if >= 0
)
{
- int textwidth;
char_u *p;
- int fo_ins_blank;
int force_format = flags & INSCHAR_FORMAT;
- textwidth = comp_textwidth(force_format);
- fo_ins_blank = has_format_option(FO_INS_BLANK);
+ const int textwidth = comp_textwidth(force_format);
+ const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
/*
* Try to break the line in two or more pieces when:
@@ -5762,10 +5775,11 @@ internal_format (
int cc;
int save_char = NUL;
bool haveto_redraw = false;
- int fo_ins_blank = has_format_option(FO_INS_BLANK);
- int fo_multibyte = has_format_option(FO_MBYTE_BREAK);
- int fo_white_par = has_format_option(FO_WHITE_PAR);
- int first_line = TRUE;
+ const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
+ const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
+ const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
+ const bool fo_white_par = has_format_option(FO_WHITE_PAR);
+ bool first_line = true;
colnr_T leader_len;
bool no_leader = false;
int do_comments = (flags & INSCHAR_DO_COM);
@@ -5844,6 +5858,7 @@ internal_format (
curwin->w_cursor.col = startcol;
foundcol = 0;
+ int skip_pos = 0;
/*
* Find position to break at.
@@ -5913,7 +5928,11 @@ internal_format (
foundcol = curwin->w_cursor.col;
if (curwin->w_cursor.col <= (colnr_T)wantcol)
break;
- } else if (cc >= 0x100 && fo_multibyte) {
+ } else if ((cc >= 0x100 || !utf_allow_break_before(cc))
+ && fo_multibyte) {
+ int ncc;
+ bool allow_break;
+
// Break after or before a multi-byte character.
if (curwin->w_cursor.col != startcol) {
// Don't break until after the comment leader
@@ -5922,8 +5941,11 @@ internal_format (
}
col = curwin->w_cursor.col;
inc_cursor();
- // Don't change end_foundcol if already set.
- if (foundcol != curwin->w_cursor.col) {
+ ncc = gchar_cursor();
+ allow_break = utf_allow_break(cc, ncc);
+
+ // If we have already checked this position, skip!
+ if (curwin->w_cursor.col != skip_pos && allow_break) {
foundcol = curwin->w_cursor.col;
end_foundcol = foundcol;
if (curwin->w_cursor.col <= (colnr_T)wantcol)
@@ -5935,6 +5957,7 @@ internal_format (
if (curwin->w_cursor.col == 0)
break;
+ ncc = cc;
col = curwin->w_cursor.col;
dec_cursor();
@@ -5943,17 +5966,56 @@ internal_format (
if (WHITECHAR(cc)) {
continue; // break with space
}
- // Don't break until after the comment leader
+ // Don't break until after the comment leader.
if (curwin->w_cursor.col < leader_len) {
break;
}
curwin->w_cursor.col = col;
+ skip_pos = curwin->w_cursor.col;
- foundcol = curwin->w_cursor.col;
- end_foundcol = foundcol;
- if (curwin->w_cursor.col <= (colnr_T)wantcol)
- break;
+ allow_break = utf_allow_break(cc, ncc);
+
+ // Must handle this to respect line break prohibition.
+ if (allow_break) {
+ foundcol = curwin->w_cursor.col;
+ end_foundcol = foundcol;
+ }
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ const bool ncc_allow_break = utf_allow_break_before(ncc);
+
+ if (allow_break) {
+ break;
+ }
+ if (!ncc_allow_break && !fo_rigor_tw) {
+ // Enable at most 1 punct hang outside of textwidth.
+ if (curwin->w_cursor.col == startcol) {
+ // We are inserting a non-breakable char, postpone
+ // line break check to next insert.
+ end_foundcol = foundcol = 0;
+ break;
+ }
+
+ // Neither cc nor ncc is NUL if we are here, so
+ // it's safe to inc_cursor.
+ col = curwin->w_cursor.col;
+
+ inc_cursor();
+ cc = ncc;
+ ncc = gchar_cursor();
+ // handle insert
+ ncc = (ncc != NUL) ? ncc : c;
+
+ allow_break = utf_allow_break(cc, ncc);
+
+ if (allow_break) {
+ // Break only when we are not at end of line.
+ end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col;
+ break;
+ }
+ curwin->w_cursor.col = col;
+ }
+ }
}
if (curwin->w_cursor.col == 0)
break;
@@ -6055,7 +6117,7 @@ internal_format (
}
}
}
- first_line = FALSE;
+ first_line = false;
}
if (State & VREPLACE_FLAG) {
@@ -6242,12 +6304,10 @@ static void check_auto_format(
* Set default to window width (maximum 79) for "gq" operator.
*/
int comp_textwidth(
- int ff // force formatting (for "gq" command)
+ bool ff // force formatting (for "gq" command)
)
{
- int textwidth;
-
- textwidth = curbuf->b_p_tw;
+ int textwidth = curbuf->b_p_tw;
if (textwidth == 0 && curbuf->b_p_wm) {
// The width is the window width minus 'wrapmargin' minus all the
// things that add to the margin.
@@ -7697,6 +7757,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
undisplay_dollar();
}
+ if (cmdchar != 'r' && cmdchar != 'v') {
+ ins_apply_autocmds(EVENT_INSERTLEAVEPRE);
+ }
+
// When an autoindent was removed, curswant stays after the
// indent
if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) {
@@ -8513,7 +8577,7 @@ static void ins_up(
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill
)
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
start_arrow(&tpos);
can_cindent = true;
} else {
@@ -8561,7 +8625,7 @@ static void ins_down(
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill
)
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
start_arrow(&tpos);
can_cindent = true;
} else {
@@ -8955,7 +9019,7 @@ static int ins_ctrl_ey(int tc)
scrolldown_clamp();
else
scrollup_clamp();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
} else {
c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1));
if (c != NUL) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a2490be355..cccf1e50ff 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2994,7 +2994,6 @@ char_u *get_user_var_name(expand_T *xp, int idx)
static size_t tdone;
static size_t vidx;
static hashitem_T *hi;
- hashtab_T *ht;
if (idx == 0) {
gdone = bdone = wdone = vidx = 0;
@@ -3015,7 +3014,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
}
// b: variables
- ht = &curbuf->b_vars->dv_hashtab;
+ // In cmdwin, the alternative buffer should be used.
+ hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
+ ? &prevwin->w_buffer->b_vars->dv_hashtab
+ : &curbuf->b_vars->dv_hashtab;
if (bdone < ht->ht_used) {
if (bdone++ == 0)
hi = ht->ht_array;
@@ -3027,7 +3029,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
}
// w: variables
- ht = &curwin->w_vars->dv_hashtab;
+ // In cmdwin, the alternative window should be used.
+ ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
+ ? &prevwin->w_vars->dv_hashtab
+ : &curwin->w_vars->dv_hashtab;
if (wdone < ht->ht_used) {
if (wdone++ == 0)
hi = ht->ht_array;
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 11f6a350e4..6c316bb1fe 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -275,6 +275,7 @@ return {
rpcrequest={args=varargs(2)},
rpcstart={args={1, 2}},
rpcstop={args=1},
+ rubyeval={args=1},
screenattr={args=2},
screenchar={args=2},
screencol={},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 38ffa38e20..83ef9c8762 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2071,6 +2071,12 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
expand_T xpc;
bool error = false;
char_u *result;
+#ifdef BACKSLASH_IN_FILENAME
+ char_u *p_csl_save = p_csl;
+
+ // avoid using 'completeslash' here
+ p_csl = empty_option;
+#endif
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN
@@ -2123,6 +2129,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = NULL;
}
}
+#ifdef BACKSLASH_IN_FILENAME
+ p_csl = p_csl_save;
+#endif
}
@@ -4007,7 +4016,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "globpath()" function
static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int flags = 0; // Flags for globpath.
+ int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath.
bool error = false;
// Return a string, or a list if the optional third argument is non-zero.
@@ -6381,6 +6390,12 @@ static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
script_host_eval("perl", argvars, rettv);
}
+// "rubyeval()" function
+static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ script_host_eval("ruby", argvars, rettv);
+}
+
/*
* "range()" function
*/
@@ -8141,15 +8156,17 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Create quickfix/location list from VimL values
///
/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
-/// list_arg, action_arg and what_arg arguments in which case errors out,
-/// including VAR_UNKNOWN parameters.
+/// args argument in which case errors out, including VAR_UNKNOWN parameters.
///
/// @param[in,out] wp Window to create location list for. May be NULL in
/// which case quickfix list will be created.
-/// @param[in] list_arg Quickfix list contents.
-/// @param[in] action_arg Action to perform: append to an existing list,
-/// replace its content or create a new one.
-/// @param[in] title_arg New list title. Defaults to caller function name.
+/// @param[in] args [list, action, what]
+/// @param[in] args[0] Quickfix list contents.
+/// @param[in] args[1] Optional. Action to perform:
+/// append to an existing list, replace its content,
+/// or create a new one.
+/// @param[in] args[2] Optional. Quickfix list properties or title.
+/// Defaults to caller function name.
/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(2, 3)
@@ -8159,7 +8176,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
int action = ' ';
static int recursive = 0;
rettv->vval.v_number = -1;
- dict_T *d = NULL;
+ dict_T *what = NULL;
typval_T *list_arg = &args[0];
if (list_arg->v_type != VAR_LIST) {
@@ -8187,18 +8204,18 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
return;
}
- typval_T *title_arg = &args[2];
- if (title_arg->v_type == VAR_UNKNOWN) {
+ typval_T *const what_arg = &args[2];
+ if (what_arg->v_type == VAR_UNKNOWN) {
// Option argument was not given.
goto skip_args;
- } else if (title_arg->v_type == VAR_STRING) {
- title = tv_get_string_chk(title_arg);
+ } else if (what_arg->v_type == VAR_STRING) {
+ title = tv_get_string_chk(what_arg);
if (!title) {
// Type error. Error already printed by tv_get_string_chk().
return;
}
- } else if (title_arg->v_type == VAR_DICT) {
- d = title_arg->vval.v_dict;
+ } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
+ what = what_arg->vval.v_dict;
} else {
EMSG(_(e_dictreq));
return;
@@ -8211,7 +8228,7 @@ skip_args:
recursive++;
list_T *const l = list_arg->vval.v_list;
- if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) {
+ if (set_errorlist(wp, l, action, (char_u *)title, what) == OK) {
rettv->vval.v_number = 0;
}
recursive--;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index e0361048bc..dc94bc698d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -1052,7 +1052,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- eval1(&p, rettv, true);
+ (void)eval1(&p, rettv, true);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -2920,8 +2920,10 @@ void ex_call(exarg_T *eap)
if (!failed || eap->cstack->cs_trylevel > 0) {
// Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
- emsg_severe = true;
- EMSG(_(e_trailing));
+ if (!failed) {
+ emsg_severe = true;
+ EMSG(_(e_trailing));
+ }
} else {
eap->nextcmd = check_nextcmd(arg);
}
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 3669cbbd2d..41137bf2db 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3721,8 +3721,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
update_topline();
validate_cursor();
update_screen(SOME_VALID);
- highlight_match = FALSE;
- redraw_later(SOME_VALID);
+ highlight_match = false;
+ redraw_later(curwin, SOME_VALID);
curwin->w_p_fen = save_p_fen;
if (msg_row == Rows - 1)
@@ -5751,7 +5751,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
}
xfree(str);
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
win_enter(save_curwin, false); // Return to original window
update_topline();
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 503fd8e0d0..713d18b44d 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2363,7 +2363,7 @@ void ex_compiler(exarg_T *eap)
do_unlet(S_LEN("b:current_compiler"), true);
snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg);
- if (source_runtime(buf, DIP_ALL) == FAIL) {
+ if (source_in_path(p_rtp, buf, DIP_ALL) == FAIL) {
EMSG2(_("E666: compiler not supported: %s"), eap->arg);
}
xfree(buf);
@@ -2581,6 +2581,7 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
/// return FAIL when no file could be sourced, OK otherwise.
int source_runtime(char_u *name, int flags)
{
+ flags |= (flags & DIP_NORTP) ? 0 : DIP_START;
return source_in_path(p_rtp, name, flags);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 71ac542323..d7ed2fe97d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1251,7 +1251,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
char_u *errormsg = NULL; // error message
char_u *after_modifier = NULL;
exarg_T ea;
- int save_msg_scroll = msg_scroll;
+ const int save_msg_scroll = msg_scroll;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
char_u *cmd;
@@ -2003,34 +2003,10 @@ doend:
? cmdnames[(int)ea.cmdidx].cmd_name
: (char_u *)NULL);
- if (ea.verbose_save >= 0) {
- p_verbose = ea.verbose_save;
- }
- free_cmdmod();
-
+ undo_cmdmod(&ea, save_msg_scroll);
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
- if (ea.save_msg_silent != -1) {
- // messages could be enabled for a serious error, need to check if the
- // counters don't become negative
- if (!did_emsg || msg_silent > ea.save_msg_silent) {
- msg_silent = ea.save_msg_silent;
- }
- emsg_silent -= ea.did_esilent;
- if (emsg_silent < 0) {
- emsg_silent = 0;
- }
- // Restore msg_scroll, it's set by file I/O commands, even when no
- // message is actually displayed.
- msg_scroll = save_msg_scroll;
-
- /* "silent reg" or "silent echo x" inside "redir" leaves msg_col
- * somewhere in the line. Put it back in the first column. */
- if (redirecting())
- msg_col = 0;
- }
-
if (ea.did_sandbox) {
sandbox--;
}
@@ -2298,9 +2274,14 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only)
return OK;
}
-// Free contents of "cmdmod".
-static void free_cmdmod(void)
+// Undo and free contents of "cmdmod".
+static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll)
+ FUNC_ATTR_NONNULL_ALL
{
+ if (eap->verbose_save >= 0) {
+ p_verbose = eap->verbose_save;
+ }
+
if (cmdmod.save_ei != NULL) {
/* Restore 'eventignore' to the value before ":noautocmd". */
set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
@@ -2308,8 +2289,27 @@ static void free_cmdmod(void)
free_string_option(cmdmod.save_ei);
}
- if (cmdmod.filter_regmatch.regprog != NULL) {
- vim_regfree(cmdmod.filter_regmatch.regprog);
+ vim_regfree(cmdmod.filter_regmatch.regprog);
+
+ if (eap->save_msg_silent != -1) {
+ // messages could be enabled for a serious error, need to check if the
+ // counters don't become negative
+ if (!did_emsg || msg_silent > eap->save_msg_silent) {
+ msg_silent = eap->save_msg_silent;
+ }
+ emsg_silent -= eap->did_esilent;
+ if (emsg_silent < 0) {
+ emsg_silent = 0;
+ }
+ // Restore msg_scroll, it's set by file I/O commands, even when no
+ // message is actually displayed.
+ msg_scroll = save_msg_scroll;
+
+ // "silent reg" or "silent echo x" inside "redir" leaves msg_col
+ // somewhere in the line. Put it back in the first column.
+ if (redirecting()) {
+ msg_col = 0;
+ }
}
}
@@ -4446,6 +4446,9 @@ void separate_nextcmd(exarg_T *eap)
else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) {
p += 2;
(void)skip_expr(&p);
+ if (*p == NUL) { // stop at NUL after CTRL-V
+ break;
+ }
}
/* Check for '"': start of comment or '|': next command */
/* :@" does not start a comment!
@@ -5400,7 +5403,7 @@ invalid_count:
if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) {
return FAIL;
}
- if (addr_type_arg != ADDR_LINES) {
+ if (*addr_type_arg != ADDR_LINES) {
*argt |= (ZEROR | NOTADR);
}
} else {
@@ -7379,7 +7382,7 @@ static void ex_syncbind(exarg_T *eap)
else
scrolldown(-y, TRUE);
curwin->w_scbind_pos = topline;
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
cursor_correct();
curwin->w_redr_status = TRUE;
}
@@ -8504,7 +8507,7 @@ static void ex_pedit(exarg_T *eap)
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
win_enter(curwin_save, true);
}
g_do_tagpreview = 0;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index f9ca7bfa42..53feffd2d7 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4990,7 +4990,7 @@ ExpandFromContext (
char_u *pat,
int *num_file,
char_u ***file,
- int options /* EW_ flags */
+ int options // WILD_ flags
)
{
regmatch_T regmatch;
@@ -5052,6 +5052,21 @@ ExpandFromContext (
ret = expand_wildcards_eval(&pat, num_file, file, flags);
if (free_pat)
xfree(pat);
+#ifdef BACKSLASH_IN_FILENAME
+ if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
+ for (int i = 0; i < *num_file; i++) {
+ char_u *ptr = (*file)[i];
+ while (*ptr != NUL) {
+ if (p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
return ret;
}
@@ -5193,7 +5208,7 @@ ExpandFromContext (
* obtain strings, one by one. The strings are matched against a regexp
* program. Matching strings are copied into an array, which is returned.
*/
-void ExpandGeneric(
+static void ExpandGeneric(
expand_T *xp,
regmatch_T *regmatch,
int *num_file,
@@ -6468,7 +6483,7 @@ static int open_cmdwin(void)
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
// Save the command line info, can be used recursively.
save_cmdline(&save_ccline);
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 6b00b986dc..42a9ef08f9 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -897,8 +897,8 @@ void ex_mkrc(exarg_T *eap)
if (!failed && view_session) {
if (put_line(fd,
- "let s:so_save = &so | let s:siso_save = &siso"
- " | set so=0 siso=0") == FAIL) {
+ "let s:so_save = &g:so | let s:siso_save = &g:siso"
+ " | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) {
failed = true;
}
if (eap->cmdidx == CMD_mksession) {
@@ -949,7 +949,7 @@ void ex_mkrc(exarg_T *eap)
}
if (fprintf(fd,
"%s",
- "let &so = s:so_save | let &siso = s:siso_save\n"
+ "let &g:so = s:so_save | let &g:siso = s:siso_save\n"
"doautoall SessionLoadPost\n")
< 0) {
failed = true;
diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h
index f5ca0ebbb0..382aaf6ed3 100644
--- a/src/nvim/extmark_defs.h
+++ b/src/nvim/extmark_defs.h
@@ -50,10 +50,13 @@ typedef struct {
LuaRef redraw_win;
LuaRef redraw_line;
LuaRef redraw_end;
+ LuaRef hl_def;
+ int hl_valid;
} DecorationProvider;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorationProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, LUA_NOREF, LUA_NOREF }
+ LUA_NOREF, LUA_NOREF, LUA_NOREF, \
+ LUA_NOREF, -1 }
#endif // NVIM_EXTMARK_DEFS_H
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 197aedabec..57001f77ed 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -862,7 +862,7 @@ void foldUpdateAfterInsert(void)
void foldUpdateAll(win_T *win)
{
win->w_foldinvalid = true;
- redraw_win_later(win, NOT_VALID);
+ redraw_later(win, NOT_VALID);
}
// foldMoveTo() {{{2
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index a8cf496cb9..d80a6219eb 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -141,9 +141,6 @@ local dump_option = function(i, o)
elseif #o.scope == 1 and o.scope[1] == 'window' then
w(' .var=VAR_WIN')
end
- if o.enable_if then
- w('#endif')
- end
if #o.scope == 1 and o.scope[1] == 'global' then
w(' .indir=PV_NONE')
else
@@ -163,6 +160,12 @@ local dump_option = function(i, o)
defines['PV_' .. varname:sub(3):upper()] = pv_name
w(' .indir=' .. pv_name)
end
+ if o.enable_if then
+ w('#else')
+ w(' .var=NULL')
+ w(' .indir=PV_NONE')
+ w('#endif')
+ end
if o.defaults then
if o.defaults.condition then
w(get_cond(o.defaults.condition))
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 2db8689a56..31dd3fc848 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -92,6 +92,10 @@ EXTERN struct nvim_stats_s {
EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen
EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen
+EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights
+EXTERN bool ns_hl_changed INIT(= false); // highlight need update
+
+
// We use 64-bit file functions here, if available. E.g. ftello() returns
// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit.
// We assume that when fseeko() is available then ftello() is too.
@@ -941,8 +945,10 @@ EXTERN char_u e_readonly[] INIT(= N_(
EXTERN char_u e_readonlyvar[] INIT(= N_(
"E46: Cannot change read-only variable \"%.*s\""));
EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required"));
-EXTERN char_u e_toomanyarg[] INIT(= N_("E118: Too many arguments for function: %s"));
-EXTERN char_u e_dictkey[] INIT(= N_("E716: Key not present in Dictionary: %s"));
+EXTERN char_u e_toomanyarg[] INIT(= N_(
+ "E118: Too many arguments for function: %s"));
+EXTERN char_u e_dictkey[] INIT(= N_(
+ "E716: Key not present in Dictionary: \"%s\""));
EXTERN char_u e_listreq[] INIT(= N_("E714: List required"));
EXTERN char_u e_listdictarg[] INIT(= N_(
"E712: Argument of %s must be a List or Dictionary"));
diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h
index 19633d455f..c82a6cc121 100644
--- a/src/nvim/hashtab.h
+++ b/src/nvim/hashtab.h
@@ -51,6 +51,7 @@ typedef struct hashitem_S {
/// Initial size for a hashtable.
/// Our items are relatively small and growing is expensive, thus start with 16.
/// Must be a power of 2.
+/// This allows for storing 10 items (2/3 of 16) before a resize is needed.
#define HT_INIT_SIZE 16
/// An array-based hashtable.
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 262afba07a..8403615166 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -14,6 +14,7 @@
#include "nvim/ui.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/lua/executor.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "highlight.c.generated.h"
@@ -28,12 +29,16 @@ static Map(int, int) *combine_attr_entries;
static Map(int, int) *blend_attr_entries;
static Map(int, int) *blendthrough_attr_entries;
+/// highlight entries private to a namespace
+static Map(ColorKey, ColorItem) *ns_hl;
+
void highlight_init(void)
{
attr_entry_ids = map_new(HlEntry, int)();
combine_attr_entries = map_new(int, int)();
blend_attr_entries = map_new(int, int)();
blendthrough_attr_entries = map_new(int, int)();
+ ns_hl = map_new(ColorKey, ColorItem)();
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
@@ -129,21 +134,114 @@ void ui_send_all_hls(UI *ui)
}
/// Get attribute code for a syntax group.
-int hl_get_syn_attr(int idx, HlAttrs at_en)
+int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
{
// TODO(bfredl): should we do this unconditionally
if (at_en.cterm_fg_color != 0 || at_en.cterm_bg_color != 0
|| at_en.rgb_fg_color != -1 || at_en.rgb_bg_color != -1
|| at_en.rgb_sp_color != -1 || at_en.cterm_ae_attr != 0
- || at_en.rgb_ae_attr != 0) {
+ || at_en.rgb_ae_attr != 0 || ns_id != 0) {
return get_attr_entry((HlEntry){ .attr = at_en, .kind = kHlSyntax,
- .id1 = idx, .id2 = 0 });
+ .id1 = idx, .id2 = ns_id });
} else {
// If all the fields are cleared, clear the attr field back to default value
return 0;
}
}
+static ColorKey colored_key(NS ns_id, int syn_id)
+{
+ return (ColorKey){ .ns_id = (int)ns_id, .syn_id = syn_id };
+}
+
+void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id)
+{
+ DecorationProvider *p = get_provider(ns_id, true);
+ int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
+ ColorItem it = { .attr_id = attr_id,
+ .link_id = link_id,
+ .version = p->hl_valid };
+ map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it);
+}
+
+int ns_get_hl(NS ns_id, int hl_id, bool link)
+{
+ static int recursive = 0;
+
+ if (ns_id < 0) {
+ if (ns_hl_active <= 0) {
+ return -1;
+ }
+ ns_id = ns_hl_active;
+ }
+
+ DecorationProvider *p = get_provider(ns_id, true);
+ ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id));
+ // TODO(bfredl): map_ref true even this?
+ bool valid_cache = it.version >= p->hl_valid;
+
+ if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) {
+ FIXED_TEMP_ARRAY(args, 3);
+ args.items[0] = INTEGER_OBJ((Integer)ns_id);
+ args.items[1] = STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id)));
+ args.items[2] = BOOLEAN_OBJ(link);
+ // TODO(bfredl): preload the "global" attr dict?
+
+ Error err = ERROR_INIT;
+ recursive++;
+ Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err);
+ recursive--;
+
+ // TODO(bfredl): or "inherit", combine with global value?
+ bool fallback = true;
+ int tmp = false;
+ HlAttrs attrs = HLATTRS_INIT;
+ if (ret.type == kObjectTypeDictionary) {
+ Dictionary dict = ret.data.dictionary;
+ fallback = false;
+ attrs = dict2hlattrs(dict, true, &it.link_id, &err);
+ for (size_t i = 0; i < dict.size; i++) {
+ char *key = dict.items[i].key.data;
+ Object val = dict.items[i].value;
+ bool truthy = api_object_to_bool(val, key, false, &err);
+
+ if (strequal(key, "fallback")) {
+ fallback = truthy;
+ } else if (strequal(key, "temp")) {
+ tmp = truthy;
+ }
+ }
+ if (it.link_id >= 0) {
+ fallback = true;
+ }
+ }
+
+ it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
+ it.version = p->hl_valid-tmp;
+ map_put(ColorKey, ColorItem)(ns_hl, colored_key(ns_id, hl_id), it);
+ }
+
+ if (link) {
+ return it.attr_id >= 0 ? -1 : it.link_id;
+ } else {
+ return it.attr_id;
+ }
+}
+
+
+bool win_check_ns_hl(win_T *wp)
+{
+ if (ns_hl_changed) {
+ highlight_changed();
+ if (wp) {
+ update_window_hl(wp, true);
+ }
+ ns_hl_changed = false;
+ return true;
+ }
+ return false;
+}
+
/// Get attribute code for a builtin highlight group.
///
/// The final syntax group could be modified by hi-link or 'winhighlight'.
@@ -204,6 +302,17 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
+ // NOOOO! You cannot just pretend that "Normal" is just like any other
+ // syntax group! It needs at least 10 layers of special casing! Noooooo!
+ //
+ // haha, theme engine go brrr
+ int normality = syn_check_group((const char_u *)S_LEN("Normal"));
+ int ns_attr = ns_get_hl(-1, normality, false);
+ if (ns_attr > 0) {
+ // TODO(bfredl): hantera NormalNC and so on
+ wp->w_hl_attr_normal = ns_attr;
+ }
+
// if blend= attribute is not set, 'winblend' value overrides it.
if (wp->w_floating && wp->w_p_winbl > 0) {
HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal);
@@ -275,6 +384,7 @@ void clear_hl_tables(bool reinit)
map_free(int, int)(combine_attr_entries);
map_free(int, int)(blend_attr_entries);
map_free(int, int)(blendthrough_attr_entries);
+ map_free(ColorKey, ColorItem)(ns_hl);
}
}
@@ -663,6 +773,103 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
return hl;
}
+HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err)
+{
+ HlAttrs hlattrs = HLATTRS_INIT;
+
+ int32_t fg = -1, bg = -1, sp = -1;
+ int16_t mask = 0;
+ for (size_t i = 0; i < dict.size; i++) {
+ char *key = dict.items[i].key.data;
+ Object val = dict.items[i].value;
+
+ struct {
+ const char *name;
+ int16_t flag;
+ } flags[] = {
+ { "bold", HL_BOLD },
+ { "standout", HL_STANDOUT },
+ { "underline", HL_UNDERLINE },
+ { "undercurl", HL_UNDERCURL },
+ { "italic", HL_ITALIC },
+ { "reverse", HL_INVERSE },
+ { NULL, 0 },
+ };
+
+ int j;
+ for (j = 0; flags[j].name; j++) {
+ if (strequal(flags[j].name, key)) {
+ if (api_object_to_bool(val, key, false, err)) {
+ mask = mask | flags[j].flag;
+ }
+ break;
+ }
+ }
+
+ struct {
+ const char *name;
+ const char *shortname;
+ int *dest;
+ } colors[] = {
+ { "foreground", "fg", &fg },
+ { "background", "bg", &bg },
+ { "special", "sp", &sp },
+ { NULL, NULL, NULL },
+ };
+
+ int k;
+ for (k = 0; (!flags[j].name) && colors[k].name; k++) {
+ if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) {
+ if (val.type == kObjectTypeInteger) {
+ *colors[k].dest = (int)val.data.integer;
+ } else if (val.type == kObjectTypeString) {
+ String str = val.data.string;
+ // TODO(bfredl): be more fancy with "bg", "fg" etc
+ if (str.size) {
+ *colors[k].dest = name_to_color(str.data);
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'%s' must be string or integer", key);
+ }
+ break;
+ }
+ }
+
+
+ if (flags[j].name || colors[k].name) {
+ // handled above
+ } else if (link_id && strequal(key, "link")) {
+ if (val.type == kObjectTypeString) {
+ String str = val.data.string;
+ *link_id = syn_check_group((const char_u *)str.data, (int)str.size);
+ } else if (val.type == kObjectTypeInteger) {
+ // TODO(bfredl): validate range?
+ *link_id = (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'link' must be string or integer");
+ }
+ }
+
+ if (ERROR_SET(err)) {
+ return hlattrs; // error set, caller should not use retval
+ }
+ }
+
+ if (use_rgb) {
+ hlattrs.rgb_ae_attr = mask;
+ hlattrs.rgb_bg_color = bg;
+ hlattrs.rgb_fg_color = fg;
+ hlattrs.rgb_sp_color = sp;
+ } else {
+ hlattrs.cterm_ae_attr = mask;
+ hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1;
+ hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1;
+ }
+
+ return hlattrs;
+}
Array hl_inspect(int attr)
{
Array ret = ARRAY_DICT_INIT;
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 36f3181674..6a5c593ab1 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -4,6 +4,7 @@
#include <inttypes.h>
#include "nvim/macros.h"
+#include "nvim/types.h"
typedef int32_t RgbValue;
@@ -180,6 +181,20 @@ typedef struct {
HlKind kind;
int id1;
int id2;
+ int winid;
} HlEntry;
+typedef struct {
+ int ns_id;
+ int syn_id;
+} ColorKey;
+
+typedef struct {
+ int attr_id;
+ int link_id;
+ int version;
+} ColorItem;
+#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1 }
+
+
#endif // NVIM_HIGHLIGHT_DEFS_H
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index bb0fdfec01..9e6693afdf 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -295,13 +295,17 @@ int set_indent(int size, int flags)
// Replace the line (unless undo fails).
if (!(flags & SIN_UNDO) || (u_savesub(curwin->w_cursor.lnum) == OK)) {
+ const colnr_T old_offset = (colnr_T)(p - oldline);
+ const colnr_T new_offset = (colnr_T)(s - newline);
+
+ // this may free "newline"
ml_replace(curwin->w_cursor.lnum, newline, false);
if (!(flags & SIN_NOMARK)) {
extmark_splice_cols(curbuf,
(int)curwin->w_cursor.lnum-1,
skipcols,
- (int)(p-oldline) - skipcols,
- (int)(s-newline) - skipcols,
+ old_offset - skipcols,
+ new_offset - skipcols,
kExtmarkUndo);
}
@@ -311,15 +315,14 @@ int set_indent(int size, int flags)
// Correct saved cursor position if it is in this line.
if (saved_cursor.lnum == curwin->w_cursor.lnum) {
- if (saved_cursor.col >= (colnr_T)(p - oldline)) {
+ if (saved_cursor.col >= old_offset) {
// Cursor was after the indent, adjust for the number of
// bytes added/removed.
- saved_cursor.col += ind_len - (colnr_T)(p - oldline);
-
- } else if (saved_cursor.col >= (colnr_T)(s - newline)) {
+ saved_cursor.col += ind_len - old_offset;
+ } else if (saved_cursor.col >= new_offset) {
// Cursor was in the indent, and is now after it, put it back
// at the start of the indent (replacing spaces with TAB).
- saved_cursor.col = (colnr_T)(s - newline);
+ saved_cursor.col = new_offset;
}
}
retval = true;
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index bb443161ef..9298e57411 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -1676,6 +1676,9 @@ void parse_cino(buf_T *buf)
// Handle C++ extern "C" or "C++"
buf->b_ind_cpp_extern_c = 0;
+ // Handle C #pragma directives
+ buf->b_ind_pragma = 0;
+
for (p = buf->b_p_cino; *p; ) {
l = p++;
if (*p == '-') {
@@ -1747,6 +1750,7 @@ void parse_cino(buf_T *buf)
case 'N': buf->b_ind_cpp_namespace = n; break;
case 'k': buf->b_ind_if_for_while = n; break;
case 'E': buf->b_ind_cpp_extern_c = n; break;
+ case 'P': buf->b_ind_pragma = n; break;
}
if (*p == ',')
++p;
@@ -1858,12 +1862,14 @@ int get_c_indent(void)
goto laterend;
}
- /*
- * #defines and so on always go at the left when included in 'cinkeys'.
- */
+ // #defines and so on go at the left when included in 'cinkeys',
+ // exluding pragmas when customized in 'cinoptions'
if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', true))) {
- amount = curbuf->b_ind_hash_comment;
- goto theend;
+ const char_u *const directive = skipwhite(theline + 1);
+ if (curbuf->b_ind_pragma == 0 || STRNCMP(directive, "pragma", 6) != 0) {
+ amount = curbuf->b_ind_hash_comment;
+ goto theend;
+ }
}
/*
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 5c665920b5..a095f298f2 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -549,14 +549,6 @@ static lua_State *nlua_enter(void)
// stack: (empty)
lua_getglobal(lstate, "vim");
// stack: vim
- lua_getfield(lstate, -1, "_update_package_paths");
- // stack: vim, vim._update_package_paths
- if (lua_pcall(lstate, 0, 0, 0)) {
- // stack: vim, error
- nlua_error(lstate, _("E5117: Error while updating package paths: %.*s"));
- // stack: vim
- }
- // stack: vim
lua_pop(lstate, 1);
// stack: (empty)
last_p_rtp = (const void *)p_rtp;
@@ -564,14 +556,6 @@ static lua_State *nlua_enter(void)
return lstate;
}
-/// Force an update of lua's package paths if runtime path has changed.
-bool nlua_update_package_path(void)
-{
- lua_State *const lstate = nlua_enter();
-
- return !!lstate;
-}
-
static void nlua_print_event(void **argv)
{
char *str = argv[0];
@@ -891,6 +875,17 @@ LuaRef nlua_newref(lua_State *lstate, LuaRef original_ref)
return new_ref;
}
+LuaRef api_new_luaref(LuaRef original_ref)
+{
+ if (original_ref == LUA_NOREF) {
+ return LUA_NOREF;
+ }
+
+ lua_State *const lstate = nlua_enter();
+ return nlua_newref(lstate, original_ref);
+}
+
+
/// Evaluate lua string
///
/// Used for luaeval().
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 5258352e72..c53e208b00 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -58,6 +58,7 @@ static struct luaL_Reg node_meta[] = {
{ "__tostring", node_tostring },
{ "__eq", node_eq },
{ "__len", node_child_count },
+ { "id", node_id },
{ "range", node_range },
{ "start", node_start },
{ "end_", node_end },
@@ -176,10 +177,11 @@ int tslua_add_language(lua_State *L)
}
uint32_t lang_version = ts_language_version(lang);
- if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION) {
+ if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION
+ || lang_version > TREE_SITTER_LANGUAGE_VERSION) {
return luaL_error(
L,
- "ABI version mismatch : expected %" PRIu32 ", found %" PRIu32,
+ "ABI version mismatch : expected %d, found %d",
TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION, lang_version);
}
@@ -246,7 +248,12 @@ int tslua_push_parser(lua_State *L)
}
TSParser *parser = ts_parser_new();
- ts_parser_set_language(parser, lang);
+
+ if (!ts_parser_set_language(parser, lang)) {
+ ts_parser_delete(parser);
+ return luaL_error(L, "Failed to load language : %s", lang_name);
+ }
+
TSLua_parser *p = lua_newuserdata(L, sizeof(TSLua_parser)); // [udata]
p->parser = parser;
p->tree = NULL;
@@ -342,7 +349,7 @@ static int parser_parse(lua_State *L)
return 0;
}
- TSTree *new_tree;
+ TSTree *new_tree = NULL;
size_t len;
const char *str;
long bufnr;
@@ -374,6 +381,12 @@ static int parser_parse(lua_State *L)
return luaL_error(L, "invalid argument to parser:parse()");
}
+ // Sometimes parsing fails (timeout, or wrong parser ABI)
+ // In those case, just return an error.
+ if (!new_tree) {
+ return luaL_error(L, "An error occured when parsing.");
+ }
+
uint32_t n_ranges = 0;
TSRange *changed = p->tree ? ts_tree_get_changed_ranges(p->tree, new_tree,
&n_ranges) : NULL;
@@ -621,6 +634,17 @@ static int node_eq(lua_State *L)
return 1;
}
+static int node_id(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ lua_pushlstring(L, (const char *)&node.id, sizeof node.id);
+ return 1;
+}
+
static int node_range(lua_State *L)
{
TSNode node;
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index bfa8b91208..85d39eaef4 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -92,67 +92,48 @@ function vim._os_proc_children(ppid)
return children
end
--- TODO(ZyX-I): Create compatibility layer.
---{{{1 package.path updater function
--- Last inserted paths. Used to clear out items from package.[c]path when they
--- are no longer in &runtimepath.
-local last_nvim_paths = {}
-function vim._update_package_paths()
- local cur_nvim_paths = {}
- local rtps = vim.api.nvim_list_runtime_paths()
- local sep = package.config:sub(1, 1)
- for _, key in ipairs({'path', 'cpath'}) do
- local orig_str = package[key] .. ';'
- local pathtrails_ordered = {}
- local orig = {}
- -- Note: ignores trailing item without trailing `;`. Not using something
- -- simpler in order to preserve empty items (stand for default path).
- for s in orig_str:gmatch('[^;]*;') do
- s = s:sub(1, -2) -- Strip trailing semicolon
- orig[#orig + 1] = s
- end
- if key == 'path' then
- -- /?.lua and /?/init.lua
- pathtrails_ordered = {sep .. '?.lua', sep .. '?' .. sep .. 'init.lua'}
- else
- local pathtrails = {}
- for _, s in ipairs(orig) do
- -- Find out path patterns. pathtrail should contain something like
- -- /?.so, \?.dll. This allows not to bother determining what correct
- -- suffixes are.
- local pathtrail = s:match('[/\\][^/\\]*%?.*$')
- if pathtrail and not pathtrails[pathtrail] then
- pathtrails[pathtrail] = true
- pathtrails_ordered[#pathtrails_ordered + 1] = pathtrail
- end
- end
- end
- local new = {}
- for _, rtp in ipairs(rtps) do
- if not rtp:match(';') then
- for _, pathtrail in pairs(pathtrails_ordered) do
- local new_path = rtp .. sep .. 'lua' .. pathtrail
- -- Always keep paths from &runtimepath at the start:
- -- append them here disregarding orig possibly containing one of them.
- new[#new + 1] = new_path
- cur_nvim_paths[new_path] = true
- end
- end
+local pathtrails = {}
+vim._so_trails = {}
+for s in (package.cpath..';'):gmatch('[^;]*;') do
+ s = s:sub(1, -2) -- Strip trailing semicolon
+ -- Find out path patterns. pathtrail should contain something like
+ -- /?.so, \?.dll. This allows not to bother determining what correct
+ -- suffixes are.
+ local pathtrail = s:match('[/\\][^/\\]*%?.*$')
+ if pathtrail and not pathtrails[pathtrail] then
+ pathtrails[pathtrail] = true
+ table.insert(vim._so_trails, pathtrail)
+ end
+end
+
+function vim._load_package(name)
+ -- tricky: when debugging this function we must let vim.inspect
+ -- module to be loaded first:
+ --local inspect = (name == "vim.inspect") and tostring or vim.inspect
+
+ local basename = name:gsub('%.', '/')
+ local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"}
+ for _,path in ipairs(paths) do
+ local found = vim.api.nvim_get_runtime_file(path, false)
+ if #found > 0 then
+ return loadfile(found[1])
end
- for _, orig_path in ipairs(orig) do
- -- Handle removing obsolete paths originating from &runtimepath: such
- -- paths either belong to cur_nvim_paths and were already added above or
- -- to last_nvim_paths and should not be added at all if corresponding
- -- entry was removed from &runtimepath list.
- if not (cur_nvim_paths[orig_path] or last_nvim_paths[orig_path]) then
- new[#new + 1] = orig_path
- end
+ end
+
+ for _,trail in ipairs(vim._so_trails) do
+ local path = "lua/"..trail:gsub('?',basename)
+ local found = vim.api.nvim_get_runtime_file(path, false)
+ if #found > 0 then
+ return package.loadlib(found[1])
end
- package[key] = table.concat(new, ';')
end
- last_nvim_paths = cur_nvim_paths
+ return nil
end
+table.insert(package.loaders, 1, vim._load_package)
+
+-- TODO(ZyX-I): Create compatibility layer.
+
--- Return a human-readable representation of the given object.
---
--@see https://github.com/kikito/inspect.lua
diff --git a/src/nvim/main.c b/src/nvim/main.c
index a22df9cc69..27bf5c45c6 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -446,7 +446,7 @@ int main(int argc, char **argv)
if (exmode_active || use_remote_ui || use_builtin_ui) {
// Don't clear the screen when starting in Ex mode, or when a UI might have
// displayed messages.
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
} else {
screenclear(); // clear screen
TIME_MSG("clearing screen");
diff --git a/src/nvim/map.c b/src/nvim/map.c
index ca8ea76333..7d97b7f13d 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -12,6 +12,9 @@
#include <stdbool.h>
#include <string.h>
+#include <lua.h>
+#include <lauxlib.h>
+
#include "nvim/map.h"
#include "nvim/map_defs.h"
#include "nvim/vim.h"
@@ -173,6 +176,20 @@ static inline bool HlEntry_eq(HlEntry ae1, HlEntry ae2)
return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
}
+static inline khint_t ColorKey_hash(ColorKey ae)
+{
+ const uint8_t *data = (const uint8_t *)&ae;
+ khint_t h = 0;
+ for (size_t i = 0; i < sizeof(ae); i++) {
+ h = (h << 5) - h + data[i];
+ }
+ return h;
+}
+
+static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2)
+{
+ return memcmp(&ae1, &ae2, sizeof(ae1)) == 0;
+}
MAP_IMPL(int, int, DEFAULT_INITIALIZER)
@@ -191,6 +208,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0)
+MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
/// Deletes a key:value pair from a string:pointer map, and frees the
/// storage of both key and value.
diff --git a/src/nvim/map.h b/src/nvim/map.h
index 63a18f4129..7bd3d31330 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -55,6 +55,8 @@ MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
MAP_DECLS(String, handle_T)
+MAP_DECLS(ColorKey, ColorItem)
+
#define map_new(T, U) map_##T##_##U##_new
#define map_free(T, U) map_##T##_##U##_free
#define map_get(T, U) map_##T##_##U##_get
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 6d188c6cd0..ec4f4cbc21 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -1624,6 +1624,146 @@ int utf_head_off(const char_u *base, const char_u *p)
return (int)(p - q);
}
+// Whether space is NOT allowed before/after 'c'.
+bool utf_eat_space(int cc)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return (cc >= 0x2000 && cc <= 0x206F) // General punctuations
+ || (cc >= 0x2e00 && cc <= 0x2e7f) // Supplemental punctuations
+ || (cc >= 0x3000 && cc <= 0x303f) // CJK symbols and punctuations
+ || (cc >= 0xff01 && cc <= 0xff0f) // Full width ASCII punctuations
+ || (cc >= 0xff1a && cc <= 0xff20) // ..
+ || (cc >= 0xff3b && cc <= 0xff40) // ..
+ || (cc >= 0xff5b && cc <= 0xff65); // ..
+}
+
+// Whether line break is allowed before "cc".
+bool utf_allow_break_before(int cc)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ static const int BOL_prohibition_punct[] = {
+ '!',
+ '%',
+ ')',
+ ',',
+ ':',
+ ';',
+ '>',
+ '?',
+ ']',
+ '}',
+ 0x2019, // ’ right single quotation mark
+ 0x201d, // ” right double quotation mark
+ 0x2020, // † dagger
+ 0x2021, // ‡ double dagger
+ 0x2026, // … horizontal ellipsis
+ 0x2030, // ‰ per mille sign
+ 0x2031, // ‱ per then thousand sign
+ 0x203c, // ‼ double exclamation mark
+ 0x2047, // ⁇ double question mark
+ 0x2048, // ⁈ question exclamation mark
+ 0x2049, // ⁉ exclamation question mark
+ 0x2103, // ℃ degree celsius
+ 0x2109, // ℉ degree fahrenheit
+ 0x3001, // 、 ideographic comma
+ 0x3002, // 。 ideographic full stop
+ 0x3009, // 〉 right angle bracket
+ 0x300b, // 》 right double angle bracket
+ 0x300d, // 」 right corner bracket
+ 0x300f, // 』 right white corner bracket
+ 0x3011, // 】 right black lenticular bracket
+ 0x3015, // 〕 right tortoise shell bracket
+ 0x3017, // 〗 right white lenticular bracket
+ 0x3019, // 〙 right white tortoise shell bracket
+ 0x301b, // 〛 right white square bracket
+ 0xff01, // ! fullwidth exclamation mark
+ 0xff09, // ) fullwidth right parenthesis
+ 0xff0c, // , fullwidth comma
+ 0xff0e, // . fullwidth full stop
+ 0xff1a, // : fullwidth colon
+ 0xff1b, // ; fullwidth semicolon
+ 0xff1f, // ? fullwidth question mark
+ 0xff3d, // ] fullwidth right square bracket
+ 0xff5d, // } fullwidth right curly bracket
+ };
+
+ int first = 0;
+ int last = ARRAY_SIZE(BOL_prohibition_punct) - 1;
+
+ while (first < last) {
+ const int mid = (first + last) / 2;
+
+ if (cc == BOL_prohibition_punct[mid]) {
+ return false;
+ } else if (cc > BOL_prohibition_punct[mid]) {
+ first = mid + 1;
+ } else {
+ last = mid - 1;
+ }
+ }
+
+ return cc != BOL_prohibition_punct[first];
+}
+
+// Whether line break is allowed after "cc".
+bool utf_allow_break_after(int cc)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ static const int EOL_prohibition_punct[] = {
+ '(',
+ '<',
+ '[',
+ '`',
+ '{',
+ // 0x2014, // — em dash
+ 0x2018, // ‘ left single quotation mark
+ 0x201c, // “ left double quotation mark
+ // 0x2053, // ~ swung dash
+ 0x3008, // 〈 left angle bracket
+ 0x300a, // 《 left double angle bracket
+ 0x300c, // 「 left corner bracket
+ 0x300e, // 『 left white corner bracket
+ 0x3010, // 【 left black lenticular bracket
+ 0x3014, // 〔 left tortoise shell bracket
+ 0x3016, // 〖 left white lenticular bracket
+ 0x3018, // 〘 left white tortoise shell bracket
+ 0x301a, // 〚 left white square bracket
+ 0xff08, // ( fullwidth left parenthesis
+ 0xff3b, // [ fullwidth left square bracket
+ 0xff5b, // { fullwidth left curly bracket
+ };
+
+ int first = 0;
+ int last = ARRAY_SIZE(EOL_prohibition_punct) - 1;
+
+ while (first < last) {
+ const int mid = (first + last)/2;
+
+ if (cc == EOL_prohibition_punct[mid]) {
+ return false;
+ } else if (cc > EOL_prohibition_punct[mid]) {
+ first = mid + 1;
+ } else {
+ last = mid - 1;
+ }
+ }
+
+ return cc != EOL_prohibition_punct[first];
+}
+
+// Whether line break is allowed between "cc" and "ncc".
+bool utf_allow_break(int cc, int ncc)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // don't break between two-letter punctuations
+ if (cc == ncc
+ && (cc == 0x2014 // em dash
+ || cc == 0x2026)) { // horizontal ellipsis
+ return false;
+ }
+ return utf_allow_break_after(cc) && utf_allow_break_before(ncc);
+}
+
/// Copy a character, advancing the pointers
///
/// @param[in,out] fp Source of the character to copy.
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index b060464383..4ba2e36656 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -193,7 +193,7 @@ ex_menu(exarg_T *eap)
vimmenu_T **root_menu_ptr = get_root_menu(menu_path);
if (root_menu_ptr == &curwin->w_winbar) {
// Assume the window toolbar menu will change.
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
if (enable != kNone) {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 6cd5616acf..06ba607323 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1216,7 +1216,7 @@ void wait_return(int redraw)
ui_refresh();
} else if (!skip_redraw) {
if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
}
if (ui_has(kUIMessages)) {
msg_ext_clear(true);
@@ -2222,7 +2222,7 @@ void msg_scroll_up(bool may_throttle)
///
/// Probably message scrollback storage should reimplented as a file_buffer, and
/// message scrolling in TUI be reimplemented as a modal floating window. Then
-/// we get throttling "for free" using standard redraw_win_later code paths.
+/// we get throttling "for free" using standard redraw_later code paths.
void msg_scroll_flush(void)
{
if (msg_grid.throttled) {
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index fcd9ee4f75..748001dcec 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -286,7 +286,7 @@ retnomove:
check_topfill(curwin, false);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
row = 0;
} else if (row >= curwin->w_height_inner) {
count = 0;
@@ -316,7 +316,7 @@ retnomove:
}
}
check_topfill(curwin, false);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
row = curwin->w_height_inner - 1;
@@ -450,7 +450,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
// skip line number and fold column in front of the line
col -= win_col_off(win);
- if (col < 0) {
+ if (col <= 0) {
col = 0;
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 8084461d3a..218dcd289d 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -107,7 +107,7 @@ void redraw_for_cursorline(win_T *wp)
&& !pum_visible()) {
if (wp->w_p_rnu) {
// win_line() will redraw the number column only.
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
}
if (win_cursorline_standout(wp)) {
if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) {
@@ -117,7 +117,7 @@ void redraw_for_cursorline(win_T *wp)
redrawWinline(wp, wp->w_last_cursorline);
redrawWinline(wp, wp->w_cursor.lnum);
} else {
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
}
}
@@ -172,7 +172,7 @@ void update_topline(void)
// If the buffer is empty, always set topline to 1.
if (BUFEMPTY()) { // special case - file is empty
if (curwin->w_topline != 1) {
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
curwin->w_topline = 1;
curwin->w_botline = 2;
@@ -326,12 +326,14 @@ void update_topline(void)
dollar_vcol = -1;
if (curwin->w_skipcol != 0) {
curwin->w_skipcol = 0;
- redraw_later(NOT_VALID);
- } else
- redraw_later(VALID);
- /* May need to set w_skipcol when cursor in w_topline. */
- if (curwin->w_cursor.lnum == curwin->w_topline)
+ redraw_later(curwin, NOT_VALID);
+ } else {
+ redraw_later(curwin, VALID);
+ }
+ // May need to set w_skipcol when cursor in w_topline.
+ if (curwin->w_cursor.lnum == curwin->w_topline) {
validate_cursor();
+ }
}
*so_ptr = save_so;
@@ -439,7 +441,7 @@ void changed_window_setting_win(win_T *wp)
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
/*
@@ -455,8 +457,8 @@ void set_topline(win_T *wp, linenr_T lnum)
wp->w_topline_was_set = true;
wp->w_topfill = 0;
wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
- /* Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked. */
- redraw_later(VALID);
+ // Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked.
+ redraw_later(wp, VALID);
}
/*
@@ -634,7 +636,7 @@ void validate_virtcol_win(win_T *wp)
if (wp->w_p_cuc
&& !pum_visible()
)
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
}
@@ -830,7 +832,7 @@ void curs_columns(
curwin->w_leftcol = new_leftcol;
win_check_anchored_floats(curwin);
// screen has to be redrawn with new curwin->w_leftcol
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
}
curwin->w_wcol -= curwin->w_leftcol;
@@ -934,13 +936,14 @@ void curs_columns(
} else {
curwin->w_skipcol = 0;
}
- if (prev_skipcol != curwin->w_skipcol)
- redraw_later(NOT_VALID);
+ if (prev_skipcol != curwin->w_skipcol) {
+ redraw_later(curwin, NOT_VALID);
+ }
/* Redraw when w_virtcol changes and 'cursorcolumn' is set */
if (curwin->w_p_cuc && (curwin->w_valid & VALID_VIRTCOL) == 0
&& !pum_visible()) {
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
}
// now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise
@@ -2023,7 +2026,7 @@ int onepage(Direction dir, long count)
}
}
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
return retval;
}
@@ -2209,7 +2212,7 @@ void halfpage(bool flag, linenr_T Prenum)
check_topfill(curwin, !flag);
cursor_correct();
beginline(BL_SOL | BL_FIX);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
}
void do_check_cursorbind(void)
@@ -2257,7 +2260,7 @@ void do_check_cursorbind(void)
}
// Correct cursor for multi-byte character.
mb_adjust_cursor();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
// Only scroll when 'scrollbind' hasn't done this.
if (!curwin->w_p_scb) {
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 92ca29209e..68ef4cd41e 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -262,11 +262,9 @@ static void parse_msgpack(Channel *channel)
call_set_error(channel, buf, ERROR_LOG_LEVEL);
}
msgpack_unpacked_destroy(&unpacked);
- // Bail out from this event loop iteration
- return;
+ } else {
+ handle_request(channel, &unpacked.data);
}
-
- handle_request(channel, &unpacked.data);
}
if (result == MSGPACK_UNPACK_NOMEM_ERROR) {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f291adaffe..b09b99cf35 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -903,6 +903,10 @@ normal_end:
msg_nowait = false;
+ if (finish_op) {
+ set_reg_var(get_default_register_name());
+ }
+
// Reset finish_op, in case it was set
s->c = finish_op;
finish_op = false;
@@ -3615,7 +3619,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
scrolldown(-y, false);
}
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
cursor_correct();
curwin->w_redr_status = true;
}
@@ -4158,7 +4162,7 @@ void scroll_redraw(int up, long count)
coladvance(curwin->w_curswant);
if (moved)
curwin->w_viewport_invalid = true;
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
}
/*
@@ -4256,7 +4260,7 @@ dozet:
FALLTHROUGH;
case 't': scroll_cursor_top(0, true);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
set_fraction(curwin);
break;
@@ -4265,7 +4269,7 @@ dozet:
FALLTHROUGH;
case 'z': scroll_cursor_halfway(true);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
set_fraction(curwin);
break;
@@ -4286,7 +4290,7 @@ dozet:
FALLTHROUGH;
case 'b': scroll_cursor_bot(0, true);
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
set_fraction(curwin);
break;
@@ -4333,7 +4337,7 @@ dozet:
col = 0;
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
}
break;
@@ -4352,7 +4356,7 @@ dozet:
}
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
}
break;
@@ -4705,7 +4709,7 @@ static void nv_clear(cmdarg_T *cap)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
wp->w_s->b_syn_slow = false;
}
- redraw_later(CLEAR);
+ redraw_later(curwin, CLEAR);
}
}
@@ -6856,7 +6860,7 @@ static void nv_g_cmd(cmdarg_T *cap)
} else {
if (cap->count1 > 1) {
// if it fails, let the cursor still move to the last char
- cursor_down(cap->count1 - 1, false);
+ (void)cursor_down(cap->count1 - 1, false);
}
i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
coladvance((colnr_T)i);
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 8329daf5f1..9e7d81fc82 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2652,7 +2652,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
xfree(reg->y_array);
}
if (curwin->w_p_rnu) {
- redraw_later(SOME_VALID); // cursor moved to start
+ redraw_later(curwin, SOME_VALID); // cursor moved to start
}
if (message) { // Display message about yank?
if (yank_type == kMTCharWise && yanklines == 1) {
@@ -3833,7 +3833,8 @@ int do_join(size_t count,
&& (!has_format_option(FO_MBYTE_JOIN)
|| (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
&& (!has_format_option(FO_MBYTE_JOIN2)
- || utf_ptr2char(curr) < 0x100 || endcurr1 < 0x100)
+ || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1))
+ || (endcurr1 < 0x100 && !utf_eat_space(utf_ptr2char(curr))))
) {
/* don't add a space if the line is ending in a space */
if (endcurr1 == ' ')
@@ -4158,49 +4159,41 @@ format_lines(
int avoid_fex /* don't use 'formatexpr' */
)
{
- int max_len;
- int is_not_par; /* current line not part of parag. */
- int next_is_not_par; /* next line not part of paragraph */
- int is_end_par; /* at end of paragraph */
- int prev_is_end_par = FALSE; /* prev. line not part of parag. */
- int next_is_start_par = FALSE;
- int leader_len = 0; /* leader len of current line */
- int next_leader_len; /* leader len of next line */
- char_u *leader_flags = NULL; /* flags for leader of current line */
- char_u *next_leader_flags; /* flags for leader of next line */
- int do_comments; /* format comments */
- int do_comments_list = 0; /* format comments with 'n' or '2' */
- int advance = TRUE;
- int second_indent = -1; /* indent for second line (comment
- * aware) */
- int do_second_indent;
- int do_number_indent;
- int do_trail_white;
- int first_par_line = TRUE;
+ bool is_not_par; // current line not part of parag.
+ bool next_is_not_par; // next line not part of paragraph
+ bool is_end_par; // at end of paragraph
+ bool prev_is_end_par = false; // prev. line not part of parag.
+ bool next_is_start_par = false;
+ int leader_len = 0; // leader len of current line
+ int next_leader_len; // leader len of next line
+ char_u *leader_flags = NULL; // flags for leader of current line
+ char_u *next_leader_flags; // flags for leader of next line
+ bool advance = true;
+ int second_indent = -1; // indent for second line (comment aware)
+ bool first_par_line = true;
int smd_save;
long count;
- int need_set_indent = TRUE; /* set indent of next paragraph */
- int force_format = FALSE;
- int old_State = State;
-
- /* length of a line to force formatting: 3 * 'tw' */
- max_len = comp_textwidth(TRUE) * 3;
-
- /* check for 'q', '2' and '1' in 'formatoptions' */
- do_comments = has_format_option(FO_Q_COMS);
- do_second_indent = has_format_option(FO_Q_SECOND);
- do_number_indent = has_format_option(FO_Q_NUMBER);
- do_trail_white = has_format_option(FO_WHITE_PAR);
-
- /*
- * Get info about the previous and current line.
- */
- if (curwin->w_cursor.lnum > 1)
- is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
- , &leader_len, &leader_flags, do_comments
- );
- else
- is_not_par = TRUE;
+ bool need_set_indent = true; // set indent of next paragraph
+ bool force_format = false;
+ const int old_State = State;
+
+ // length of a line to force formatting: 3 * 'tw'
+ const int max_len = comp_textwidth(true) * 3;
+
+ // check for 'q', '2' and '1' in 'formatoptions'
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
+ int do_comments_list = 0; // format comments with 'n' or '2'
+ const bool do_second_indent = has_format_option(FO_Q_SECOND);
+ const bool do_number_indent = has_format_option(FO_Q_NUMBER);
+ const bool do_trail_white = has_format_option(FO_WHITE_PAR);
+
+ // Get info about the previous and current line.
+ if (curwin->w_cursor.lnum > 1) {
+ is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
+ &leader_len, &leader_flags, do_comments);
+ } else {
+ is_not_par = true;
+ }
next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
, &next_leader_len, &next_leader_flags, do_comments
);
@@ -4225,7 +4218,7 @@ format_lines(
* The last line to be formatted.
*/
if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
- next_is_not_par = TRUE;
+ next_is_not_par = true;
next_leader_len = 0;
next_leader_flags = NULL;
} else {
@@ -4236,7 +4229,7 @@ format_lines(
next_is_start_par =
(get_number_indent(curwin->w_cursor.lnum + 1) > 0);
}
- advance = TRUE;
+ advance = true;
is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
if (!is_end_par && do_trail_white)
is_end_par = !ends_in_white(curwin->w_cursor.lnum);
@@ -4287,7 +4280,7 @@ format_lines(
leader_len, leader_flags,
next_leader_len, next_leader_flags)
)
- is_end_par = TRUE;
+ is_end_par = true;
/*
* If we have got to the end of a paragraph, or the line is
@@ -4324,9 +4317,9 @@ format_lines(
* end of the paragraph. */
if (line_count < 0)
break;
- first_par_line = TRUE;
+ first_par_line = true;
}
- force_format = FALSE;
+ force_format = false;
}
/*
@@ -4334,7 +4327,7 @@ format_lines(
* first delete the leader from the second line.
*/
if (!is_end_par) {
- advance = FALSE;
+ advance = false;
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
if (line_count < 0 && u_save_cursor() == FAIL)
@@ -4357,12 +4350,13 @@ format_lines(
beep_flush();
break;
}
- first_par_line = FALSE;
- /* If the line is getting long, format it next time */
- if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len)
- force_format = TRUE;
- else
- force_format = FALSE;
+ first_par_line = false;
+ // If the line is getting long, format it next time
+ if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
+ force_format = true;
+ } else {
+ force_format = false;
+ }
}
}
line_breakcheck();
@@ -4423,11 +4417,10 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
int paragraph_start(linenr_T lnum)
{
char_u *p;
- int leader_len = 0; /* leader len of current line */
- char_u *leader_flags = NULL; /* flags for leader of current line */
- int next_leader_len = 0; /* leader len of next line */
- char_u *next_leader_flags = NULL; /* flags for leader of next line */
- int do_comments; /* format comments */
+ int leader_len = 0; // leader len of current line
+ char_u *leader_flags = NULL; // flags for leader of current line
+ int next_leader_len = 0; // leader len of next line
+ char_u *next_leader_flags = NULL; // flags for leader of next line
if (lnum <= 1)
return TRUE; /* start of the file */
@@ -4436,7 +4429,7 @@ int paragraph_start(linenr_T lnum)
if (*p == NUL)
return TRUE; /* after empty line */
- do_comments = has_format_option(FO_Q_COMS);
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
return true; // after non-paragraph line
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 484d9da3a1..fcc051ef1a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -312,6 +312,9 @@ static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
"noinsert", "noselect", NULL };
+#ifdef BACKSLASH_IN_FILENAME
+static char *(p_csl_values[]) = { "slash", "backslash", NULL };
+#endif
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2",
"auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9",
@@ -2563,7 +2566,7 @@ static bool valid_spellfile(const char_u *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
for (const char_u *s = val; *s != NUL; s++) {
- if (!vim_isfilec(*s) && *s != ',') {
+ if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
return false;
}
}
@@ -3118,7 +3121,7 @@ ambw_end:
} else {
if (curwin->w_status_height) {
curwin->w_redr_status = true;
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
}
curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
redraw_titles();
@@ -3188,6 +3191,13 @@ ambw_end:
} else {
completeopt_was_set();
}
+#ifdef BACKSLASH_IN_FILENAME
+ } else if (gvarp == &p_csl) { // 'completeslash'
+ if (check_opt_strings(p_csl, p_csl_values, false) != OK
+ || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+#endif
} else if (varp == &curwin->w_p_scl) {
// 'signcolumn'
if (check_opt_strings(*varp, p_scl_values, false) != OK) {
@@ -3354,10 +3364,6 @@ ambw_end:
if (!parse_winhl_opt(curwin)) {
errmsg = e_invarg;
}
- } else if (varp == &p_rtp) { // 'runtimepath'
- if (!nlua_update_package_path()) {
- errmsg = (char_u *)N_("E970: Failed to initialize lua interpreter");
- }
} else {
// Options that are a list of flags.
p = NULL;
@@ -3741,11 +3747,10 @@ static char_u *set_chars_option(win_T *wp, char_u **varp, bool set)
/// Return error message or NULL.
char_u *check_stl_option(char_u *s)
{
- int itemcnt = 0;
int groupdepth = 0;
static char_u errbuf[80];
- while (*s && itemcnt < STL_MAX_ITEM) {
+ while (*s) {
// Check for valid keys after % sequences
while (*s && *s != '%') {
s++;
@@ -3754,9 +3759,6 @@ char_u *check_stl_option(char_u *s)
break;
}
s++;
- if (*s != '%' && *s != ')') {
- itemcnt++;
- }
if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) {
s++;
continue;
@@ -3798,9 +3800,6 @@ char_u *check_stl_option(char_u *s)
}
}
}
- if (itemcnt >= STL_MAX_ITEM) {
- return (char_u *)N_("E541: too many items");
- }
if (groupdepth != 0) {
return (char_u *)N_("E542: unbalanced groups");
}
@@ -4692,7 +4691,7 @@ static void check_redraw(uint32_t flags)
redraw_curbuf_later(NOT_VALID);
}
if (flags & P_RWINONLY) {
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
if (doclear) {
redraw_all_later(CLEAR);
@@ -5706,12 +5705,12 @@ void unset_global_local_option(char *name, void *from)
case PV_LCS:
clear_string_option(&((win_T *)from)->w_p_lcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true);
- redraw_win_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, NOT_VALID);
break;
case PV_FCS:
clear_string_option(&((win_T *)from)->w_p_fcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
- redraw_win_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, NOT_VALID);
break;
}
}
@@ -5866,6 +5865,9 @@ static char_u *get_varp(vimoption_T *p)
case PV_COM: return (char_u *)&(curbuf->b_p_com);
case PV_CMS: return (char_u *)&(curbuf->b_p_cms);
case PV_CPT: return (char_u *)&(curbuf->b_p_cpt);
+# ifdef BACKSLASH_IN_FILENAME
+ case PV_CSL: return (char_u *)&(curbuf->b_p_csl);
+# endif
case PV_CFU: return (char_u *)&(curbuf->b_p_cfu);
case PV_OFU: return (char_u *)&(curbuf->b_p_ofu);
case PV_EOL: return (char_u *)&(curbuf->b_p_eol);
@@ -6153,6 +6155,9 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_inf = p_inf;
buf->b_p_swf = cmdmod.noswapfile ? false : p_swf;
buf->b_p_cpt = vim_strsave(p_cpt);
+# ifdef BACKSLASH_IN_FILENAME
+ buf->b_p_csl = vim_strsave(p_csl);
+# endif
buf->b_p_cfu = vim_strsave(p_cfu);
buf->b_p_ofu = vim_strsave(p_ofu);
buf->b_p_tfu = vim_strsave(p_tfu);
@@ -6803,7 +6808,8 @@ static void langmap_set(void)
/// Return true if format option 'x' is in effect.
/// Take care of no formatting when 'paste' is set.
-int has_format_option(int x)
+bool has_format_option(int x)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (p_paste) {
return false;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index a09811c8fb..af0ea7f4a2 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -77,12 +77,13 @@
#define FO_ONE_LETTER '1'
#define FO_WHITE_PAR 'w' // trailing white space continues paragr.
#define FO_AUTO 'a' // automatic formatting
+#define FO_RIGOROUS_TW ']' // respect textwidth rigorously
#define FO_REMOVE_COMS 'j' // remove comment leaders when joining lines
#define FO_PERIOD_ABBR 'p' // don't break a single space after a period
#define DFLT_FO_VI "vt"
#define DFLT_FO_VIM "tcqj"
-#define FO_ALL "tcroq2vlb1mMBn,awjp" // for do_set()
+#define FO_ALL "tcroq2vlb1mMBn,aw]jp" // for do_set()
// characters for the p_cpo option:
#define CPO_ALTREAD 'a' // ":read" sets alternate file name
@@ -187,6 +188,7 @@ enum {
#define GO_ASELML 'A' // autoselect modeless selection
#define GO_BOT 'b' // use bottom scrollbar
#define GO_CONDIALOG 'c' // use console dialog
+#define GO_DARKTHEME 'd' // use dark theme variant
#define GO_TABLINE 'e' // may show tabline
#define GO_FORG 'f' // start GUI in foreground
#define GO_GREY 'g' // use grey menu items
@@ -204,7 +206,7 @@ enum {
#define GO_FOOTER 'F' // add footer
#define GO_VERTICAL 'v' // arrange dialog buttons vertically
#define GO_KEEPWINSIZE 'k' // keep GUI window size
-#define GO_ALL "aAbcefFghilmMprTvk" // all possible flags for 'go'
+#define GO_ALL "aAbcdefFghilmMprTvk" // all possible flags for 'go'
// flags for 'comments' option
#define COM_NEST 'n' // comments strings nest
@@ -372,6 +374,9 @@ EXTERN long p_columns; // 'columns'
EXTERN int p_confirm; // 'confirm'
EXTERN int p_cp; // 'compatible'
EXTERN char_u *p_cot; // 'completeopt'
+# ifdef BACKSLASH_IN_FILENAME
+EXTERN char_u *p_csl; // 'completeslash'
+# endif
EXTERN long p_pb; // 'pumblend'
EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
@@ -743,6 +748,7 @@ enum {
, BV_CPT
, BV_DICT
, BV_TSR
+ , BV_CSL
, BV_CFU
, BV_DEF
, BV_INC
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 02df0ab276..65ef7a4527 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -453,6 +453,15 @@ return {
defaults={if_true={vi="menu,preview"}}
},
{
+ full_name='completeslash', abbreviation='csl',
+ type='string', scope={'buffer'},
+ vi_def=true,
+ vim=true,
+ varname='p_csl',
+ enable_if='BACKSLASH_IN_FILENAME',
+ defaults={if_true={vi=""}}
+ },
+ {
full_name='confirm', abbreviation='cf',
type='bool', scope={'global'},
vi_def=true,
diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h
index c8ac4218f6..bd5f2b889d 100644
--- a/src/nvim/os/os_defs.h
+++ b/src/nvim/os/os_defs.h
@@ -16,10 +16,11 @@
#define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense.
-#if defined(PATH_MAX) && (PATH_MAX > 1024)
+# define DEFAULT_MAXPATHL 4096
+#if defined(PATH_MAX) && (PATH_MAX > DEFAULT_MAXPATHL)
# define MAXPATHL PATH_MAX
#else
-# define MAXPATHL 1024
+# define MAXPATHL DEFAULT_MAXPATHL
#endif
// Command-processing buffer. Use large buffers for all platforms.
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 3beada5bc9..ac0c1f6eb1 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -301,49 +301,49 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed,
if (pum_width < p_pw) {
pum_width = (int)p_pw;
}
- }
- } else if (((cursor_col > p_pw || cursor_col > max_width) && !pum_rl)
- || (pum_rl && (cursor_col < Columns - p_pw
- || cursor_col < Columns - max_width))) {
- // align pum edge with "cursor_col"
- if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) {
- pum_col = cursor_col + max_width + pum_scrollbar + 1;
- if (pum_col >= Columns) {
- pum_col = Columns - 1;
- }
- } else if (!pum_rl) {
- if (curwin->w_wincol > Columns - max_width - pum_scrollbar
- && max_width <= p_pw) {
- // use full width to end of the screen
- pum_col = cursor_col - max_width - pum_scrollbar;
- if (pum_col < 0) {
- pum_col = 0;
+ } else if (((cursor_col > p_pw || cursor_col > max_width) && !pum_rl)
+ || (pum_rl && (cursor_col < Columns - p_pw
+ || cursor_col < Columns - max_width))) {
+ // align pum edge with "cursor_col"
+ if (pum_rl && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) {
+ pum_col = cursor_col + max_width + pum_scrollbar + 1;
+ if (pum_col >= Columns) {
+ pum_col = Columns - 1;
+ }
+ } else if (!pum_rl) {
+ if (curwin->w_wincol > Columns - max_width - pum_scrollbar
+ && max_width <= p_pw) {
+ // use full width to end of the screen
+ pum_col = Columns - max_width - pum_scrollbar;
+ if (pum_col < 0) {
+ pum_col = 0;
+ }
}
}
- }
-
- if (pum_rl) {
- pum_width = pum_col - pum_scrollbar + 1;
- } else {
- pum_width = Columns - pum_col - pum_scrollbar;
- }
- if (pum_width < p_pw) {
- pum_width = (int)p_pw;
if (pum_rl) {
- if (pum_width > pum_col) {
- pum_width = pum_col;
- }
+ pum_width = pum_col - pum_scrollbar + 1;
} else {
- if (pum_width >= Columns - pum_col) {
- pum_width = Columns - pum_col - 1;
- }
+ pum_width = Columns - pum_col - pum_scrollbar;
}
- } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
- && pum_width > p_pw) {
- pum_width = max_width + pum_kind_width + pum_extra_width + 1;
+
if (pum_width < p_pw) {
pum_width = (int)p_pw;
+ if (pum_rl) {
+ if (pum_width > pum_col) {
+ pum_width = pum_col;
+ }
+ } else {
+ if (pum_width >= Columns - pum_col) {
+ pum_width = Columns - pum_col - 1;
+ }
+ }
+ } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
+ && pum_width > p_pw) {
+ pum_width = max_width + pum_kind_width + pum_extra_width + 1;
+ if (pum_width < p_pw) {
+ pum_width = (int)p_pw;
+ }
}
}
} else if (Columns < def_width) {
@@ -786,7 +786,7 @@ static int pum_set_selected(int n, int repeat)
// Return cursor to where we were
validate_cursor();
- redraw_later(SOME_VALID);
+ redraw_later(curwin, SOME_VALID);
// When the preview window was resized we need to
// update the view on the buffer. Only go back to
@@ -919,11 +919,11 @@ void pum_set_event_info(dict_T *dict)
r = (double)pum_row;
c = (double)pum_col;
}
- tv_dict_add_float(dict, S_LEN("height"), h);
- tv_dict_add_float(dict, S_LEN("width"), w);
- tv_dict_add_float(dict, S_LEN("row"), r);
- tv_dict_add_float(dict, S_LEN("col"), c);
- tv_dict_add_nr(dict, S_LEN("size"), pum_size);
- tv_dict_add_bool(dict, S_LEN("scrollbar"),
- pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
+ (void)tv_dict_add_float(dict, S_LEN("height"), h);
+ (void)tv_dict_add_float(dict, S_LEN("width"), w);
+ (void)tv_dict_add_float(dict, S_LEN("row"), r);
+ (void)tv_dict_add_float(dict, S_LEN("col"), c);
+ (void)tv_dict_add_nr(dict, S_LEN("size"), pum_size);
+ (void)tv_dict_add_bool(dict, S_LEN("scrollbar"),
+ pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 96c903d206..6ca97c3072 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3707,7 +3707,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum)
curwin->w_cursor.coladd = 0;
curwin->w_curswant = 0;
update_topline(); // scroll to show the line
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
curwin->w_redr_status = true; // update ruler
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -6317,6 +6317,7 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi)
// Populate the quickfix list with the items supplied in the list
// of dictionaries. "title" will be copied to w:quickfix_title
// "action" is 'a' for add, 'r' for replace. Otherwise create a new list.
+// When "what" is not NULL then only set some properties.
int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
dict_T *what)
{
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 9f450888a2..36a6ae59f8 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -25,7 +25,7 @@
//
// Commands that scroll a window change w_topline and must call
// check_cursor() to move the cursor into the visible part of the window, and
-// call redraw_later(VALID) to have the window displayed by update_screen()
+// call redraw_later(wp, VALID) to have the window displayed by update_screen()
// later.
//
// Commands that change text in the buffer must call changed_bytes() or
@@ -37,7 +37,7 @@
//
// Commands that change how a window is displayed (e.g., setting 'list') or
// invalidate the contents of a window in another way (e.g., change fold
-// settings), must call redraw_later(NOT_VALID) to have the whole window
+// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
// redisplayed by update_screen() later.
//
// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
@@ -45,11 +45,11 @@
// buffer redisplayed by update_screen() later.
//
// Commands that change highlighting and possibly cause a scroll too must call
-// redraw_later(SOME_VALID) to update the whole window but still use scrolling
-// to avoid redrawing everything. But the length of displayed lines must not
-// change, use NOT_VALID then.
+// redraw_later(wp, SOME_VALID) to update the whole window but still use
+// scrolling to avoid redrawing everything. But the length of displayed lines
+// must not change, use NOT_VALID then.
//
-// Commands that move the window position must call redraw_later(NOT_VALID).
+// Commands that move the window position must call redraw_later(wp, NOT_VALID).
// TODO(neovim): should minimize redrawing by scrolling when possible.
//
// Commands that change everything (e.g., resizing the screen) must call
@@ -176,7 +176,7 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref,
textlock--;
if (!ERROR_SET(&err)
- && api_is_truthy(ret, "provider %s retval", default_true, &err)) {
+ && api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
return true;
}
@@ -195,17 +195,11 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref,
return false;
}
-/*
- * Redraw the current window later, with update_screen(type).
- * Set must_redraw only if not already set to a higher value.
- * e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
- */
-void redraw_later(int type)
-{
- redraw_win_later(curwin, type);
-}
-
-void redraw_win_later(win_T *wp, int type)
+/// Redraw a window later, with update_screen(type).
+///
+/// Set must_redraw only if not already set to a higher value.
+/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
+void redraw_later(win_T *wp, int type)
FUNC_ATTR_NONNULL_ALL
{
if (!exiting && wp->w_redr_type < type) {
@@ -223,7 +217,7 @@ void redraw_win_later(win_T *wp, int type)
void redraw_all_later(int type)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_win_later(wp, type);
+ redraw_later(wp, type);
}
// This may be needed when switching tabs.
if (must_redraw < type) {
@@ -234,7 +228,7 @@ void redraw_all_later(int type)
void screen_invalidate_highlights(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
wp->w_grid.valid = false;
}
}
@@ -251,7 +245,7 @@ void redraw_buf_later(buf_T *buf, int type)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf) {
- redraw_win_later(wp, type);
+ redraw_later(wp, type);
}
}
}
@@ -277,7 +271,7 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
wp->w_redraw_bot = lastline;
}
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
}
}
}
@@ -305,7 +299,7 @@ redrawWinline(
if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
wp->w_redraw_bot = lnum;
}
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
}
}
@@ -359,7 +353,6 @@ int update_screen(int type)
/* Postpone the redrawing when it's not needed and when being called
* recursively. */
if (!redrawing() || updating_screen) {
- redraw_later(type); /* remember type for next time */
must_redraw = type;
if (type > INVERTED_ALL) {
curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
@@ -501,6 +494,12 @@ int update_screen(int type)
}
}
+ // "start" callback could have changed highlights for global elements
+ if (win_check_ns_hl(NULL)) {
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ }
+
if (clear_cmdline) /* going to clear cmdline (done below) */
check_for_delay(FALSE);
@@ -1331,6 +1330,8 @@ static void win_update(win_T *wp, Providers *providers)
}
}
+ win_check_ns_hl(wp);
+
for (;; ) {
/* stop updating when reached the end of the window (check for _past_
@@ -2185,6 +2186,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// return 'false' or error: skip rest of this window
kv_A(*providers, k) = NULL;
}
+
+ win_check_ns_hl(wp);
}
}
@@ -4649,8 +4652,8 @@ void status_redraw_all(void)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height) {
- wp->w_redr_status = TRUE;
- redraw_later(VALID);
+ wp->w_redr_status = true;
+ redraw_later(wp, VALID);
}
}
}
@@ -4667,7 +4670,7 @@ void status_redraw_buf(buf_T *buf)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_status_height != 0 && wp->w_buffer == buf) {
wp->w_redr_status = true;
- redraw_later(VALID);
+ redraw_later(wp, VALID);
}
}
}
@@ -5191,8 +5194,8 @@ win_redr_custom (
char_u buf[MAXPATHL];
char_u *stl;
char_u *p;
- struct stl_hlrec hltab[STL_MAX_ITEM];
- StlClickRecord tabtab[STL_MAX_ITEM];
+ stl_hlrec_t *hltab;
+ StlClickRecord *tabtab;
int use_sandbox = false;
win_T *ewp;
int p_crb_save;
@@ -5270,9 +5273,9 @@ win_redr_custom (
/* Make a copy, because the statusline may include a function call that
* might change the option value and free the memory. */
stl = vim_strsave(stl);
- width = build_stl_str_hl(ewp, buf, sizeof(buf),
- stl, use_sandbox,
- fillchar, maxwidth, hltab, tabtab);
+ width =
+ build_stl_str_hl(ewp, buf, sizeof(buf), stl, use_sandbox,
+ fillchar, maxwidth, &hltab, &tabtab);
xfree(stl);
ewp->w_p_crb = p_crb_save;
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 9458e6333f..b25333c9fa 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -4923,7 +4923,7 @@ search_line:
&& curwin != curwin_save && win_valid(curwin_save)) {
/* Return cursor to where we were */
validate_cursor();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
win_enter(curwin_save, true);
}
break;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1984a357c3..636c71657d 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -2258,7 +2258,7 @@ char_u *did_set_spelllang(win_T *wp)
theend:
xfree(spl_copy);
recursive = false;
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
return ret_msg;
}
@@ -6877,7 +6877,7 @@ void ex_spelldump(exarg_T *eap)
if (curbuf->b_ml.ml_line_count > 1) {
ml_delete(curbuf->b_ml.ml_line_count, false);
}
- redraw_later(NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
// Go through all possible words and:
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 034c580b3e..05667f060e 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -119,6 +119,7 @@ struct slang_S {
bool sl_add; // true if it's a .add file.
char_u *sl_fbyts; // case-folded word bytes
+ long sl_fbyts_len; // length of sl_fbyts
idx_T *sl_fidxs; // case-folded word indexes
char_u *sl_kbyts; // keep-case word bytes
idx_T *sl_kidxs; // keep-case word indexes
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 09d8646c6d..b415a4635b 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -764,20 +764,24 @@ truncerr:
}
// <LWORDTREE>
- res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fidxs, false, 0);
- if (res != 0)
+ res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fbyts_len,
+ &lp->sl_fidxs, false, 0);
+ if (res != 0) {
goto someerror;
+ }
// <KWORDTREE>
- res = spell_read_tree(fd, &lp->sl_kbyts, &lp->sl_kidxs, false, 0);
- if (res != 0)
+ res = spell_read_tree(fd, &lp->sl_kbyts, NULL, &lp->sl_kidxs, false, 0);
+ if (res != 0) {
goto someerror;
+ }
// <PREFIXTREE>
- res = spell_read_tree(fd, &lp->sl_pbyts, &lp->sl_pidxs, true,
- lp->sl_prefixcnt);
- if (res != 0)
+ res = spell_read_tree(fd, &lp->sl_pbyts, NULL, &lp->sl_pidxs, true,
+ lp->sl_prefixcnt);
+ if (res != 0) {
goto someerror;
+ }
// For a new file link it in the list of spell files.
if (old_lp == NULL && lang != NULL) {
@@ -920,8 +924,8 @@ void suggest_load_files(void)
// <SUGWORDTREE>: <wordtree>
// Read the trie with the soundfolded words.
- if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sidxs,
- false, 0) != 0) {
+ if (spell_read_tree(fd, &slang->sl_sbyts, NULL, &slang->sl_sidxs,
+ false, 0) != 0) {
someerror:
EMSG2(_("E782: error while reading .sug file: %s"),
slang->sl_fname);
@@ -1630,10 +1634,12 @@ static int
spell_read_tree (
FILE *fd,
char_u **bytsp,
+ long *bytsp_len,
idx_T **idxsp,
bool prefixtree, // true for the prefix tree
int prefixcnt // when "prefixtree" is true: prefix count
)
+ FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
int idx;
char_u *bp;
@@ -1653,6 +1659,9 @@ spell_read_tree (
// Allocate the byte array.
bp = xmalloc(len);
*bytsp = bp;
+ if (bytsp_len != NULL) {
+ *bytsp_len = len;
+ }
// Allocate the index array.
ip = xcalloc(len, sizeof(*ip));
@@ -4850,10 +4859,10 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
spin->si_blocks_cnt = 0;
// Skip over any other NUL bytes (same word with different
- // flags).
- while (byts[n + 1] == 0) {
- ++n;
- ++curi[depth];
+ // flags). But don't go over the end.
+ while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0) {
+ n++;
+ curi[depth]++;
}
} else {
// Normal char, go one level deeper.
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 9a9cc45c6b..ec6accd473 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -73,9 +73,9 @@ struct hl_group {
RgbValue sg_rgb_fg; ///< RGB foreground color
RgbValue sg_rgb_bg; ///< RGB background color
RgbValue sg_rgb_sp; ///< RGB special color
- uint8_t *sg_rgb_fg_name; ///< RGB foreground color name
- uint8_t *sg_rgb_bg_name; ///< RGB background color name
- uint8_t *sg_rgb_sp_name; ///< RGB special color name
+ char *sg_rgb_fg_name; ///< RGB foreground color name
+ char *sg_rgb_bg_name; ///< RGB background color name
+ char *sg_rgb_sp_name; ///< RGB special color name
int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
};
@@ -3147,7 +3147,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
}
// assume spell checking changed, force a redraw
- redraw_win_later(curwin, NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
/// Handle ":syntax iskeyword" command.
@@ -3187,7 +3187,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
curbuf->b_p_isk = save_isk;
}
}
- redraw_win_later(curwin, NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
}
/*
@@ -4297,7 +4297,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
curwin->w_s->b_syn_topgrp = sgl_id;
if (source ? do_source(eap->arg, false, DOSO_NONE) == FAIL
- : source_runtime(eap->arg, DIP_ALL) == FAIL) {
+ : source_in_path(p_rtp, eap->arg, DIP_ALL) == FAIL) {
EMSG2(_(e_notopen), eap->arg);
}
curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
@@ -6899,7 +6899,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
}
} else if (strcmp(key, "GUIFG") == 0) {
- char_u **const namep = &HL_TABLE()[idx].sg_rgb_fg_name;
+ char **namep = &HL_TABLE()[idx].sg_rgb_fg_name;
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init) {
@@ -6909,8 +6909,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (*namep == NULL || STRCMP(*namep, arg) != 0) {
xfree(*namep);
if (strcmp(arg, "NONE") != 0) {
- *namep = (char_u *)xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_fg = name_to_color((char_u *)arg);
+ *namep = xstrdup(arg);
+ HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg);
} else {
*namep = NULL;
HL_TABLE()[idx].sg_rgb_fg = -1;
@@ -6923,7 +6923,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
normal_fg = HL_TABLE()[idx].sg_rgb_fg;
}
} else if (STRCMP(key, "GUIBG") == 0) {
- char_u **const namep = &HL_TABLE()[idx].sg_rgb_bg_name;
+ char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name;
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init) {
@@ -6933,8 +6933,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (*namep == NULL || STRCMP(*namep, arg) != 0) {
xfree(*namep);
if (STRCMP(arg, "NONE") != 0) {
- *namep = (char_u *)xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_bg = name_to_color((char_u *)arg);
+ *namep = xstrdup(arg);
+ HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg);
} else {
*namep = NULL;
HL_TABLE()[idx].sg_rgb_bg = -1;
@@ -6947,7 +6947,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
normal_bg = HL_TABLE()[idx].sg_rgb_bg;
}
} else if (strcmp(key, "GUISP") == 0) {
- char_u **const namep = &HL_TABLE()[idx].sg_rgb_sp_name;
+ char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name;
if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) {
if (!init) {
@@ -6957,8 +6957,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (*namep == NULL || STRCMP(*namep, arg) != 0) {
xfree(*namep);
if (strcmp(arg, "NONE") != 0) {
- *namep = (char_u *)xstrdup(arg);
- HL_TABLE()[idx].sg_rgb_sp = name_to_color((char_u *)arg);
+ *namep = xstrdup(arg);
+ HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg);
} else {
*namep = NULL;
HL_TABLE()[idx].sg_rgb_sp = -1;
@@ -7152,13 +7152,32 @@ static void highlight_list_one(const int id)
msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
}
- if (!didh)
- highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
+ if (!didh) {
+ highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", "");
+ }
if (p_verbose > 0) {
last_set_msg(sgp->sg_script_ctx);
}
}
+Dictionary get_global_hl_defs(void)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+ for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
+ Dictionary attrs = ARRAY_DICT_INIT;
+ struct hl_group *h = &HL_TABLE()[i - 1];
+ if (h->sg_attr > 0) {
+ attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true);
+ } else if (h->sg_link > 0) {
+ const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name;
+ PUT(attrs, "link", STRING_OBJ(cstr_to_string(link)));
+ }
+ PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs));
+ }
+
+ return rv;
+}
+
/// Outputs a highlight when doing ":hi MyHighlight"
///
/// @param type one of \ref LIST_XXX
@@ -7166,15 +7185,15 @@ static void highlight_list_one(const int id)
/// @param sarg string used if \p type == LIST_STRING
static bool highlight_list_arg(
const int id, bool didh, const int type, int iarg,
- char_u *const sarg, const char *const name)
+ char *const sarg, const char *const name)
{
- char_u buf[100];
+ char buf[100];
if (got_int) {
return false;
}
if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) {
- char_u *ts = buf;
+ char *ts = buf;
if (type == LIST_INT) {
snprintf((char *)buf, sizeof(buf), "%d", iarg - 1);
} else if (type == LIST_STRING) {
@@ -7191,15 +7210,15 @@ static bool highlight_list_arg(
}
}
- (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id,
- false);
+ (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name)
+ + 1), id, false);
didh = true;
if (!got_int) {
if (*name != NUL) {
MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
}
- msg_outtrans(ts);
+ msg_outtrans((char_u *)ts);
}
}
return didh;
@@ -7378,7 +7397,7 @@ static void set_hl_attr(int idx)
at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1;
at_en.hl_blend = sgp->sg_blend;
- sgp->sg_attr = hl_get_syn_attr(idx+1, at_en);
+ sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en);
// a cursor style uses this syn_id, make sure its attribute is updated.
if (cursor_mode_uses_syn_id(idx+1)) {
@@ -7541,11 +7560,17 @@ int syn_id2attr(int hl_id)
struct hl_group *sgp;
hl_id = syn_get_final_id(hl_id);
- sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
+ int attr = ns_get_hl(-1, hl_id, false);
+ if (attr >= 0) {
+ return attr;
+ }
+ sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
return sgp->sg_attr;
}
+
+
/*
* Translate a group ID to the final group ID (following links).
*/
@@ -7562,9 +7587,22 @@ int syn_get_final_id(int hl_id)
* Look out for loops! Break after 100 links.
*/
for (count = 100; --count >= 0; ) {
- sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
- if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
+ // ACHTUNG: when using "tmp" attribute (no link) the function might be
+ // called twice. it needs be smart enough to remember attr only to
+ // syn_id2attr time
+ int check = ns_get_hl(-1, hl_id, true);
+ if (check == 0) {
+ return 0; // how dare! it broke the link!
+ } else if (check > 0) {
+ hl_id = check;
+ continue;
+ }
+
+
+ sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
+ if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
break;
+ }
hl_id = sgp->sg_link;
}
@@ -8494,7 +8532,7 @@ color_name_table_T color_name_table[] = {
///
/// @param[in] name string value to convert to RGB
/// return the hex value or -1 if could not find a correct value
-RgbValue name_to_color(const char_u *name)
+RgbValue name_to_color(const char *name)
{
if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2])
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 81d1ef4c9f..c6b1a0d04c 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2903,7 +2903,7 @@ static int jumpto_tag(
&& curwin != curwin_save && win_valid(curwin_save)) {
/* Return cursor to where we were */
validate_cursor();
- redraw_later(VALID);
+ redraw_later(curwin, VALID);
win_enter(curwin_save, true);
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 52d3eef810..39e2ca6171 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -233,7 +233,7 @@ Terminal *terminal_open(TerminalOptions opts)
snprintf(var, sizeof(var), "terminal_color_%d", i);
char *name = get_config_string(var);
if (name) {
- color_val = name_to_color((uint8_t *)name);
+ color_val = name_to_color(name);
xfree(name);
if (color_val != -1) {
@@ -1060,7 +1060,7 @@ static bool send_mouse_event(Terminal *term, int c)
curwin->w_redr_status = true;
curwin = save_curwin;
curbuf = curwin->w_buffer;
- redraw_win_later(mouse_win, NOT_VALID);
+ redraw_later(mouse_win, NOT_VALID);
invalidate_terminal(term, -1, -1);
// Only need to exit focus if the scrolled window is the terminal window
return mouse_win == curwin;
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 094bb3ebd1..04a678eeb8 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1897,4 +1897,17 @@ func Test_autocmd_FileReadCmd()
delfunc ReadFileCmd
endfunc
+" Tests for SigUSR1 autocmd event, which is only available on posix systems.
+func Test_autocmd_sigusr1()
+ CheckUnix
+
+ let g:sigusr1_passed = 0
+ au Signal SIGUSR1 let g:sigusr1_passed = 1
+ call system('/bin/kill -s usr1 ' . getpid())
+ call WaitForAssert({-> assert_true(g:sigusr1_passed)})
+
+ au! Signal
+ unlet g:sigusr1_passed
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim
index debc9da46d..b6c2d1467e 100644
--- a/src/nvim/testdir/test_cindent.vim
+++ b/src/nvim/testdir/test_cindent.vim
@@ -127,4 +127,40 @@ func Test_cindent_case()
bwipe!
endfunc
+func Test_cindent_pragma()
+ new
+ setl cindent ts=4 sw=4
+ setl cino=Ps
+
+ let code =<< trim [CODE]
+ {
+ #pragma omp parallel
+ {
+ #pragma omp task
+ foo();
+ # pragma omp taskwait
+ }
+ }
+ [CODE]
+
+ call append(0, code)
+ normal gg
+ normal =G
+
+ let expected =<< trim [CODE]
+ {
+ #pragma omp parallel
+ {
+ #pragma omp task
+ foo();
+ # pragma omp taskwait
+ }
+ }
+
+ [CODE]
+
+ call assert_equal(expected, getline(1, '$'))
+ enew! | close
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cjk_linebreak.vim b/src/nvim/testdir/test_cjk_linebreak.vim
new file mode 100644
index 0000000000..dfaa8fa1af
--- /dev/null
+++ b/src/nvim/testdir/test_cjk_linebreak.vim
@@ -0,0 +1,97 @@
+scriptencoding utf-8
+
+func Run_cjk_linebreak_after(rigorous)
+ set textwidth=12
+ for punct in [
+ \ '!', '%', ')', ',', ':', ';', '>', '?', ']', '}', '’', '”', '†', '‡',
+ \ '…', '‰', '‱', '‼', '⁇', '⁈', '⁉', '℃', '℉', '、', '。', '〉', '》',
+ \ '」', '』', '】', '〕', '〗', '〙', '〛', '!', ')', ',', '.', ':',
+ \ ';', '?', ']', '}']
+ call setline('.', '这是一个测试' .. punct.'试试 CJK 行禁则补丁。')
+ normal gqq
+ if a:rigorous
+ call assert_equal('这是一个测', getline(1))
+ else
+ call assert_equal('这是一个测试' .. punct, getline(1))
+ endif
+ %d_
+ endfor
+endfunc
+
+func Test_cjk_linebreak_after()
+ set formatoptions=croqn2mB1j
+ call Run_cjk_linebreak_after(0)
+endfunc
+
+func Test_cjk_linebreak_after_rigorous()
+ set formatoptions=croqn2mB1j]
+ call Run_cjk_linebreak_after(1)
+endfunc
+
+func Run_cjk_linebreak_before()
+ set textwidth=12
+ for punct in [
+ \ '(', '<', '[', '`', '{', '‘', '“', '〈', '《', '「', '『', '【', '〔',
+ \ '〖', '〘', '〚', '(', '[', '{']
+ call setline('.', '这是个测试' .. punct.'试试 CJK 行禁则补丁。')
+ normal gqq
+ call assert_equal('这是个测试', getline(1))
+ %d_
+ endfor
+endfunc
+
+func Test_cjk_linebreak_before()
+ set formatoptions=croqn2mB1j
+ call Run_cjk_linebreak_before()
+endfunc
+
+func Test_cjk_linebreak_before_rigorous()
+ set formatoptions=croqn2mB1j]
+ call Run_cjk_linebreak_before()
+endfunc
+
+func Run_cjk_linebreak_nobetween(rigorous)
+ " …… must not start a line
+ call setline('.', '这是个测试……试试 CJK 行禁则补丁。')
+ set textwidth=12 ambiwidth=double
+ normal gqq
+ if a:rigorous
+ call assert_equal('这是个测', getline(1))
+ else
+ call assert_equal('这是个测试……', getline(1))
+ endif
+ %d_
+
+ call setline('.', '这是一个测试……试试 CJK 行禁则补丁。')
+ set textwidth=12 ambiwidth=double
+ normal gqq
+ call assert_equal('这是一个测', getline(1))
+ %d_
+
+ " but —— can
+ call setline('.', '这是个测试——试试 CJK 行禁则补丁。')
+ set textwidth=12 ambiwidth=double
+ normal gqq
+ call assert_equal('这是个测试', getline(1))
+endfunc
+
+func Test_cjk_linebreak_nobetween()
+ set formatoptions=croqn2mB1j
+ call Run_cjk_linebreak_nobetween(0)
+endfunc
+
+func Test_cjk_linebreak_nobetween_rigorous()
+ set formatoptions=croqn2mB1j]
+ call Run_cjk_linebreak_nobetween(1)
+endfunc
+
+func Test_cjk_linebreak_join_punct()
+ for punct in ['——', '〗', ',', '。', '……']
+ call setline(1, '文本文本' .. punct)
+ call setline(2, 'English')
+ set formatoptions=croqn2mB1j
+ normal ggJ
+ call assert_equal('文本文本' .. punct.'English', getline(1))
+ %d_
+ endfor
+endfunc
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 871143699a..e3c42a4fe3 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -722,7 +722,7 @@ func Test_verbosefile()
endfunc
func Test_verbose_option()
- " See test/functional/ui/cmdline_spec.lua
+ " See test/functional/legacy/cmdline_spec.lua
CheckScreendump
let lines =<< trim [SCRIPT]
@@ -842,6 +842,25 @@ func Test_buffers_lastused()
bwipeout bufc
endfunc
+func Test_cmdlineclear_tabenter()
+ " See test/functional/legacy/cmdline_spec.lua
+ CheckScreendump
+
+ let lines =<< trim [SCRIPT]
+ call setline(1, range(30))
+ [SCRIPT]
+
+ call writefile(lines, 'XtestCmdlineClearTabenter')
+ let buf = RunVimInTerminal('-S XtestCmdlineClearTabenter', #{rows: 10})
+ call term_wait(buf, 50)
+ " in one tab make the command line higher with CTRL-W -
+ call term_sendkeys(buf, ":tabnew\<cr>\<C-w>-\<C-w>-gtgt")
+ call VerifyScreenDump(buf, 'Test_cmdlineclear_tabenter', {})
+
+ call StopVimInTerminal(buf)
+ call delete('XtestCmdlineClearTabenter')
+endfunc
+
" test that ";" works to find a match at the start of the first line
func Test_zero_line_search()
new
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index caebc341e0..abad6983dc 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1441,31 +1441,40 @@ endfunc
func Test_edit_InsertLeave()
new
+ au InsertLeavePre * let g:did_au_pre = 1
au InsertLeave * let g:did_au = 1
+ let g:did_au_pre = 0
let g:did_au = 0
call feedkeys("afoo\<Esc>", 'tx')
+ call assert_equal(1, g:did_au_pre)
call assert_equal(1, g:did_au)
call assert_equal('foo', getline(1))
+ let g:did_au_pre = 0
let g:did_au = 0
call feedkeys("Sbar\<C-C>", 'tx')
+ call assert_equal(1, g:did_au_pre)
call assert_equal(0, g:did_au)
call assert_equal('bar', getline(1))
inoremap x xx<Esc>
+ let g:did_au_pre = 0
let g:did_au = 0
call feedkeys("Saax", 'tx')
+ call assert_equal(1, g:did_au_pre)
call assert_equal(1, g:did_au)
call assert_equal('aaxx', getline(1))
inoremap x xx<C-C>
+ let g:did_au_pre = 0
let g:did_au = 0
call feedkeys("Sbbx", 'tx')
+ call assert_equal(1, g:did_au_pre)
call assert_equal(0, g:did_au)
call assert_equal('bbxx', getline(1))
bwipe!
- au! InsertLeave
+ au! InsertLeave InsertLeavePre
iunmap x
endfunc
@@ -1578,4 +1587,11 @@ func Test_edit_browse()
bwipe!
endfunc
+func Test_read_invalid()
+ " set encoding=latin1
+ " This was not properly checking for going past the end.
+ call assert_fails('r`=', 'E484')
+ set encoding=utf-8
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index c7ca682c8c..9f79c1b545 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -532,6 +532,7 @@ let s:filename_case_checks = {
\ }
func CheckItems(checks)
+ set noswapfile
for [ft, names] in items(a:checks)
for i in range(0, len(names) - 1)
new
@@ -548,6 +549,7 @@ func CheckItems(checks)
bwipe!
endfor
endfor
+ set swapfile&
endfunc
func Test_filetype_detection()
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 1339a9f25d..57a0a7aaf4 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -327,7 +327,10 @@ func Test_compl_in_cmdwin()
set wildmenu wildchar=<Tab>
com! -nargs=1 -complete=command GetInput let input = <q-args>
com! -buffer TestCommand echo 'TestCommand'
+ let w:test_winvar = 'winvar'
+ let b:test_bufvar = 'bufvar'
+ " User-defined commands
let input = ''
call feedkeys("q:iGetInput T\<C-x>\<C-v>\<CR>", 'tx!')
call assert_equal('TestCommand', input)
@@ -336,11 +339,94 @@ func Test_compl_in_cmdwin()
call feedkeys("q::GetInput T\<Tab>\<CR>:q\<CR>", 'tx!')
call assert_equal('T', input)
+ com! -nargs=1 -complete=var GetInput let input = <q-args>
+ " Window-local variables
+ let input = ''
+ call feedkeys("q:iGetInput w:test_\<C-x>\<C-v>\<CR>", 'tx!')
+ call assert_equal('w:test_winvar', input)
+
+ let input = ''
+ call feedkeys("q::GetInput w:test_\<Tab>\<CR>:q\<CR>", 'tx!')
+ call assert_equal('w:test_', input)
+
+ " Buffer-local variables
+ let input = ''
+ call feedkeys("q:iGetInput b:test_\<C-x>\<C-v>\<CR>", 'tx!')
+ call assert_equal('b:test_bufvar', input)
+
+ let input = ''
+ call feedkeys("q::GetInput b:test_\<Tab>\<CR>:q\<CR>", 'tx!')
+ call assert_equal('b:test_', input)
+
delcom TestCommand
delcom GetInput
+ unlet w:test_winvar
+ unlet b:test_bufvar
set wildmenu& wildchar&
endfunc
+" Test for insert path completion with completeslash option
+func Test_ins_completeslash()
+ CheckMSWindows
+
+ call mkdir('Xdir')
+
+ let orig_shellslash = &shellslash
+ set cpt&
+
+ new
+
+ set noshellslash
+
+ set completeslash=
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir\', getline('.'))
+
+ set completeslash=backslash
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir\', getline('.'))
+
+ set completeslash=slash
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir/', getline('.'))
+
+ set shellslash
+
+ set completeslash=
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir/', getline('.'))
+
+ set completeslash=backslash
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir\', getline('.'))
+
+ set completeslash=slash
+ exe "normal oXd\<C-X>\<C-F>"
+ call assert_equal('Xdir/', getline('.'))
+ %bw!
+ call delete('Xdir', 'rf')
+
+ set noshellslash
+ set completeslash=slash
+ call assert_true(stridx(globpath(&rtp, 'syntax/*.vim', 1, 1)[0], '\') != -1)
+
+ let &shellslash = orig_shellslash
+ set completeslash=
+endfunc
+
+func Test_issue_7021()
+ CheckMSWindows
+
+ let orig_shellslash = &shellslash
+ set noshellslash
+
+ set completeslash=slash
+ call assert_false(expand('~') =~ '/')
+
+ let &shellslash = orig_shellslash
+ set completeslash=
+endfunc
+
func Test_pum_with_folds_two_tabs()
CheckScreendump
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 31a8b48d37..8e2a987e74 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -199,9 +199,9 @@ func Test_dict_big()
try
let n = d[1500]
catch
- let str=substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '')
+ let str = substitute(v:exception, '\v(.{14}).*( "\d{4}").*', '\1\2', '')
endtry
- call assert_equal('Vim(let):E716: 1500', str)
+ call assert_equal('Vim(let):E716: "1500"', str)
" lookup each items
for i in range(1500)
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index 7fbf04311d..30239a90c2 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -1,20 +1,16 @@
" Tests for :messages, :echomsg, :echoerr
-function Test_messages()
+source shared.vim
+
+func Test_messages()
let oldmore = &more
try
set nomore
- " Avoid the "message maintainer" line.
- let $LANG = ''
- let $LC_ALL = ''
- let $LC_MESSAGES = ''
- let $LC_COLLATE = ''
let arr = map(range(10), '"hello" . v:val')
for s in arr
echomsg s | redraw
endfor
- let result = ''
" get last two messages
redir => result
@@ -25,22 +21,17 @@ function Test_messages()
" clear messages without last one
1messages clear
- redir => result
- redraw | messages
- redir END
- let msg_list = split(result, "\n")
+ let msg_list = GetMessages()
call assert_equal(['hello9'], msg_list)
" clear all messages
messages clear
- redir => result
- redraw | messages
- redir END
- call assert_equal('', result)
+ let msg_list = GetMessages()
+ call assert_equal([], msg_list)
finally
let &more = oldmore
endtry
-endfunction
+endfunc
" Patch 7.4.1696 defined the "clearmode()" command for clearing the mode
" indicator (e.g., "-- INSERT --") when ":stopinsert" is invoked. Message
@@ -74,6 +65,7 @@ func Test_echomsg()
call assert_equal("\n12345", execute(':echomsg 12345'))
call assert_equal("\n[]", execute(':echomsg []'))
call assert_equal("\n[1, 2, 3]", execute(':echomsg [1, 2, 3]'))
+ call assert_equal("\n[1, 2, []]", execute(':echomsg [1, 2, v:_null_list]'))
call assert_equal("\n{}", execute(':echomsg {}'))
call assert_equal("\n{'a': 1, 'b': 2}", execute(':echomsg {"a": 1, "b": 2}'))
if has('float')
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 3243edbf55..215065f941 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -470,4 +470,17 @@ func Test_mkvimrc()
call delete('Xtestvimrc')
endfunc
+func Test_scrolloff()
+ set sessionoptions+=localoptions
+ setlocal so=1 siso=1
+ mksession! Xtest_mks.out
+ setlocal so=-1 siso=-1
+ source Xtest_mks.out
+ call assert_equal(1, &l:so)
+ call assert_equal(1, &l:siso)
+ call delete('Xtest_mks.out')
+ setlocal so& siso&
+ set sessionoptions&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index 9e8da74db7..10e16f4198 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -267,7 +267,6 @@ func Test_set_errors()
call assert_fails('set commentstring=x', 'E537:')
call assert_fails('set complete=x', 'E539:')
call assert_fails('set statusline=%{', 'E540:')
- call assert_fails('set statusline=' . repeat("%p", 81), 'E541:')
call assert_fails('set statusline=%(', 'E542:')
if has('cursorshape')
" This invalid value for 'guicursor' used to cause Vim to crash.
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index cf0af07528..049163890e 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -3602,6 +3602,10 @@ func Test_lhelpgrep_autocmd()
au BufEnter * call setqflist([], 'f')
augroup END
call assert_fails('helpgrep quickfix', 'E925:')
+ " run the test with a help window already open
+ help
+ wincmd w
+ call assert_fails('helpgrep quickfix', 'E925:')
augroup QF_Test
au! BufEnter
augroup END
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index d20f8d1eef..19a7c6c9d0 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -167,6 +167,75 @@ func Test_set_register()
enew!
endfunc
+func Test_v_register()
+ enew
+ call setline(1, 'nothing')
+
+ func s:Put()
+ let s:register = v:register
+ exec 'normal! "' .. v:register .. 'P'
+ endfunc
+ nnoremap <buffer> <plug>(test) :<c-u>call s:Put()<cr>
+ nmap <buffer> S <plug>(test)
+
+ let @z = "testz\n"
+ let @" = "test@\n"
+
+ let s:register = ''
+ call feedkeys('"_ddS', 'mx')
+ call assert_equal('test@', getline('.')) " fails before 8.2.0929
+ call assert_equal('"', s:register) " fails before 8.2.0929
+
+ let s:register = ''
+ call feedkeys('"zS', 'mx')
+ call assert_equal('z', s:register)
+
+ let s:register = ''
+ call feedkeys('"zSS', 'mx')
+ call assert_equal('"', s:register)
+
+ let s:register = ''
+ call feedkeys('"_S', 'mx')
+ call assert_equal('_', s:register)
+
+ let s:register = ''
+ normal "_ddS
+ call assert_equal('"', s:register) " fails before 8.2.0929
+ call assert_equal('test@', getline('.')) " fails before 8.2.0929
+
+ let s:register = ''
+ execute 'normal "z:call' "s:Put()\n"
+ call assert_equal('z', s:register)
+ call assert_equal('testz', getline('.'))
+
+ " Test operator and omap
+ let @b = 'testb'
+ func s:OpFunc(...)
+ let s:register2 = v:register
+ endfunc
+ set opfunc=s:OpFunc
+
+ normal "bg@l
+ normal S
+ call assert_equal('"', s:register) " fails before 8.2.0929
+ call assert_equal('b', s:register2)
+
+ func s:Motion()
+ let s:register1 = v:register
+ normal! l
+ endfunc
+ onoremap <buffer> Q :<c-u>call s:Motion()<cr>
+
+ normal "bg@Q
+ normal S
+ call assert_equal('"', s:register)
+ call assert_equal('b', s:register1)
+ call assert_equal('"', s:register2)
+
+ set opfunc&
+ bwipe!
+endfunc
+
func Test_ve_blockpaste()
new
set ve=all
diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim
index 77a42e0a8a..9c74c35049 100644
--- a/src/nvim/testdir/test_ruby.vim
+++ b/src/nvim/testdir/test_ruby.vim
@@ -1,15 +1,7 @@
" Tests for ruby interface
-if !has('ruby')
- finish
-end
-
-" Helper function as there is no builtin rubyeval() function similar
-" to perleval, luaevel() or pyeval().
-func RubyEval(ruby_expr)
- let s = split(execute('ruby print ' . a:ruby_expr), "\n")
- return (len(s) == 0) ? '' : s[-1]
-endfunc
+source check.vim
+CheckFeature ruby
func Test_ruby_change_buffer()
call setline(line('$'), ['1 line 1'])
@@ -43,19 +35,19 @@ func Test_rubyfile()
call delete(tempfile)
endfunc
-func Test_set_cursor()
+func Test_ruby_set_cursor()
" Check that setting the cursor position works.
new
call setline(1, ['first line', 'second line'])
normal gg
rubydo $curwin.cursor = [1, 5]
call assert_equal([1, 6], [line('.'), col('.')])
- call assert_equal('[1, 5]', RubyEval('$curwin.cursor'))
+ call assert_equal([1, 5], rubyeval('$curwin.cursor'))
" Check that movement after setting cursor position keeps current column.
normal j
call assert_equal([2, 6], [line('.'), col('.')])
- call assert_equal('[2, 5]', RubyEval('$curwin.cursor'))
+ call assert_equal([2, 5], rubyeval('$curwin.cursor'))
" call assert_fails('ruby $curwin.cursor = [1]',
" \ 'ArgumentError: array length must be 2')
@@ -63,34 +55,34 @@ func Test_set_cursor()
endfunc
" Test buffer.count and buffer.length (number of lines in buffer)
-func Test_buffer_count()
+func Test_ruby_buffer_count()
new
call setline(1, ['one', 'two', 'three'])
- call assert_equal('3', RubyEval('$curbuf.count'))
- call assert_equal('3', RubyEval('$curbuf.length'))
+ call assert_equal(3, rubyeval('$curbuf.count'))
+ call assert_equal(3, rubyeval('$curbuf.length'))
bwipe!
endfunc
" Test buffer.name (buffer name)
-func Test_buffer_name()
+func Test_ruby_buffer_name()
new Xfoo
- call assert_equal(expand('%:p'), RubyEval('$curbuf.name'))
+ call assert_equal(expand('%:p'), rubyeval('$curbuf.name'))
bwipe
- call assert_equal('', RubyEval('$curbuf.name'))
+ call assert_equal('', rubyeval('$curbuf.name'))
endfunc
" Test buffer.number (number of the buffer).
-func Test_buffer_number()
+func Test_ruby_buffer_number()
new
- call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number'))
+ call assert_equal(bufnr('%'), rubyeval('$curbuf.number'))
new
- call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number'))
+ call assert_equal(bufnr('%'), rubyeval('$curbuf.number'))
%bwipe
endfunc
" Test buffer.delete({n}) (delete line {n})
-func Test_buffer_delete()
+func Test_ruby_buffer_delete()
new
call setline(1, ['one', 'two', 'three'])
ruby $curbuf.delete(2)
@@ -104,7 +96,7 @@ func Test_buffer_delete()
endfunc
" Test buffer.append({str}, str) (append line {str} after line {n})
-func Test_buffer_append()
+func Test_ruby_buffer_append()
new
ruby $curbuf.append(0, 'one')
ruby $curbuf.append(1, 'three')
@@ -124,11 +116,11 @@ func Test_buffer_append()
endfunc
" Test buffer.line (get or set the current line)
-func Test_buffer_line()
+func Test_ruby_buffer_line()
new
call setline(1, ['one', 'two', 'three'])
2
- call assert_equal('two', RubyEval('$curbuf.line'))
+ call assert_equal('two', rubyeval('$curbuf.line'))
ruby $curbuf.line = 'TWO'
call assert_equal(['one', 'TWO', 'three'], getline(1, '$'))
@@ -137,20 +129,20 @@ func Test_buffer_line()
endfunc
" Test buffer.line_number (get current line number)
-func Test_buffer_line_number()
+func Test_ruby_buffer_line_number()
new
call setline(1, ['one', 'two', 'three'])
2
- call assert_equal('2', RubyEval('$curbuf.line_number'))
+ call assert_equal(2, rubyeval('$curbuf.line_number'))
bwipe!
endfunc
-func Test_buffer_get()
+func Test_ruby_buffer_get()
new
call setline(1, ['one', 'two'])
- call assert_equal('one', RubyEval('$curbuf[1]'))
- call assert_equal('two', RubyEval('$curbuf[2]'))
+ call assert_equal('one', rubyeval('$curbuf[1]'))
+ call assert_equal('two', rubyeval('$curbuf[2]'))
" call assert_fails('ruby $curbuf[0]',
" \ 'IndexError: line number 0 out of range')
@@ -160,7 +152,7 @@ func Test_buffer_get()
bwipe!
endfunc
-func Test_buffer_set()
+func Test_ruby_buffer_set()
new
call setline(1, ['one', 'two'])
ruby $curbuf[2] = 'TWO'
@@ -176,7 +168,7 @@ func Test_buffer_set()
endfunc
" Test window.width (get or set window height).
-func Test_window_height()
+func Test_ruby_window_height()
new
" Test setting window height
@@ -184,13 +176,13 @@ func Test_window_height()
call assert_equal(2, winheight(0))
" Test getting window height
- call assert_equal('2', RubyEval('$curwin.height'))
+ call assert_equal(2, rubyeval('$curwin.height'))
bwipe
endfunc
" Test window.width (get or set window width).
-func Test_window_width()
+func Test_ruby_window_width()
vnew
" Test setting window width
@@ -198,13 +190,13 @@ func Test_window_width()
call assert_equal(2, winwidth(0))
" Test getting window width
- call assert_equal('2', RubyEval('$curwin.width'))
+ call assert_equal(2, rubyeval('$curwin.width'))
bwipe
endfunc
" Test window.buffer (get buffer object of a window object).
-func Test_window_buffer()
+func Test_ruby_window_buffer()
new Xfoo1
new Xfoo2
ruby $b2 = $curwin.buffer
@@ -213,69 +205,69 @@ func Test_window_buffer()
ruby $b1 = $curwin.buffer
ruby $w1 = $curwin
- " call assert_equal(RubyEval('$b1'), RubyEval('$w1.buffer'))
- " call assert_equal(RubyEval('$b2'), RubyEval('$w2.buffer'))
- call assert_equal(string(bufnr('Xfoo1')), RubyEval('$w1.buffer.number'))
- call assert_equal(string(bufnr('Xfoo2')), RubyEval('$w2.buffer.number'))
+ " call assert_equal(rubyeval('$b1'), rubyeval('$w1.buffer'))
+ " call assert_equal(rubyeval('$b2'), rubyeval('$w2.buffer'))
+ call assert_equal(bufnr('Xfoo1'), rubyeval('$w1.buffer.number'))
+ call assert_equal(bufnr('Xfoo2'), rubyeval('$w2.buffer.number'))
ruby $b1, $w1, $b2, $w2 = nil
%bwipe
endfunc
" Test Vim::Window.current (get current window object)
-func Test_Vim_window_current()
- let cw = RubyEval('$curwin')
- " call assert_equal(cw, RubyEval('Vim::Window.current'))
+func Test_ruby_Vim_window_current()
+ let cw = rubyeval('$curwin.to_s')
+ " call assert_equal(cw, rubyeval('Vim::Window.current'))
call assert_match('^#<Neovim::Window:0x\x\+>$', cw)
endfunc
" Test Vim::Window.count (number of windows)
-func Test_Vim_window_count()
+func Test_ruby_Vim_window_count()
new Xfoo1
new Xfoo2
split
- call assert_equal('4', RubyEval('Vim::Window.count'))
+ call assert_equal(4, rubyeval('Vim::Window.count'))
%bwipe
- call assert_equal('1', RubyEval('Vim::Window.count'))
+ call assert_equal(1, rubyeval('Vim::Window.count'))
endfunc
" Test Vim::Window[n] (get window object of window n)
-func Test_Vim_window_get()
+func Test_ruby_Vim_window_get()
new Xfoo1
new Xfoo2
- call assert_match('Xfoo2$', RubyEval('Vim::Window[0].buffer.name'))
+ call assert_match('Xfoo2$', rubyeval('Vim::Window[0].buffer.name'))
wincmd j
- call assert_match('Xfoo1$', RubyEval('Vim::Window[1].buffer.name'))
+ call assert_match('Xfoo1$', rubyeval('Vim::Window[1].buffer.name'))
wincmd j
- call assert_equal('', RubyEval('Vim::Window[2].buffer.name'))
+ call assert_equal('', rubyeval('Vim::Window[2].buffer.name'))
%bwipe
endfunc
" Test Vim::Buffer.current (return the buffer object of current buffer)
-func Test_Vim_buffer_current()
- let cb = RubyEval('$curbuf')
- " call assert_equal(cb, RubyEval('Vim::Buffer.current'))
+func Test_ruby_Vim_buffer_current()
+ let cb = rubyeval('$curbuf.to_s')
+ " call assert_equal(cb, rubyeval('Vim::Buffer.current'))
call assert_match('^#<Neovim::Buffer:0x\x\+>$', cb)
endfunc
" Test Vim::Buffer:.count (return the number of buffers)
-func Test_Vim_buffer_count()
+func Test_ruby_Vim_buffer_count()
new Xfoo1
new Xfoo2
- call assert_equal('3', RubyEval('Vim::Buffer.count'))
+ call assert_equal(3, rubyeval('Vim::Buffer.count'))
%bwipe
- call assert_equal('1', RubyEval('Vim::Buffer.count'))
+ call assert_equal(1, rubyeval('Vim::Buffer.count'))
endfunc
" Test Vim::buffer[n] (return the buffer object of buffer number n)
-func Test_Vim_buffer_get()
+func Test_ruby_Vim_buffer_get()
new Xfoo1
new Xfoo2
" Index of Vim::Buffer[n] goes from 0 to the number of buffers.
- call assert_equal('', RubyEval('Vim::Buffer[0].name'))
- call assert_match('Xfoo1$', RubyEval('Vim::Buffer[1].name'))
- call assert_match('Xfoo2$', RubyEval('Vim::Buffer[2].name'))
+ call assert_equal('', rubyeval('Vim::Buffer[0].name'))
+ call assert_match('Xfoo1$', rubyeval('Vim::Buffer[1].name'))
+ call assert_match('Xfoo2$', rubyeval('Vim::Buffer[2].name'))
call assert_fails('ruby print Vim::Buffer[3].name',
\ "NoMethodError: undefined method `name' for nil:NilClass")
%bwipe
@@ -283,7 +275,7 @@ endfunc
" Test Vim::command({cmd}) (execute a Ex command))
" Test Vim::command({cmd})
-func Test_Vim_command()
+func Test_ruby_Vim_command()
new
call setline(1, ['one', 'two', 'three', 'four'])
ruby Vim::command('2,3d')
@@ -292,7 +284,7 @@ func Test_Vim_command()
endfunc
" Test Vim::set_option (set a vim option)
-func Test_Vim_set_option()
+func Test_ruby_Vim_set_option()
call assert_equal(0, &number)
ruby Vim::set_option('number')
call assert_equal(1, &number)
@@ -300,37 +292,39 @@ func Test_Vim_set_option()
call assert_equal(0, &number)
endfunc
-func Test_Vim_evaluate()
- call assert_equal('123', RubyEval('Vim::evaluate("123")'))
+func Test_ruby_Vim_evaluate()
+ call assert_equal(123, rubyeval('Vim::evaluate("123")'))
" Vim::evaluate("123").class gives Integer or Fixnum depending
" on versions of Ruby.
- call assert_match('^Integer\|Fixnum$', RubyEval('Vim::evaluate("123").class'))
+ call assert_match('^Integer\|Fixnum$', rubyeval('Vim::evaluate("123").class'))
- call assert_equal('1.23', RubyEval('Vim::evaluate("1.23")'))
- call assert_equal('Float', RubyEval('Vim::evaluate("1.23").class'))
+ if has('float')
+ call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")'))
+ call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class'))
+ endif
- call assert_equal('foo', RubyEval('Vim::evaluate("\"foo\"")'))
- call assert_equal('String', RubyEval('Vim::evaluate("\"foo\"").class'))
+ call assert_equal('foo', rubyeval('Vim::evaluate("\"foo\"")'))
+ call assert_equal('String', rubyeval('Vim::evaluate("\"foo\"").class'))
- call assert_equal('[1, 2]', RubyEval('Vim::evaluate("[1, 2]")'))
- call assert_equal('Array', RubyEval('Vim::evaluate("[1, 2]").class'))
+ call assert_equal([1, 2], rubyeval('Vim::evaluate("[1, 2]")'))
+ call assert_equal('Array', rubyeval('Vim::evaluate("[1, 2]").class'))
- call assert_equal('{"1"=>2}', RubyEval('Vim::evaluate("{1:2}")'))
- call assert_equal('Hash', RubyEval('Vim::evaluate("{1:2}").class'))
+ call assert_equal({'1': 2}, rubyeval('Vim::evaluate("{1:2}")'))
+ call assert_equal('Hash', rubyeval('Vim::evaluate("{1:2}").class'))
- call assert_equal('', RubyEval('Vim::evaluate("v:null")'))
- call assert_equal('NilClass', RubyEval('Vim::evaluate("v:null").class'))
+ call assert_equal(v:null, rubyeval('Vim::evaluate("v:null")'))
+ call assert_equal('NilClass', rubyeval('Vim::evaluate("v:null").class'))
- " call assert_equal('', RubyEval('Vim::evaluate("v:none")'))
- " call assert_equal('NilClass', RubyEval('Vim::evaluate("v:none").class'))
+ " call assert_equal(v:null, rubyeval('Vim::evaluate("v:none")'))
+ " call assert_equal('NilClass', rubyeval('Vim::evaluate("v:none").class'))
- call assert_equal('true', RubyEval('Vim::evaluate("v:true")'))
- call assert_equal('TrueClass', RubyEval('Vim::evaluate("v:true").class'))
- call assert_equal('false', RubyEval('Vim::evaluate("v:false")'))
- call assert_equal('FalseClass',RubyEval('Vim::evaluate("v:false").class'))
+ call assert_equal(v:true, rubyeval('Vim::evaluate("v:true")'))
+ call assert_equal('TrueClass', rubyeval('Vim::evaluate("v:true").class'))
+ call assert_equal(v:false, rubyeval('Vim::evaluate("v:false")'))
+ call assert_equal('FalseClass',rubyeval('Vim::evaluate("v:false").class'))
endfunc
-func Test_Vim_evaluate_list()
+func Test_ruby_Vim_evaluate_list()
call setline(line('$'), ['2 line 2'])
ruby Vim.command("normal /^2\n")
let l = ["abc", "def"]
@@ -344,7 +338,7 @@ EOF
call assert_equal('abc/def', getline('$'))
endfunc
-func Test_Vim_evaluate_dict()
+func Test_ruby_Vim_evaluate_dict()
let d = {'a': 'foo', 'b': 123}
redir => l:out
ruby d = Vim.evaluate("d"); print d
@@ -353,34 +347,47 @@ func Test_Vim_evaluate_dict()
endfunc
" Test Vim::message({msg}) (display message {msg})
-func Test_Vim_message()
+func Test_ruby_Vim_message()
throw 'skipped: TODO: '
ruby Vim::message('A message')
let messages = split(execute('message'), "\n")
call assert_equal('A message', messages[-1])
endfunc
-func Test_print()
- ruby print "Hello World!"
- let messages = split(execute('message'), "\n")
- call assert_equal('Hello World!', messages[-1])
+func Test_ruby_print()
+ func RubyPrint(expr)
+ return trim(execute('ruby print ' . a:expr))
+ endfunc
+
+ call assert_equal('123', RubyPrint('123'))
+ call assert_equal('1.23', RubyPrint('1.23'))
+ call assert_equal('Hello World!', RubyPrint('"Hello World!"'))
+ call assert_equal('[1, 2]', RubyPrint('[1, 2]'))
+ call assert_equal('{"k1"=>"v1", "k2"=>"v2"}', RubyPrint('({"k1" => "v1", "k2" => "v2"})'))
+ call assert_equal('true', RubyPrint('true'))
+ call assert_equal('false', RubyPrint('false'))
+ call assert_equal('', RubyPrint('nil'))
+ call assert_match('Vim', RubyPrint('Vim'))
+ call assert_match('Module', RubyPrint('Vim.class'))
+
+ delfunc RubyPrint
endfunc
-func Test_p()
+func Test_ruby_p()
ruby p 'Just a test'
- let messages = split(execute('message'), "\n")
+ let messages = GetMessages()
call assert_equal('"Just a test"', messages[-1])
" Check return values of p method
- call assert_equal('123', RubyEval('p(123)'))
- call assert_equal('[1, 2, 3]', RubyEval('p(1, 2, 3)'))
+ call assert_equal(123, rubyeval('p(123)'))
+ call assert_equal([1, 2, 3], rubyeval('p(1, 2, 3)'))
" Avoid the "message maintainer" line.
let $LANG = ''
messages clear
- call assert_equal('true', RubyEval('p() == nil'))
+ call assert_equal(v:true, rubyeval('p() == nil'))
- let messages = split(execute('message'), "\n")
+ let messages = GetMessages()
call assert_equal(0, len(messages))
endfunc
diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim
index 7efd181d04..4e38f7ebd8 100644
--- a/src/nvim/testdir/test_statusline.vim
+++ b/src/nvim/testdir/test_statusline.vim
@@ -354,6 +354,21 @@ func Test_statusline()
delfunc GetNested
delfunc GetStatusLine
+ " Test statusline works with 80+ items
+ function! StatusLabel()
+ redrawstatus
+ return '[label]'
+ endfunc
+ let statusline = '%{StatusLabel()}'
+ for i in range(150)
+ let statusline .= '%#TabLine' . (i % 2 == 0 ? 'Fill' : 'Sel') . '#' . string(i)[0]
+ endfor
+ let &statusline = statusline
+ redrawstatus
+ set statusline&
+ delfunc StatusLabel
+
+
" Check statusline in current and non-current window
" with the 'fillchars' option.
set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:-
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 2ef9bf5a2e..62d7dc8b18 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -316,7 +316,13 @@ static void terminfo_start(UI *ui)
#ifdef WIN32
uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW);
#else
- uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ int retry_count = 10;
+ // A signal may cause uv_tty_set_mode() to fail (e.g., SIGCONT). Retry a
+ // few times. #12322
+ while (uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO) == UV_EINTR
+ && retry_count > 0) {
+ retry_count--;
+ }
#endif
} else {
uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
@@ -1102,6 +1108,15 @@ static void tui_set_mode(UI *ui, ModeShape mode)
static void tui_mode_change(UI *ui, String mode, Integer mode_idx)
{
TUIData *data = ui->data;
+#ifdef UNIX
+ // If stdin is not a TTY, the LHS of pipe may change the state of the TTY
+ // after calling uv_tty_set_mode. So, set the mode of the TTY again here.
+ // #13073
+ if (data->is_starting && data->input.in_fd == STDERR_FILENO) {
+ uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_NORMAL);
+ uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO);
+ }
+#endif
tui_set_mode(ui, (ModeShape)mode_idx);
data->is_starting = false; // mode entered, no longer starting
data->showing_mode = (ModeShape)mode_idx;
diff --git a/src/nvim/types.h b/src/nvim/types.h
index a3d87f35ca..17f7e16740 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -21,7 +21,7 @@ typedef int handle_T;
// absent callback etc.
typedef int LuaRef;
-typedef uint64_t NS;
+typedef handle_T NS;
typedef struct expand expand_T;
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index e582d8f859..325ad31eaf 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -26,6 +26,7 @@
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/lua/executor.h"
#include "nvim/os/os.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 6c5a6cdb46..a8b8f7aa50 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2455,7 +2455,7 @@ static void u_undo_end(
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_cole > 0) {
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
}
}
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 832703e83d..873bef32d3 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -259,7 +259,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
#define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno))
#define SHOWCMD_COLS 10 // columns needed by shown command
-#define STL_MAX_ITEM 80 // max nr of %<flag> in statusline
#include "nvim/path.h"
diff --git a/src/nvim/window.c b/src/nvim/window.c
index bbc039d151..47b6b7e713 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -606,7 +606,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
win_config_float(wp, fconfig);
wp->w_pos_changed = true;
- redraw_win_later(wp, VALID);
+ redraw_later(wp, VALID);
return wp;
}
@@ -679,7 +679,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
wp->w_pos_changed = true;
if (change_external) {
wp->w_hl_needs_update = true;
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
}
@@ -764,7 +764,7 @@ static void ui_ext_win_position(win_T *wp)
wp->w_grid.focusable = wp->w_float_config.focusable;
if (!valid) {
wp->w_grid.valid = false;
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
}
} else {
@@ -1492,8 +1492,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// Both windows need redrawing. Update all status lines, in case they
// show something related to the window count or position.
- redraw_win_later(wp, NOT_VALID);
- redraw_win_later(oldwin, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
+ redraw_later(oldwin, NOT_VALID);
status_redraw_all();
if (need_status) {
@@ -1823,8 +1823,8 @@ static void win_exchange(long Prenum)
(void)win_comp_pos(); /* recompute window positions */
win_enter(wp, true);
- redraw_later(NOT_VALID);
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(curwin, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
// rotate windows: if upwards true the second window becomes the first one
@@ -1996,8 +1996,8 @@ void win_move_after(win_T *win1, win_T *win2)
win_append(win2, win1);
frame_append(win2->w_frame, win1->w_frame);
- (void)win_comp_pos(); /* recompute w_winrow for all windows */
- redraw_later(NOT_VALID);
+ (void)win_comp_pos(); // recompute w_winrow for all windows
+ redraw_later(curwin, NOT_VALID);
}
win_enter(win1, false);
@@ -3635,7 +3635,7 @@ void curwin_init(void)
void win_init_empty(win_T *wp)
{
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
@@ -4049,7 +4049,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
prevwin = next_prevwin;
last_status(false); // status line may appear or disappear
- (void)win_comp_pos(); // recompute w_winrow for all windows
+ const int row = win_comp_pos(); // recompute w_winrow for all windows
diff_need_scrollbind = true;
/* The tabpage line may have appeared or disappeared, may need to resize
@@ -4060,11 +4060,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
clear_cmdline = true;
}
p_ch = curtab->tp_ch_used;
- if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow
- ))
+
+ // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
+ // changed but p_ch and tp_ch_used are not changed. Thus we also need to
+ // check cmdline_row.
+ if ((row < cmdline_row) && (cmdline_row <= Rows - p_ch)) {
+ clear_cmdline = true;
+ }
+
+ if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) {
shell_new_rows();
- if (curtab->tp_old_Columns != Columns && starting == 0)
- shell_new_columns(); /* update window widths */
+ }
+ if (curtab->tp_old_Columns != Columns && starting == 0) {
+ shell_new_columns(); // update window widths
+ }
lastused_tabpage = old_curtab;
@@ -4587,10 +4596,11 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
}
maketitle();
- curwin->w_redr_status = TRUE;
- redraw_tabline = TRUE;
- if (restart_edit)
- redraw_later(VALID); /* causes status line redraw */
+ curwin->w_redr_status = true;
+ redraw_tabline = true;
+ if (restart_edit) {
+ redraw_later(curwin, VALID); // causes status line redraw
+ }
if (HL_ATTR(HLF_INACTIVE)
|| (prevwin && prevwin->w_hl_ids[HLF_INACTIVE])
@@ -5080,7 +5090,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
/* position changed, redraw */
wp->w_winrow = *row;
wp->w_wincol = *col;
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
@@ -5133,7 +5143,7 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) {
win->w_float_config.height = height;
win_config_float(win, win->w_float_config);
- redraw_win_later(win, NOT_VALID);
+ redraw_later(win, NOT_VALID);
} else {
frame_setheight(win->w_frame, height + win->w_status_height);
@@ -5336,7 +5346,7 @@ void win_setwidth_win(int width, win_T *wp)
if (wp->w_floating) {
wp->w_float_config.width = width;
win_config_float(wp, wp->w_float_config);
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
} else {
frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
@@ -5869,8 +5879,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
win_comp_scroll(wp);
- redraw_win_later(wp, SOME_VALID);
- wp->w_redr_status = TRUE;
+ redraw_later(wp, SOME_VALID);
+ wp->w_redr_status = true;
invalidate_botline_win(wp);
}
@@ -5909,7 +5919,7 @@ void win_set_inner_size(win_T *wp)
if (!exiting) {
scroll_to_fraction(wp, prev_height);
}
- redraw_win_later(wp, NOT_VALID); // SOME_VALID??
+ redraw_later(wp, NOT_VALID); // SOME_VALID??
}
if (width != wp->w_width_inner) {
@@ -5921,7 +5931,7 @@ void win_set_inner_size(win_T *wp)
update_topline();
curs_columns(true); // validate w_wrow
}
- redraw_win_later(wp, NOT_VALID);
+ redraw_later(wp, NOT_VALID);
}
if (wp->w_buffer->terminal) {
@@ -6765,7 +6775,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
prev->next = m;
m->next = cur;
- redraw_win_later(wp, rtype);
+ redraw_later(wp, rtype);
return id;
fail:
@@ -6823,7 +6833,7 @@ int match_delete(win_T *wp, int id, int perr)
rtype = VALID;
}
xfree(cur);
- redraw_win_later(wp, rtype);
+ redraw_later(wp, rtype);
return 0;
}
@@ -6841,7 +6851,7 @@ void clear_matches(win_T *wp)
xfree(wp->w_match_head);
wp->w_match_head = m;
}
- redraw_win_later(wp, SOME_VALID);
+ redraw_later(wp, SOME_VALID);
}
/*