aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cjson/lua_cjson.c2
-rwxr-xr-xsrc/clint.py4
-rw-r--r--src/nvim/CMakeLists.txt11
-rw-r--r--src/nvim/api/autocmd.c178
-rw-r--r--src/nvim/api/extmark.c56
-rw-r--r--src/nvim/api/keysets.lua12
-rw-r--r--src/nvim/api/private/helpers.c36
-rw-r--r--src/nvim/autocmd.c59
-rw-r--r--src/nvim/buffer.c111
-rw-r--r--src/nvim/buffer_defs.h10
-rw-r--r--src/nvim/charset.c2
-rw-r--r--src/nvim/debugger.c2
-rw-r--r--src/nvim/decoration.c173
-rw-r--r--src/nvim/decoration.h19
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/eval.c8
-rw-r--r--src/nvim/eval/funcs.c25
-rw-r--r--src/nvim/eval/userfunc.c8
-rw-r--r--src/nvim/ex_cmds2.c7
-rw-r--r--src/nvim/ex_docmd.c6
-rw-r--r--src/nvim/ex_eval.c4
-rw-r--r--src/nvim/extmark.c11
-rw-r--r--src/nvim/fileio.c6
-rw-r--r--src/nvim/getchar.c19
-rw-r--r--src/nvim/hardcopy.c3
-rw-r--r--src/nvim/hardcopy.h3
-rw-r--r--src/nvim/highlight.c22
-rw-r--r--src/nvim/highlight_defs.h4
-rw-r--r--src/nvim/lua/executor.c57
-rw-r--r--src/nvim/lua/vim.lua666
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/map.c1
-rw-r--r--src/nvim/map.h1
-rw-r--r--src/nvim/marktree.c4
-rw-r--r--src/nvim/marktree.h5
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/ops.c103
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/regexp.c27
-rw-r--r--src/nvim/screen.c14
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/sign.c18
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--src/nvim/spell.c2
-rw-r--r--src/nvim/strings.c2
-rw-r--r--src/nvim/syntax.c10
-rw-r--r--src/nvim/testdir/test_autochdir.vim61
-rw-r--r--src/nvim/testdir/test_diffmode.vim26
-rw-r--r--src/nvim/testdir/test_registers.vim161
-rw-r--r--src/nvim/tui/tui.c35
-rw-r--r--src/nvim/ui.c4
-rw-r--r--src/nvim/window.c3
-rw-r--r--src/nvim/window.h25
53 files changed, 1108 insertions, 938 deletions
diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c
index b5f97bc485..c243f93c05 100644
--- a/src/cjson/lua_cjson.c
+++ b/src/cjson/lua_cjson.c
@@ -1110,7 +1110,7 @@ static int json_is_invalid_number(json_parse_t *json)
/* Reject numbers starting with 0x, or leading zeros */
if (*p == '0') {
- int ch2 = *(p + 1);
+ char ch2 = *(p + 1);
if ((ch2 | 0x20) == 'x' || /* Hex */
('0' <= ch2 && ch2 <= '9')) /* Leading zero */
diff --git a/src/clint.py b/src/clint.py
index 4b7bf002e6..37b44920af 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -191,7 +191,6 @@ _ERROR_CATEGORIES = [
'readability/fn_size',
'readability/multiline_comment',
'readability/multiline_string',
- 'readability/nolint',
'readability/nul',
'readability/todo',
'readability/utf8',
@@ -298,9 +297,6 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error):
if category in _ERROR_CATEGORIES:
_error_suppressions.setdefault(
category, set()).add(linenum)
- else:
- error(filename, linenum, 'readability/nolint', 5,
- 'Unknown NOLINT error category: %s' % category)
def ParseKnownErrorSuppressions(filename, raw_lines, linenum):
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 21150965f9..9f29cae29d 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -57,13 +57,13 @@ set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua)
set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode)
set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h)
set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h)
-set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua)
+set(LUA_EDITOR_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_editor.lua)
set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua)
set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua)
set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua)
set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua)
set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua)
-set(LUA_LOAD_PACKAGE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_load_package.lua)
+set(LUA_INIT_PACKAGES_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_init_packages.lua)
set(LUA_KEYMAP_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/keymap.lua)
set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua)
set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json)
@@ -332,9 +332,9 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env
"LUAC_PRG=${LUAC_PRG}"
${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE}
- ${LUA_LOAD_PACKAGE_MODULE_SOURCE} "vim._load_package"
+ ${LUA_INIT_PACKAGES_MODULE_SOURCE} "vim._init_packages"
${LUA_INSPECT_MODULE_SOURCE} "vim.inspect"
- ${LUA_VIM_MODULE_SOURCE} "vim"
+ ${LUA_EDITOR_MODULE_SOURCE} "vim._editor"
${LUA_SHARED_MODULE_SOURCE} "vim.shared"
${LUA_F_MODULE_SOURCE} "vim.F"
${LUA_META_MODULE_SOURCE} "vim._meta"
@@ -342,7 +342,8 @@ add_custom_command(
${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap"
DEPENDS
${CHAR_BLOB_GENERATOR}
- ${LUA_VIM_MODULE_SOURCE}
+ ${LUA_INIT_PACKAGES_MODULE_SOURCE}
+ ${LUA_EDITOR_MODULE_SOURCE}
${LUA_SHARED_MODULE_SOURCE}
${LUA_INSPECT_MODULE_SOURCE}
${LUA_F_MODULE_SOURCE}
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 0ad7b320d0..5ede0e5265 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -40,8 +40,10 @@ static int64_t next_autocmd_id = 1;
///
/// @param opts Optional Parameters:
/// - event : Name or list of name of events to match against
-/// - group (string): Name of group to match against
-/// - pattern: Pattern or list of patterns to match against
+/// - group (string|int): Name or id of group to match against
+/// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer}
+/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// |autocmd-buflocal|. Cannot be used with {pattern}
///
/// @return A list of autocmds that match
Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
@@ -53,24 +55,34 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
char_u *pattern_filters[AUCMD_MAX_PATTERNS];
char_u pattern_buflocal[BUFLOCAL_PAT_LEN];
+ Array buffers = ARRAY_DICT_INIT;
+
bool event_set[NUM_EVENTS] = { false };
bool check_event = false;
int group = 0;
- if (opts->group.type != kObjectTypeNil) {
- Object v = opts->group;
- if (v.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation, "group must be a string.");
- goto cleanup;
- }
-
- group = augroup_find(v.data.string.data);
-
- if (group < 0) {
- api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
+ switch (opts->group.type) {
+ case kObjectTypeNil:
+ break;
+ case kObjectTypeString:
+ group = augroup_find(opts->group.data.string.data);
+ if (group < 0) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
+ goto cleanup;
+ }
+ break;
+ case kObjectTypeInteger:
+ group = (int)opts->group.data.integer;
+ char *name = augroup_name(group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
+ goto cleanup;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "group must be a string or an integer.");
goto cleanup;
- }
}
if (opts->event.type != kObjectTypeNil) {
@@ -100,6 +112,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
}
+ if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Cannot use both 'pattern' and 'buffer'");
+ goto cleanup;
+ }
+
int pattern_filter_count = 0;
if (opts->pattern.type != kObjectTypeNil) {
Object v = opts->pattern;
@@ -107,25 +125,70 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
pattern_filters[pattern_filter_count] = (char_u *)v.data.string.data;
pattern_filter_count += 1;
} else if (v.type == kObjectTypeArray) {
+ if (v.data.array.size > AUCMD_MAX_PATTERNS) {
+ api_set_error(err, kErrorTypeValidation,
+ "Too many patterns. Please limit yourself to %d or fewer",
+ AUCMD_MAX_PATTERNS);
+ goto cleanup;
+ }
+
FOREACH_ITEM(v.data.array, item, {
+ if (item.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string");
+ goto cleanup;
+ }
+
pattern_filters[pattern_filter_count] = (char_u *)item.data.string.data;
pattern_filter_count += 1;
});
} else {
- api_set_error(err,
- kErrorTypeValidation,
+ api_set_error(err, kErrorTypeValidation,
"Not a valid 'pattern' value. Must be a string or an array");
goto cleanup;
}
+ }
+
+ if (opts->buffer.type == kObjectTypeInteger || opts->buffer.type == kObjectTypeBuffer) {
+ buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
- if (pattern_filter_count >= AUCMD_MAX_PATTERNS) {
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ } else if (opts->buffer.type == kObjectTypeArray) {
+ if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) {
api_set_error(err,
kErrorTypeValidation,
- "Too many patterns. Please limit yourself to less");
+ "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS);
goto cleanup;
}
+
+ FOREACH_ITEM(opts->buffer.data.array, bufnr, {
+ if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer");
+ goto cleanup;
+ }
+
+ buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err);
+ if (ERROR_SET(err)) {
+ goto cleanup;
+ }
+
+ snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
+ ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal));
+ });
+ } else if (opts->buffer.type != kObjectTypeNil) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value for 'buffer': must be an integer or array of integers");
+ goto cleanup;
}
+ FOREACH_ITEM(buffers, bufnr, {
+ pattern_filters[pattern_filter_count] = (char_u *)bufnr.data.string.data;
+ pattern_filter_count += 1;
+ });
+
FOR_ALL_AUEVENTS(event) {
if (check_event && !event_set[event]) {
continue;
@@ -234,6 +297,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
cleanup:
+ api_free_array(buffers);
return autocmd_list;
}
@@ -265,7 +329,7 @@ cleanup:
/// - buffer: (bufnr)
/// - create a |autocmd-buflocal| autocmd.
/// - NOTE: Cannot be used with {pattern}
-/// - group: (string) The augroup name
+/// - group: (string|int) The augroup name or id
/// - once: (boolean) - See |autocmd-once|
/// - nested: (boolean) - See |autocmd-nested|
/// - desc: (string) - Description of the autocmd
@@ -350,23 +414,29 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
bool is_once = api_object_to_bool(opts->once, "once", false, err);
bool is_nested = api_object_to_bool(opts->nested, "nested", false, err);
- // TODO(tjdevries): accept number for namespace instead
- if (opts->group.type != kObjectTypeNil) {
- Object *v = &opts->group;
- if (v->type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation, "'group' must be a string");
- goto cleanup;
- }
-
- au_group = augroup_find(v->data.string.data);
-
- if (au_group == AUGROUP_ERROR) {
- api_set_error(err,
- kErrorTypeException,
- "invalid augroup: %s", v->data.string.data);
-
+ switch (opts->group.type) {
+ case kObjectTypeNil:
+ break;
+ case kObjectTypeString:
+ au_group = augroup_find(opts->group.data.string.data);
+ if (au_group == AUGROUP_ERROR) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "invalid augroup: %s", opts->group.data.string.data);
+ goto cleanup;
+ }
+ break;
+ case kObjectTypeInteger:
+ au_group = (int)opts->group.data.integer;
+ char *name = augroup_name(au_group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
+ goto cleanup;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
goto cleanup;
- }
}
if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
@@ -568,7 +638,7 @@ void nvim_del_augroup_by_name(String name)
/// - NOTE: Cannot be used with {pattern}
/// - pattern (string|table) - optional, defaults to "*".
/// - NOTE: Cannot be used with {buffer}
-/// - group (string) - autocmd group name
+/// - group (string|int) - autocmd group name or id
/// - modeline (boolean) - Default true, see |<nomodeline>|
void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err)
FUNC_API_SINCE(9)
@@ -588,21 +658,29 @@ void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err)
goto cleanup;
}
- if (opts->group.type != kObjectTypeNil) {
- if (opts->group.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation, "'group' must be a string");
- goto cleanup;
- }
-
- au_group = augroup_find(opts->group.data.string.data);
-
- if (au_group == AUGROUP_ERROR) {
- api_set_error(err,
- kErrorTypeException,
- "invalid augroup: %s", opts->group.data.string.data);
-
+ switch (opts->group.type) {
+ case kObjectTypeNil:
+ break;
+ case kObjectTypeString:
+ au_group = augroup_find(opts->group.data.string.data);
+ if (au_group == AUGROUP_ERROR) {
+ api_set_error(err,
+ kErrorTypeValidation,
+ "invalid augroup: %s", opts->group.data.string.data);
+ goto cleanup;
+ }
+ break;
+ case kObjectTypeInteger:
+ au_group = (int)opts->group.data.integer;
+ char *name = augroup_name(au_group);
+ if (!augroup_exists(name)) {
+ api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
+ goto cleanup;
+ }
+ break;
+ default:
+ api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
goto cleanup;
- }
}
if (opts->buffer.type != kObjectTypeNil) {
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 3a968f07ab..0ee8134ec4 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -445,6 +445,27 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - strict: boolean that indicates extmark should not be placed
/// if the line or column value is past the end of the
/// buffer or end of the line respectively. Defaults to true.
+/// - sign_text: string of length 1-2 used to display in the
+/// sign column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - sign_hl_group: name of the highlight group used to
+/// highlight the sign column text.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - number_hl_group: name of the highlight group used to
+/// highlight the number column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - line_hl_group: name of the highlight group used to
+/// highlight the whole line.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - cursorline_hl_group: name of the highlight group used to
+/// highlight the line when the cursor is on the same line
+/// as the mark and 'cursorline' is enabled.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
///
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
@@ -519,10 +540,25 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
- if (HAS_KEY(opts->hl_group)) {
- decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
- if (ERROR_SET(err)) {
- goto error;
+ struct {
+ const char *name;
+ Object *opt;
+ int *dest;
+ } hls[] = {
+ { "hl_group" , &opts->hl_group , &decor.hl_id },
+ { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
+ { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
+ { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
+ { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
+ { NULL, NULL, NULL },
+ };
+
+ for (int j = 0; hls[j].name && hls[j].dest; j++) {
+ if (HAS_KEY(*hls[j].opt)) {
+ *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
}
}
@@ -622,6 +658,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
+ if (opts->sign_text.type == kObjectTypeString) {
+ if (!init_sign_text(&decor.sign_text,
+ (char_u *)opts->sign_text.data.string.data)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
+ goto error;
+ }
+ } else if (HAS_KEY(opts->sign_text)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
+ goto error;
+ }
+
bool right_gravity = true;
OPTION_TO_BOOL(right_gravity, right_gravity, true);
@@ -709,6 +756,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
error:
clear_virttext(&decor.virt_text);
+ xfree(decor.sign_text);
return 0;
}
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index b05816f8ac..435e8195dd 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -22,6 +22,11 @@ return {
"virt_lines_above";
"virt_lines_leftcol";
"strict";
+ "sign_text";
+ "sign_hl_group";
+ "number_hl_group";
+ "line_hl_group";
+ "cursorline_hl_group";
};
keymap = {
"noremap";
@@ -83,7 +88,10 @@ return {
"standout";
"strikethrough";
"underline";
+ "underlineline";
"undercurl";
+ "underdot";
+ "underdash";
"italic";
"reverse";
"nocombine";
@@ -105,7 +113,10 @@ return {
"standout";
"strikethrough";
"underline";
+ "underlineline";
"undercurl";
+ "underdot";
+ "underdash";
"italic";
"reverse";
"nocombine";
@@ -131,6 +142,7 @@ return {
"event";
"group";
"pattern";
+ "buffer";
};
create_augroup = {
"clear";
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 35e8589255..8056950e26 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id)
}
return old_current_sctx;
}
+
+// adapted from sign.c:sign_define_init_text.
+// TODO(lewis6991): Consider merging
+int init_sign_text(char_u **sign_text, char_u *text)
+{
+ char_u *s;
+
+ char_u *endp = text + (int)STRLEN(text);
+
+ // Count cells and check for non-printable chars
+ int cells = 0;
+ for (s = text; s < endp; s += utfc_ptr2len(s)) {
+ if (!vim_isprintc(utf_ptr2char(s))) {
+ break;
+ }
+ cells += utf_ptr2cells(s);
+ }
+ // Currently must be empty, one or two display cells
+ if (s != endp || cells > 2) {
+ return FAIL;
+ }
+ if (cells < 1) {
+ return OK;
+ }
+
+ // Allocate one byte more if we need to pad up
+ // with a space.
+ size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
+ *sign_text = vim_strnsave(text, len);
+
+ if (cells == 1) {
+ STRCPY(*sign_text + len - 1, " ");
+ }
+
+ return OK;
+}
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 7cb493f57d..a850e5c1a0 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -105,15 +105,24 @@ static char_u *old_termresponse = NULL;
#define FOR_ALL_AUPATS_IN_EVENT(event, ap) \
for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT
-// Map of autocmd group names.
+// Map of autocmd group names and ids.
// name -> ID
-static Map(String, int) augroup_map = MAP_INIT;
+// ID -> name
+static Map(String, int) map_augroup_name_to_id = MAP_INIT;
+static Map(int, String) map_augroup_id_to_name = MAP_INIT;
-static void augroup_map_del(char *name)
+static void augroup_map_del(int id, char *name)
{
- String key = map_key(String, int)(&augroup_map, cstr_as_string(name));
- map_del(String, int)(&augroup_map, key);
- api_free_string(key);
+ if (name != NULL) {
+ String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name));
+ map_del(String, int)(&map_augroup_name_to_id, key);
+ api_free_string(key);
+ }
+ if (id > 0) {
+ String mapped = map_get(int, String)(&map_augroup_id_to_name, id);
+ api_free_string(mapped);
+ map_del(int, String)(&map_augroup_id_to_name, id);
+ }
}
@@ -382,12 +391,14 @@ int augroup_add(char *name)
}
if (existing_id == AUGROUP_DELETED) {
- augroup_map_del(name);
+ augroup_map_del(existing_id, name);
}
int next_id = next_augroup_id++;
- String name_copy = cstr_to_string(name);
- map_put(String, int)(&augroup_map, name_copy, next_id);
+ String name_key = cstr_to_string(name);
+ String name_val = cstr_to_string(name);
+ map_put(String, int)(&map_augroup_name_to_id, name_key, next_id);
+ map_put(int, String)(&map_augroup_id_to_name, next_id, name_val);
return next_id;
}
@@ -416,7 +427,8 @@ void augroup_del(char *name, bool stupid_legacy_mode)
FOR_ALL_AUPATS_IN_EVENT(event, ap) {
if (ap->group == i && ap->pat != NULL) {
give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true);
- map_put(String, int)(&augroup_map, cstr_as_string(name), AUGROUP_DELETED);
+ map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED);
+ augroup_map_del(ap->group, NULL);
return;
}
}
@@ -432,7 +444,7 @@ void augroup_del(char *name, bool stupid_legacy_mode)
}
// Remove the group because it's not currently in use.
- augroup_map_del(name);
+ augroup_map_del(i, name);
au_cleanup();
}
}
@@ -445,7 +457,7 @@ void augroup_del(char *name, bool stupid_legacy_mode)
int augroup_find(const char *name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- int existing_id = map_get(String, int)(&augroup_map, cstr_as_string((char *)name));
+ int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name));
if (existing_id == AUGROUP_DELETED) {
return existing_id;
}
@@ -487,13 +499,10 @@ char *augroup_name(int group)
return NULL;
}
- String key;
- int value;
- map_foreach(&augroup_map, key, value, {
- if (value == group) {
- return key.data;
- }
- });
+ String key = map_get(int, String)(&map_augroup_id_to_name, group);
+ if (key.data != NULL) {
+ return key.data;
+ }
// If it's not in the map anymore, then it must have been deleted.
return (char *)get_deleted_augroup();
@@ -526,7 +535,7 @@ void do_augroup(char_u *arg, int del_group)
String name;
int value;
- map_foreach(&augroup_map, name, value, {
+ map_foreach(&map_augroup_name_to_id, name, value, {
if (value > 0) {
msg_puts(name.data);
} else {
@@ -556,11 +565,17 @@ void free_all_autocmds(void)
// Delete the augroup_map, including free the data
String name;
int id;
- map_foreach(&augroup_map, name, id, {
+ map_foreach(&map_augroup_name_to_id, name, id, {
+ (void)id;
+ api_free_string(name);
+ })
+ map_destroy(String, int)(&map_augroup_name_to_id);
+
+ map_foreach(&map_augroup_id_to_name, id, name, {
(void)id;
api_free_string(name);
})
- map_destroy(String, int)(&augroup_map);
+ map_destroy(int, String)(&map_augroup_id_to_name);
}
#endif
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 9e82b4e80b..2bd14e2103 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -36,6 +36,7 @@
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/decoration.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
@@ -1736,7 +1737,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
- buf->b_signcols_valid = false;
+ buf->b_signcols.valid = false;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -5467,15 +5468,23 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
int linesum = 0;
linenr_T curline = 0;
+ buf->b_signcols.sentinel = 0;
+
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->se_lnum > curline) {
+ // Counted all signs, now add extmark signs
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1,
+ maximum-linesum);
+ }
+ curline = sign->se_lnum;
if (linesum > signcols) {
signcols = linesum;
+ buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
}
- curline = sign->se_lnum;
linesum = 0;
}
if (sign->se_has_text_or_icon) {
@@ -5483,8 +5492,22 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
}
}
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum);
+ }
+ if (linesum > signcols) {
+ signcols = linesum;
+ if (signcols >= maximum) {
+ return maximum;
+ }
+ }
+
+ // Check extmarks between signs
+ linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count-1, maximum);
+
if (linesum > signcols) {
signcols = linesum;
+ buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
@@ -5493,28 +5516,96 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
return signcols;
}
+/// Invalidate the signcolumn if needed after deleting
+/// signs between line1 and line2 (inclusive).
+///
+/// @param buf buffer to check
+/// @param line1 start of region being deleted
+/// @param line2 end of region being deleted
+void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ linenr_T sent = buf->b_signcols.sentinel;
+
+ if (sent >= line1 && sent <= line2) {
+ // Only invalidate when removing signs at the sentinel line.
+ buf->b_signcols.valid = false;
+ }
+}
+
+/// Re-calculate the signcolumn after adding a sign.
+///
+/// @param buf buffer to check
+/// @param added sign being added
+void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
+{
+ if (!buf->b_signcols.valid) {
+ return;
+ }
+
+ if (!added || !buf->b_signcols.sentinel) {
+ buf->b_signcols.valid = false;
+ return;
+ }
+
+ if (added->se_lnum == buf->b_signcols.sentinel) {
+ if (buf->b_signcols.size == buf->b_signcols.max) {
+ buf->b_signcols.max++;
+ }
+ buf->b_signcols.size++;
+ return;
+ }
+
+ sign_entry_T *s;
+
+ // Get first sign for added lnum
+ for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {}
+
+ // Count signs for lnum
+ int linesum = 1;
+ for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) {
+ linesum++;
+ }
+ linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum-1, (int)s->se_lnum-1,
+ SIGN_SHOW_MAX-linesum);
+
+ if (linesum > buf->b_signcols.size) {
+ buf->b_signcols.size = linesum;
+ buf->b_signcols.max = linesum;
+ buf->b_signcols.sentinel = added->se_lnum;
+ }
+}
+
int buf_signcols(buf_T *buf, int maximum)
{
// The maximum can be determined from 'signcolumn' which is window scoped so
// need to invalidate signcols if the maximum is greater than the previous
// maximum.
- if (maximum > buf->b_signcols_max) {
- buf->b_signcols_valid = false;
+ if (maximum > buf->b_signcols.max) {
+ buf->b_signcols.valid = false;
}
- if (!buf->b_signcols_valid) {
+ if (!buf->b_signcols.valid) {
int signcols = buf_signcols_inner(buf, maximum);
// Check if we need to redraw
- if (signcols != buf->b_signcols) {
- buf->b_signcols = signcols;
- buf->b_signcols_max = maximum;
+ if (signcols != buf->b_signcols.size) {
+ buf->b_signcols.size = signcols;
+ buf->b_signcols.max = maximum;
redraw_buf_later(buf, NOT_VALID);
}
- buf->b_signcols_valid = true;
+ buf->b_signcols.valid = true;
}
- return buf->b_signcols;
+ return buf->b_signcols.size;
}
// Get "buf->b_fname", use "[No Name]" if it is NULL.
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 1e0c837056..7acb646980 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -862,9 +862,12 @@ struct file_buffer {
// may use a different synblock_T.
sign_entry_T *b_signlist; // list of placed signs
- int b_signcols; // last calculated number of sign columns
- bool b_signcols_valid; // calculated sign columns is valid
- int b_signcols_max; // Maximum value b_signcols is valid for.
+ struct {
+ int size; // last calculated number of sign columns
+ bool valid; // calculated sign columns is valid
+ linenr_T sentinel; // a line number which is holding up the signcolumn
+ int max; // Maximum value size is valid for.
+ } b_signcols;
Terminal *terminal; // Terminal instance associated with the buffer
@@ -875,6 +878,7 @@ struct file_buffer {
MarkTree b_marktree[1];
Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
size_t b_virt_line_blocks; // number of virt_line blocks
+ size_t b_signs; // number of sign extmarks
// array of channel_id:s which have asked to receive updates for this
// buffer.
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index f4882e57e1..97aac67627 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1500,7 +1500,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons
} else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN))
&& !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8'
&& ptr[1] != '9') {
- pre = ptr[1];
+ pre = (char_u)ptr[1];
// Detect hexadecimal: 0x or 0X followed by hex digit.
if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2)
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 75ebf0084e..b48a3155b6 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -201,7 +201,7 @@ void do_debug(char_u *cmd)
if (last_cmd != 0) {
// Check that the tail matches.
p++;
- while (*p != NUL && *p == *tail) {
+ while (*p != NUL && *p == (char_u)(*tail)) {
p++;
tail++;
}
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 935b233752..619e8fdadc 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -1,10 +1,12 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+#include "nvim/buffer.h"
#include "nvim/decoration.h"
#include "nvim/extmark.h"
#include "nvim/highlight.h"
#include "nvim/lua/executor.h"
+#include "nvim/move.h"
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
@@ -65,8 +67,10 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
{
- if ((!decor || decor->hl_id) && row2 >= row1) {
- redraw_buf_range_later(buf, row1+1, row2+1);
+ if (row2 >= row1) {
+ if (!decor || decor->hl_id || decor_has_sign(decor)) {
+ redraw_buf_range_later(buf, row1+1, row2+1);
+ }
}
if (decor && kv_size(decor->virt_text)) {
@@ -82,9 +86,18 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
{
decor_redraw(buf, row, row2, decor);
- if (decor && kv_size(decor->virt_lines)) {
- assert(buf->b_virt_line_blocks > 0);
- buf->b_virt_line_blocks--;
+ if (decor) {
+ if (kv_size(decor->virt_lines)) {
+ assert(buf->b_virt_line_blocks > 0);
+ buf->b_virt_line_blocks--;
+ }
+ if (decor_has_sign(decor)) {
+ assert(buf->b_signs > 0);
+ buf->b_signs--;
+ }
+ if (row2 >= row && decor->sign_text) {
+ buf_signcols_del_check(buf, row+1, row2+1);
+ }
}
decor_free(decor);
}
@@ -97,6 +110,7 @@ void decor_free(Decoration *decor)
clear_virttext(&kv_A(decor->virt_lines, i).line);
}
kv_destroy(decor->virt_lines);
+ xfree(decor->sign_text);
xfree(decor);
}
}
@@ -136,6 +150,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
{
state->row = -1;
state->buf = buf;
+ state->has_sign_decor = false;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange item = kv_A(state->active, i);
if (item.virt_text_owned) {
@@ -180,9 +195,23 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
Decoration decor = get_decor(mark);
+ // Exclude non-paired marks unless they contain virt_text or a sign
+ if (!mt_paired(mark)
+ && !kv_size(decor.virt_text)
+ && !decor_has_sign(&decor)) {
+ goto next_mark;
+ }
+
+ // Don't add signs for end marks as the start mark has already been added.
+ if (mt_end(mark) && decor_has_sign(&decor)) {
+ goto next_mark;
+ }
+
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
- if ((!mt_end(mark) && altpos.row < top_row
+ // Exclude start marks if the end mark position is above the top row
+ // Exclude end marks if we have already added the start mark
+ if ((mt_start(mark) && altpos.row < top_row
&& !kv_size(decor.virt_text))
|| (mt_end(mark) && altpos.row >= top_row)) {
goto next_mark;
@@ -241,6 +270,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
kv_A(state->active, index) = kv_A(state->active, index-1);
}
kv_A(state->active, index) = range;
+
+ if (decor_has_sign(decor)) {
+ state->has_sign_decor = true;
+ }
}
int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state)
@@ -294,7 +327,8 @@ next_mark:
bool active = false, keep = true;
if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
+ if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))
+ && !decor_has_sign(&item.decor)) {
keep = false;
}
} else {
@@ -329,6 +363,131 @@ next_mark:
return attr;
}
+void decor_redraw_signs(buf_T *buf, DecorState *state, int row,
+ int *num_signs, sign_attrs_T sattrs[])
+{
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange item = kv_A(state->active, i);
+ Decoration *decor = &item.decor;
+
+ if (!decor_has_sign(decor)) {
+ continue;
+ }
+
+ if (state->row != item.start_row) {
+ continue;
+ }
+
+ int j;
+ for (j = (*num_signs); j > 0; j--) {
+ if (sattrs[j].sat_prio <= decor->priority) {
+ break;
+ }
+ sattrs[j] = sattrs[j-1];
+ }
+ if (j < SIGN_SHOW_MAX) {
+ memset(&sattrs[j], 0, sizeof(sign_attrs_T));
+ sattrs[j].sat_text = decor->sign_text;
+ if (decor->sign_hl_id != 0) {
+ sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
+ }
+ if (decor->number_hl_id != 0) {
+ sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
+ }
+ if (decor->line_hl_id != 0) {
+ sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
+ }
+ if (decor->cursorline_hl_id != 0) {
+ sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
+ }
+ sattrs[j].sat_prio = decor->priority;
+ (*num_signs)++;
+ }
+ }
+}
+
+// Get the maximum required amount of sign columns needed between row and
+// end_row.
+int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
+{
+ int count = 0; // count for the number of signs on a given row
+ int count_remove = 0; // how much to decrement count by when iterating marks for a new row
+ int signcols = 0; // highest value of count
+ int currow = -1; // current row
+
+ if (max <= 1 && buf->b_signs >= (size_t)max) {
+ return max;
+ }
+
+ if (buf->b_signs == 0) {
+ return 0;
+ }
+
+ MarkTreeIter itr[1] = { 0 };
+ marktree_itr_get(buf->b_marktree, 0, -1, itr);
+ while (true) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row > end_row) {
+ break;
+ }
+
+ if ((mark.pos.row < row && mt_end(mark))
+ || marktree_decor_level(mark) < kDecorLevelVisible
+ || !mark.decor_full) {
+ goto next_mark;
+ }
+
+ Decoration decor = get_decor(mark);
+
+ if (!decor.sign_text) {
+ goto next_mark;
+ }
+
+ if (mark.pos.row > currow) {
+ count -= count_remove;
+ count_remove = 0;
+ currow = mark.pos.row;
+ }
+
+ if (!mt_paired(mark)) {
+ if (mark.pos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ count_remove++;
+ }
+ goto next_mark;
+ }
+
+ mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
+
+ if (mt_end(mark)) {
+ if (mark.pos.row >= row && altpos.row <= end_row) {
+ count_remove++;
+ }
+ } else {
+ if (altpos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ }
+ }
+
+next_mark:
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+
+ return signcols;
+}
+
void decor_redraw_end(DecorState *state)
{
state->buf = NULL;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 02472d09e4..7e6dbdf1a7 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -47,13 +47,18 @@ struct Decoration {
bool virt_text_hide;
bool hl_eol;
bool virt_lines_above;
- // TODO(bfredl): style, signs, etc
+ // TODO(bfredl): style, etc
DecorPriority priority;
int col; // fixed col value, like win_col
int virt_text_width; // width of virt_text
+ char_u *sign_text;
+ int sign_hl_id;
+ int number_hl_id;
+ int line_hl_id;
+ int cursorline_hl_id;
};
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
- false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
+ false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 }
typedef struct {
int start_row;
@@ -75,6 +80,7 @@ typedef struct {
int col_until;
int current;
int eol_col;
+ bool has_sign_decor;
} DecorState;
typedef struct {
@@ -98,6 +104,15 @@ EXTERN bool provider_active INIT(= false);
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, -1 }
+static inline bool decor_has_sign(Decoration *decor)
+{
+ return decor->sign_text
+ || decor->sign_hl_id
+ || decor->number_hl_id
+ || decor->line_hl_id
+ || decor->cursorline_hl_id;
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.h.generated.h"
#endif
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 00ffa7cba1..095e082f61 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -7776,7 +7776,7 @@ int hkmap(int c)
case ';':
c = 't'; break;
default: {
- static char str[] = "zqbcxlsjphmkwonu ydafe rig";
+ static char_u str[] = "zqbcxlsjphmkwonu ydafe rig";
if (c < 'a' || c > 'z') {
return c;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d95b9560c2..8cdb03c341 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4858,7 +4858,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
long numval;
char_u *stringval;
int opt_type;
- int c;
bool working = (**arg == '+'); // has("+option")
int ret = OK;
int opt_flags;
@@ -4877,7 +4876,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return OK;
}
- c = *option_end;
+ char c = *option_end;
*option_end = NUL;
opt_type = get_option_value(*arg, &numval,
rettv == NULL ? NULL : &stringval, opt_flags);
@@ -7790,8 +7789,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
break;
case kCallbackLua:
- ILOG(" We tryin to call dat dang lua ref ");
- nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL);
+ nlua_call_ref(callback->data.luaref, NULL, args, false, NULL);
return false;
break;
@@ -9442,7 +9440,7 @@ void new_script_vars(scid_T id)
hashtab_T *ht;
scriptvar_T *sv;
- ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len));
+ ga_grow(&ga_scripts, id - ga_scripts.ga_len);
{
/* Re-allocating ga_data means that an ht_array pointing to
* ht_smallarray becomes invalid. We can recognize this: ht_mask is
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 49dde537c3..f47c9c482b 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -8296,7 +8296,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
// Repeat until {skip} returns false.
for (;;) {
subpatnum
- = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia);
+ = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia);
// finding the first match again means there is no match where {skip}
// evaluates to zero.
if (firstpos.lnum != 0 && equalpos(pos, firstpos)) {
@@ -9339,7 +9339,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
{
static char *e_invact = N_("E927: Invalid action: '%s'");
const char *title = NULL;
- int action = ' ';
+ char action = ' ';
static int recursive = 0;
rettv->vval.v_number = -1;
dict_T *what = NULL;
@@ -9569,7 +9569,6 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c
*/
static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- int regname;
bool append = false;
MotionType yank_type;
long block_len;
@@ -9583,13 +9582,13 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (strregname == NULL) {
return; // Type error; errmsg already given.
}
- regname = (uint8_t)(*strregname);
+ char regname = (uint8_t)(*strregname);
if (regname == 0 || regname == '@') {
regname = '"';
}
const typval_T *regcontents = NULL;
- int pointreg = 0;
+ char pointreg = 0;
if (argvars[1].v_type == VAR_DICT) {
dict_T *const d = argvars[1].vval.v_dict;
@@ -9759,7 +9758,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static char *e_invact2 = N_("E962: Invalid action: '%s'");
win_T *wp;
dict_T *d;
- int action = 'r';
+ char action = 'r';
rettv->vval.v_number = -1;
@@ -11409,14 +11408,22 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
p = highlight_has_attr(id, HL_STANDOUT, modec);
}
break;
- case 'u':
- if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline
+ case 'u': {
+ const size_t len = STRLEN(what);
+ if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline
p = highlight_has_attr(id, HL_UNDERLINE, modec);
- } else { // undercurl
+ } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl
p = highlight_has_attr(id, HL_UNDERCURL, modec);
+ } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline
+ p = highlight_has_attr(id, HL_UNDERLINELINE, modec);
+ } else if (len > 6 && TOLOWER_ASC(what[6]) == 'o') { // underdot
+ p = highlight_has_attr(id, HL_UNDERDOT, modec);
+ } else { // underdash
+ p = highlight_has_attr(id, HL_UNDERDASH, modec);
}
break;
}
+ }
rettv->v_type = VAR_STRING;
rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p));
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 5764f9fbd4..471c4092fe 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -516,7 +516,7 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf
if (llen > 0) {
fname_buf[0] = K_SPECIAL;
fname_buf[1] = KS_EXTRA;
- fname_buf[2] = (int)KE_SNR;
+ fname_buf[2] = KE_SNR;
int i = 3;
if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
if (current_sctx.sc_sid <= 0) {
@@ -1713,7 +1713,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA
- && (*pp)[2] == (int)KE_SNR) {
+ && (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
return (char_u *)xmemdupz(start, len);
@@ -1821,7 +1821,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
// Change "<SNR>" to the byte sequence.
name[0] = K_SPECIAL;
name[1] = KS_EXTRA;
- name[2] = (int)KE_SNR;
+ name[2] = KE_SNR;
memmove(name + 3, name + 5, strlen((char *)name + 5) + 1);
}
goto theend;
@@ -1888,7 +1888,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
if (!skip && lead > 0) {
name[0] = K_SPECIAL;
name[1] = KS_EXTRA;
- name[2] = (int)KE_SNR;
+ name[2] = KE_SNR;
if (sid_buf_len > 0) { // If it's "<SID>"
memcpy(name + 3, sid_buf, sid_buf_len);
}
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 8666c7e33a..5c040adc1c 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1698,7 +1698,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize,
return false;
}
if (ga->ga_len > init_growsize) {
- ga_set_growsize(ga, MAX(ga->ga_len, 8000));
+ ga_set_growsize(ga, MIN(ga->ga_len, 8000));
}
ga_concat_len(ga, (const char *)line + 1, len - 1);
return true;
@@ -1796,7 +1796,7 @@ scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out)
if (sid_out != NULL) {
*sid_out = sid;
}
- ga_grow(&script_items, (int)(sid - script_items.ga_len));
+ ga_grow(&script_items, sid - script_items.ga_len);
while (script_items.ga_len < sid) {
script_items.ga_len++;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
@@ -1852,7 +1852,7 @@ static void cmd_source_buffer(const exarg_T *const eap)
for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
// Adjust growsize to current length to speed up concatenating many lines.
if (ga.ga_len > 400) {
- ga_set_growsize(&ga, MAX(ga.ga_len, 8000));
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
}
ga_concat(&ga, (char *)ml_get(curr_lnum));
ga_append(&ga, NL);
@@ -2188,7 +2188,6 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
}
-
/// ":scriptnames"
void ex_scriptnames(exarg_T *eap)
{
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index d3203bcee8..a140d76858 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -80,7 +80,7 @@
static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s");
static char *e_no_such_user_defined_command_in_current_buffer_str
- = N_("E1237: No such user-defined command in current buffer: %s");
+ = N_("E1237: No such user-defined command in current buffer: %s");
static int quitmore = 0;
static bool ex_pressedreturn = false;
@@ -2835,7 +2835,7 @@ int modifier_len(char_u *cmd)
for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
int j;
for (j = 0; p[j] != NUL; j++) {
- if (p[j] != cmdmods[i].name[j]) {
+ if (p[j] != (char_u)cmdmods[i].name[j]) {
break;
}
}
@@ -8160,7 +8160,7 @@ static void ex_operators(exarg_T *eap)
case CMD_yank:
oa.op_type = OP_YANK;
- (void)op_yank(&oa, true, false);
+ (void)op_yank(&oa, true);
break;
default: // CMD_rshift or CMD_lshift
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 851828afcf..25b6aa7d8a 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -430,7 +430,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int
STRCAT(val, p);
p[-2] = NUL;
- sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]);
+ snprintf(val + STRLEN(p), strlen(" (%s)"), " (%s)", &mesg[1]);
p[-2] = '"';
}
break;
@@ -1602,7 +1602,7 @@ void ex_endtry(exarg_T *eap)
{
int idx;
bool rethrow = false;
- int pending = CSTP_NONE;
+ char pending = CSTP_NONE;
void *rettv = NULL;
cstack_T *const cstack = eap->cstack;
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 48e57e20e1..2916e3e978 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -67,7 +67,9 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
uint8_t decor_level = kDecorLevelNone; // no decor
if (decor) {
- if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) {
+ if (kv_size(decor->virt_text)
+ || kv_size(decor->virt_lines)
+ || decor_has_sign(decor)) {
decor_full = true;
decor = xmemdup(decor, sizeof *decor);
}
@@ -142,6 +144,13 @@ revised:
if (kv_size(decor->virt_lines)) {
buf->b_virt_line_blocks++;
}
+ if (decor_has_sign(decor)) {
+ buf->b_signs++;
+ }
+ if (decor->sign_text) {
+ // TODO(lewis6991): smarter invalidation
+ buf_signcols_add_check(buf, NULL);
+ }
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 965aa8749d..c5a89d3371 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -3911,13 +3911,13 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST
{
- return (long)file_info->stat.st_mtim.tv_nsec != mtime_ns
+ return file_info->stat.st_mtim.tv_nsec != mtime_ns
#if defined(__linux__) || defined(MSWIN)
// On a FAT filesystem, esp. under Linux, there are only 5 bits to store
// the seconds. Since the roundoff is done when flushing the inode, the
// time may change unexpectedly by one second!!!
- || (long)file_info->stat.st_mtim.tv_sec - mtime > 1
- || mtime - (long)file_info->stat.st_mtim.tv_sec > 1;
+ || file_info->stat.st_mtim.tv_sec - mtime > 1
+ || mtime - file_info->stat.st_mtim.tv_sec > 1;
#else
|| (long)file_info->stat.st_mtim.tv_sec != mtime;
#endif
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 8426cdb98c..299456f688 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1907,9 +1907,6 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// complete match
if (keylen >= 0 && keylen <= typebuf.tb_len) {
char_u *map_str = NULL;
- int save_m_expr;
- int save_m_noremap;
- int save_m_silent;
// Write chars to script file(s).
// Note: :lmap mappings are written *after* being applied. #5658
@@ -1946,9 +1943,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Copy the values from *mp that are used, because evaluating the
// expression may invoke a function that redefines the mapping, thereby
// making *mp invalid.
- save_m_expr = mp->m_expr;
- save_m_noremap = mp->m_noremap;
- save_m_silent = mp->m_silent;
+ char save_m_expr = mp->m_expr;
+ int save_m_noremap = mp->m_noremap;
+ char save_m_silent = mp->m_silent;
char_u *save_m_keys = NULL; // only saved when needed
char_u *save_m_str = NULL; // only saved when needed
LuaRef save_m_luaref = mp->m_luaref;
@@ -2661,9 +2658,9 @@ int fix_input_buffer(char_u *buf, int len)
/// @param[in] orig_rhs_len `strlen` of orig_rhs.
/// @param[in] cpo_flags See param docs for @ref replace_termcodes.
/// @param[out] mapargs MapArguments struct holding the replaced strings.
-void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len,
- const char_u *orig_rhs, const size_t orig_rhs_len,
- LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs)
+void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs,
+ const size_t orig_rhs_len, LuaRef rhs_lua, int cpo_flags,
+ MapArguments *mapargs)
{
char_u *lhs_buf = NULL;
char_u *rhs_buf = NULL;
@@ -3988,7 +3985,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
/// special characters.
///
/// @param c NUL or typed character for abbreviation
-static char_u *eval_map_expr(mapblock_T *mp, int c)
+static char_u *eval_map_expr(mapblock_T *mp, int c)
{
char_u *res;
char_u *p = NULL;
@@ -4143,7 +4140,7 @@ int makemap(FILE *fd, buf_T *buf)
}
for (p = mp->m_str; *p != NUL; p++) {
if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
- && p[2] == (int)KE_SNR) {
+ && p[2] == KE_SNR) {
break;
}
}
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index eb10c65be9..93c8c53e33 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -420,7 +420,10 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec)
pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL);
pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL);
pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL);
+ pattr->underlineline = (highlight_has_attr(hl_id, HL_UNDERLINELINE, modec) != NULL);
pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL);
+ pattr->underdot = (highlight_has_attr(hl_id, HL_UNDERDOT, modec) != NULL);
+ pattr->underdash = (highlight_has_attr(hl_id, HL_UNDERDASH, modec) != NULL);
uint32_t fg_color = prt_get_color(hl_id, modec);
diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h
index eba769d342..16119c5d2d 100644
--- a/src/nvim/hardcopy.h
+++ b/src/nvim/hardcopy.h
@@ -18,6 +18,9 @@ typedef struct {
TriState italic;
TriState underline;
int undercurl;
+ int underlineline;
+ int underdot;
+ int underdash;
} prt_text_attr_T;
/*
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index e43a56086f..a1fd0d0d66 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -327,7 +327,7 @@ void update_window_hl(win_T *wp, bool invalid)
wp->w_hl_attr_normal);
}
- for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
+ for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
int attr;
if (wp->w_hl_ids[hlf] != 0) {
attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
@@ -558,7 +558,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
cattrs = battrs;
cattrs.rgb_fg_color = rgb_blend(ratio, battrs.rgb_fg_color,
fattrs.rgb_bg_color);
- if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) {
cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color,
fattrs.rgb_bg_color);
} else {
@@ -576,7 +576,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
}
cattrs.rgb_fg_color = rgb_blend(ratio/2, battrs.rgb_fg_color,
fattrs.rgb_fg_color);
- if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) {
cattrs.rgb_sp_color = rgb_blend(ratio/2, battrs.rgb_bg_color,
fattrs.rgb_sp_color);
} else {
@@ -743,10 +743,23 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb)
PUT(hl, "underline", BOOLEAN_OBJ(true));
}
+ if (mask & HL_UNDERLINELINE) {
+ PUT(hl, "underlineline", BOOLEAN_OBJ(true));
+ }
+
if (mask & HL_UNDERCURL) {
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
}
+ if (mask & HL_UNDERDOT) {
+ PUT(hl, "underdot", BOOLEAN_OBJ(true));
+ }
+
+ if (mask & HL_UNDERDASH) {
+ PUT(hl, "underdash", BOOLEAN_OBJ(true));
+ }
+
+
if (mask & HL_ITALIC) {
PUT(hl, "italic", BOOLEAN_OBJ(true));
}
@@ -813,7 +826,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(dict, mask, bold, , HL_BOLD);
CHECK_FLAG(dict, mask, standout, , HL_STANDOUT);
CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE);
+ CHECK_FLAG(dict, mask, underlineline, , HL_UNDERLINELINE);
CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL);
+ CHECK_FLAG(dict, mask, underdot, , HL_UNDERDOT);
+ CHECK_FLAG(dict, mask, underdash, , HL_UNDERDASH);
CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 50a03e0c02..dcfb9358bb 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -24,6 +24,10 @@ typedef enum {
HL_FG_INDEXED = 0x0200,
HL_DEFAULT = 0x0400,
HL_GLOBAL = 0x0800,
+ HL_UNDERLINELINE = 0x1000,
+ HL_UNDERDOT = 0x2000,
+ HL_UNDERDASH = 0x4000,
+ HL_ANY_UNDERLINE = HL_UNDERLINE | HL_UNDERLINELINE | HL_UNDERCURL | HL_UNDERDOT | HL_UNDERDASH,
} HlAttrFlags;
/// Stores a complete highlighting entry, including colors and attributes
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index e233e0e6d0..6aaff100ca 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -525,24 +525,6 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread)
lua_pop(lstate, 3);
}
-static void nlua_preload_modules(lua_State *lstate)
-{
- lua_getglobal(lstate, "package"); // [package]
- lua_getfield(lstate, -1, "preload"); // [package, preload]
- for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) {
- ModuleDef def = builtin_modules[i];
- lua_pushinteger(lstate, (long)i); // [package, preload, i]
- lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure]
- lua_setfield(lstate, -2, def.name); // [package, preload]
-
- if (nlua_disable_preload && strequal(def.name, "vim")) {
- break;
- }
- }
-
- lua_pop(lstate, 2); // []
-}
-
static int nlua_module_preloader(lua_State *lstate)
{
size_t i = (size_t)lua_tointeger(lstate, lua_upvalueindex(1));
@@ -561,24 +543,30 @@ static int nlua_module_preloader(lua_State *lstate)
return 1;
}
-static bool nlua_common_package_init(lua_State *lstate)
+static bool nlua_init_packages(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
- nlua_preload_modules(lstate);
+ // put builtin packages in preload
+ lua_getglobal(lstate, "package"); // [package]
+ lua_getfield(lstate, -1, "preload"); // [package, preload]
+ for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) {
+ ModuleDef def = builtin_modules[i];
+ lua_pushinteger(lstate, (long)i); // [package, preload, i]
+ lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure]
+ lua_setfield(lstate, -2, def.name); // [package, preload]
- lua_getglobal(lstate, "require");
- lua_pushstring(lstate, "vim._load_package");
- if (nlua_pcall(lstate, 1, 0)) {
- nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s\n"));
- return false;
+ if (nlua_disable_preload && strequal(def.name, "vim.inspect")) {
+ break;
+ }
}
- // TODO(bfredl): ideally all initialization should be done as a single require
- // call.
+ lua_pop(lstate, 2); // []
+
lua_getglobal(lstate, "require");
- lua_pushstring(lstate, "vim.shared");
+ lua_pushstring(lstate, "vim._init_packages");
if (nlua_pcall(lstate, 1, 0)) {
- nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n"));
+ mch_errmsg(lua_tostring(lstate, -1));
+ mch_errmsg("\n");
return false;
}
@@ -654,14 +642,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setglobal(lstate, "vim");
- if (!nlua_common_package_init(lstate)) {
- return false;
- }
-
- lua_getglobal(lstate, "require");
- lua_pushstring(lstate, "vim");
- if (nlua_pcall(lstate, 1, 0)) {
- nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n"));
+ if (!nlua_init_packages(lstate)) {
return false;
}
@@ -732,7 +713,7 @@ static lua_State *nlua_thread_acquire_vm(void)
lua_setglobal(lstate, "vim");
- nlua_common_package_init(lstate);
+ nlua_init_packages(lstate);
lua_getglobal(lstate, "package");
lua_getfield(lstate, -1, "loaded");
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
deleted file mode 100644
index f5f293939b..0000000000
--- a/src/nvim/lua/vim.lua
+++ /dev/null
@@ -1,666 +0,0 @@
--- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib)
---
--- Lua code lives in one of three places:
--- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the
--- `inspect` and `lpeg` modules.
--- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests.
--- (This will go away if we migrate to nvim as the test-runner.)
--- 3. src/nvim/lua/: Compiled-into Nvim itself.
---
--- Guideline: "If in doubt, put it in the runtime".
---
--- Most functions should live directly in `vim.`, not in submodules.
--- The only "forbidden" names are those claimed by legacy `if_lua`:
--- $ vim
--- :lua for k,v in pairs(vim) do print(k) end
--- buffer
--- open
--- window
--- lastline
--- firstline
--- type
--- line
--- eval
--- dict
--- beep
--- list
--- command
---
--- Reference (#6580):
--- - https://github.com/luafun/luafun
--- - https://github.com/rxi/lume
--- - http://leafo.net/lapis/reference/utilities.html
--- - https://github.com/torch/paths
--- - https://github.com/bakpakin/Fennel (pretty print, repl)
--- - https://github.com/howl-editor/howl/tree/master/lib/howl/util
-
-local vim = vim
-assert(vim)
-assert(vim.inspect)
-
--- These are for loading runtime modules lazily since they aren't available in
--- the nvim binary as specified in executor.c
-setmetatable(vim, {
- __index = function(t, key)
- if key == 'treesitter' then
- t.treesitter = require('vim.treesitter')
- return t.treesitter
- elseif key == 'filetype' then
- t.filetype = require('vim.filetype')
- return t.filetype
- elseif key == 'F' then
- t.F = require('vim.F')
- return t.F
- elseif require('vim.uri')[key] ~= nil then
- -- Expose all `vim.uri` functions on the `vim` module.
- t[key] = require('vim.uri')[key]
- return t[key]
- elseif key == 'lsp' then
- t.lsp = require('vim.lsp')
- return t.lsp
- elseif key == 'highlight' then
- t.highlight = require('vim.highlight')
- return t.highlight
- elseif key == 'diagnostic' then
- t.diagnostic = require('vim.diagnostic')
- return t.diagnostic
- elseif key == 'keymap' then
- t.keymap = require('vim.keymap')
- return t.keymap
- elseif key == 'ui' then
- t.ui = require('vim.ui')
- return t.ui
- end
- end
-})
-
-vim.log = {
- levels = {
- TRACE = 0;
- DEBUG = 1;
- INFO = 2;
- WARN = 3;
- ERROR = 4;
- }
-}
-
--- Internal-only until comments in #8107 are addressed.
--- Returns:
--- {errcode}, {output}
-function vim._system(cmd)
- local out = vim.fn.system(cmd)
- local err = vim.v.shell_error
- return err, out
-end
-
--- Gets process info from the `ps` command.
--- Used by nvim_get_proc() as a fallback.
-function vim._os_proc_info(pid)
- if pid == nil or pid <= 0 or type(pid) ~= 'number' then
- error('invalid pid')
- end
- local cmd = { 'ps', '-p', pid, '-o', 'comm=', }
- local err, name = vim._system(cmd)
- if 1 == err and vim.trim(name) == '' then
- return {} -- Process not found.
- elseif 0 ~= err then
- error('command failed: '..vim.fn.string(cmd))
- end
- local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', })
- -- Remove trailing whitespace.
- name = vim.trim(name):gsub('^.*/', '')
- ppid = tonumber(ppid) or -1
- return {
- name = name,
- pid = pid,
- ppid = ppid,
- }
-end
-
--- Gets process children from the `pgrep` command.
--- Used by nvim_get_proc_children() as a fallback.
-function vim._os_proc_children(ppid)
- if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then
- error('invalid ppid')
- end
- local cmd = { 'pgrep', '-P', ppid, }
- local err, rv = vim._system(cmd)
- if 1 == err and vim.trim(rv) == '' then
- return {} -- Process not found.
- elseif 0 ~= err then
- error('command failed: '..vim.fn.string(cmd))
- end
- local children = {}
- for s in rv:gmatch('%S+') do
- local i = tonumber(s)
- if i ~= nil then
- table.insert(children, i)
- end
- end
- return children
-end
-
--- TODO(ZyX-I): Create compatibility layer.
-
---- Return a human-readable representation of the given object.
----
----@see https://github.com/kikito/inspect.lua
----@see https://github.com/mpeterv/vinspect
-local function inspect(object, options) -- luacheck: no unused
- error(object, options) -- Stub for gen_vimdoc.py
-end
-
-do
- local tdots, tick, got_line1 = 0, 0, false
-
- --- Paste handler, invoked by |nvim_paste()| when a conforming UI
- --- (such as the |TUI|) pastes text into the editor.
- ---
- --- Example: To remove ANSI color codes when pasting:
- --- <pre>
- --- vim.paste = (function(overridden)
- --- return function(lines, phase)
- --- for i,line in ipairs(lines) do
- --- -- Scrub ANSI color codes from paste input.
- --- lines[i] = line:gsub('\27%[[0-9;mK]+', '')
- --- end
- --- overridden(lines, phase)
- --- end
- --- end)(vim.paste)
- --- </pre>
- ---
- ---@see |paste|
- ---
- ---@param lines |readfile()|-style list of lines to paste. |channel-lines|
- ---@param phase -1: "non-streaming" paste: the call contains all lines.
- --- If paste is "streamed", `phase` indicates the stream state:
- --- - 1: starts the paste (exactly once)
- --- - 2: continues the paste (zero or more times)
- --- - 3: ends the paste (exactly once)
- ---@returns false if client should cancel the paste.
- function vim.paste(lines, phase)
- local call = vim.api.nvim_call_function
- local now = vim.loop.now()
- local mode = call('mode', {}):sub(1,1)
- if phase < 2 then -- Reset flags.
- tdots, tick, got_line1 = now, 0, false
- elseif mode ~= 'c' then
- vim.api.nvim_command('undojoin')
- end
- if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line.
- got_line1 = (#lines > 1)
- vim.api.nvim_set_option('paste', true) -- For nvim_input().
- local line1 = lines[1]:gsub('<', '<lt>'):gsub('[\r\n\012\027]', ' ') -- Scrub.
- vim.api.nvim_input(line1)
- vim.api.nvim_set_option('paste', false)
- elseif mode ~= 'c' then
- if phase < 2 and mode:find('^[vV\22sS\19]') then
- vim.api.nvim_command([[exe "normal! \<Del>"]])
- vim.api.nvim_put(lines, 'c', false, true)
- elseif phase < 2 and not mode:find('^[iRt]') then
- vim.api.nvim_put(lines, 'c', true, true)
- -- XXX: Normal-mode: workaround bad cursor-placement after first chunk.
- vim.api.nvim_command('normal! a')
- elseif phase < 2 and mode == 'R' then
- local nchars = 0
- for _, line in ipairs(lines) do
- nchars = nchars + line:len()
- end
- local row, col = unpack(vim.api.nvim_win_get_cursor(0))
- local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1]
- local firstline = lines[1]
- firstline = bufline:sub(1, col)..firstline
- lines[1] = firstline
- lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len())
- vim.api.nvim_buf_set_lines(0, row-1, row, false, lines)
- else
- vim.api.nvim_put(lines, 'c', false, true)
- end
- end
- if phase ~= -1 and (now - tdots >= 100) then
- local dots = ('.'):rep(tick % 4)
- tdots = now
- tick = tick + 1
- -- Use :echo because Lua print('') is a no-op, and we want to clear the
- -- message when there are zero dots.
- vim.api.nvim_command(('echo "%s"'):format(dots))
- end
- if phase == -1 or phase == 3 then
- vim.api.nvim_command('redraw'..(tick > 1 and '|echo ""' or ''))
- end
- return true -- Paste will not continue if not returning `true`.
- end
-end
-
---- Defers callback `cb` until the Nvim API is safe to call.
----
----@see |lua-loop-callbacks|
----@see |vim.schedule()|
----@see |vim.in_fast_event()|
-function vim.schedule_wrap(cb)
- return (function (...)
- local args = vim.F.pack_len(...)
- vim.schedule(function() cb(vim.F.unpack_len(args)) end)
- end)
-end
-
---- <Docs described in |vim.empty_dict()| >
----@private
-function vim.empty_dict()
- return setmetatable({}, vim._empty_dict_mt)
-end
-
--- vim.fn.{func}(...)
-vim.fn = setmetatable({}, {
- __index = function(t, key)
- local _fn
- if vim.api[key] ~= nil then
- _fn = function()
- error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key))
- end
- else
- _fn = function(...)
- return vim.call(key, ...)
- end
- end
- t[key] = _fn
- return _fn
- end
-})
-
-vim.funcref = function(viml_func_name)
- return vim.fn[viml_func_name]
-end
-
--- An easier alias for commands.
-vim.cmd = function(command)
- return vim.api.nvim_exec(command, false)
-end
-
--- These are the vim.env/v/g/o/bo/wo variable magic accessors.
-do
- local validate = vim.validate
-
- --@private
- local function make_dict_accessor(scope, handle)
- validate {
- scope = {scope, 's'};
- }
- local mt = {}
- function mt:__newindex(k, v)
- return vim._setvar(scope, handle or 0, k, v)
- end
- function mt:__index(k)
- if handle == nil and type(k) == 'number' then
- return make_dict_accessor(scope, k)
- end
- return vim._getvar(scope, handle or 0, k)
- end
- return setmetatable({}, mt)
- end
-
- vim.g = make_dict_accessor('g', false)
- vim.v = make_dict_accessor('v', false)
- vim.b = make_dict_accessor('b')
- vim.w = make_dict_accessor('w')
- vim.t = make_dict_accessor('t')
-end
-
---- Get a table of lines with start, end columns for a region marked by two points
----
----@param bufnr number of buffer
----@param pos1 (line, column) tuple marking beginning of region
----@param pos2 (line, column) tuple marking end of region
----@param regtype type of selection (:help setreg)
----@param inclusive boolean indicating whether the selection is end-inclusive
----@return region lua table of the form {linenr = {startcol,endcol}}
-function vim.region(bufnr, pos1, pos2, regtype, inclusive)
- if not vim.api.nvim_buf_is_loaded(bufnr) then
- vim.fn.bufload(bufnr)
- end
-
- -- check that region falls within current buffer
- local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
- pos1[1] = math.min(pos1[1], buf_line_count - 1)
- pos2[1] = math.min(pos2[1], buf_line_count - 1)
-
- -- in case of block selection, columns need to be adjusted for non-ASCII characters
- -- TODO: handle double-width characters
- local bufline
- if regtype:byte() == 22 then
- bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1]
- pos1[2] = vim.str_utfindex(bufline, pos1[2])
- end
-
- local region = {}
- for l = pos1[1], pos2[1] do
- local c1, c2
- if regtype:byte() == 22 then -- block selection: take width from regtype
- c1 = pos1[2]
- c2 = c1 + regtype:sub(2)
- -- and adjust for non-ASCII characters
- bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1]
- if c1 < #bufline then
- c1 = vim.str_byteindex(bufline, c1)
- end
- if c2 < #bufline then
- c2 = vim.str_byteindex(bufline, c2)
- end
- else
- c1 = (l == pos1[1]) and (pos1[2]) or 0
- c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1
- end
- table.insert(region, l, {c1, c2})
- end
- return region
-end
-
---- Defers calling `fn` until `timeout` ms passes.
----
---- Use to do a one-shot timer that calls `fn`
---- Note: The {fn} is |schedule_wrap|ped automatically, so API functions are
---- safe to call.
----@param fn Callback to call once `timeout` expires
----@param timeout Number of milliseconds to wait before calling `fn`
----@return timer luv timer object
-function vim.defer_fn(fn, timeout)
- vim.validate { fn = { fn, 'c', true}; }
- local timer = vim.loop.new_timer()
- timer:start(timeout, 0, vim.schedule_wrap(function()
- timer:stop()
- timer:close()
-
- fn()
- end))
-
- return timer
-end
-
-
---- Display a notification to the user.
----
---- This function can be overridden by plugins to display notifications using a
---- custom provider (such as the system notification provider). By default,
---- writes to |:messages|.
----
----@param msg string Content of the notification to show to the user.
----@param level number|nil One of the values from |vim.log.levels|.
----@param opts table|nil Optional parameters. Unused by default.
-function vim.notify(msg, level, opts) -- luacheck: no unused args
- if level == vim.log.levels.ERROR then
- vim.api.nvim_err_writeln(msg)
- elseif level == vim.log.levels.WARN then
- vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {})
- else
- vim.api.nvim_echo({{msg}}, true, {})
- end
-end
-
-do
- local notified = {}
-
- --- Display a notification only one time.
- ---
- --- Like |vim.notify()|, but subsequent calls with the same message will not
- --- display a notification.
- ---
- ---@param msg string Content of the notification to show to the user.
- ---@param level number|nil One of the values from |vim.log.levels|.
- ---@param opts table|nil Optional parameters. Unused by default.
- function vim.notify_once(msg, level, opts) -- luacheck: no unused args
- if not notified[msg] then
- vim.notify(msg, level, opts)
- notified[msg] = true
- end
- end
-end
-
----@private
-function vim.register_keystroke_callback()
- error('vim.register_keystroke_callback is deprecated, instead use: vim.on_key')
-end
-
-local on_key_cbs = {}
-
---- Adds Lua function {fn} with namespace id {ns_id} as a listener to every,
---- yes every, input key.
----
---- The Nvim command-line option |-w| is related but does not support callbacks
---- and cannot be toggled dynamically.
----
----@param fn function: Callback function. It should take one string argument.
---- On each key press, Nvim passes the key char to fn(). |i_CTRL-V|
---- If {fn} is nil, it removes the callback for the associated {ns_id}
----@param ns_id number? Namespace ID. If nil or 0, generates and returns a new
---- |nvim_create_namespace()| id.
----
----@return number Namespace id associated with {fn}. Or count of all callbacks
----if on_key() is called without arguments.
----
----@note {fn} will be removed if an error occurs while calling.
----@note {fn} will not be cleared by |nvim_buf_clear_namespace()|
----@note {fn} will receive the keys after mappings have been evaluated
-function vim.on_key(fn, ns_id)
- if fn == nil and ns_id == nil then
- return #on_key_cbs
- end
-
- vim.validate {
- fn = { fn, 'c', true},
- ns_id = { ns_id, 'n', true }
- }
-
- if ns_id == nil or ns_id == 0 then
- ns_id = vim.api.nvim_create_namespace('')
- end
-
- on_key_cbs[ns_id] = fn
- return ns_id
-end
-
---- Executes the on_key callbacks.
----@private
-function vim._on_key(char)
- local failed_ns_ids = {}
- local failed_messages = {}
- for k, v in pairs(on_key_cbs) do
- local ok, err_msg = pcall(v, char)
- if not ok then
- vim.on_key(nil, k)
- table.insert(failed_ns_ids, k)
- table.insert(failed_messages, err_msg)
- end
- end
-
- if failed_ns_ids[1] then
- error(string.format(
- "Error executing 'on_key' with ns_ids '%s'\n Messages: %s",
- table.concat(failed_ns_ids, ", "),
- table.concat(failed_messages, "\n")))
- end
-end
-
---- Generate a list of possible completions for the string.
---- String starts with ^ and then has the pattern.
----
---- 1. Can we get it to just return things in the global namespace with that name prefix
---- 2. Can we get it to return things from global namespace even with `print(` in front.
-function vim._expand_pat(pat, env)
- env = env or _G
-
- pat = string.sub(pat, 2, #pat)
-
- if pat == '' then
- local result = vim.tbl_keys(env)
- table.sort(result)
- return result, 0
- end
-
- -- TODO: We can handle spaces in [] ONLY.
- -- We should probably do that at some point, just for cooler completion.
- -- TODO: We can suggest the variable names to go in []
- -- This would be difficult as well.
- -- Probably just need to do a smarter match than just `:match`
-
- -- Get the last part of the pattern
- local last_part = pat:match("[%w.:_%[%]'\"]+$")
- if not last_part then return {}, 0 end
-
- local parts, search_index = vim._expand_pat_get_parts(last_part)
-
- local match_part = string.sub(last_part, search_index, #last_part)
- local prefix_match_pat = string.sub(pat, 1, #pat - #match_part) or ''
-
- local final_env = env
-
- for _, part in ipairs(parts) do
- if type(final_env) ~= 'table' then
- return {}, 0
- end
- local key
-
- -- Normally, we just have a string
- -- Just attempt to get the string directly from the environment
- if type(part) == "string" then
- key = part
- else
- -- However, sometimes you want to use a variable, and complete on it
- -- With this, you have the power.
-
- -- MY_VAR = "api"
- -- vim[MY_VAR]
- -- -> _G[MY_VAR] -> "api"
- local result_key = part[1]
- if not result_key then
- return {}, 0
- end
-
- local result = rawget(env, result_key)
-
- if result == nil then
- return {}, 0
- end
-
- key = result
- end
- local field = rawget(final_env, key)
- if field == nil then
- local mt = getmetatable(final_env)
- if mt and type(mt.__index) == "table" then
- field = rawget(mt.__index, key)
- end
- end
- final_env = field
-
- if not final_env then
- return {}, 0
- end
- end
-
- local keys = {}
- ---@private
- local function insert_keys(obj)
- for k,_ in pairs(obj) do
- if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then
- table.insert(keys,k)
- end
- end
- end
-
- if type(final_env) == "table" then
- insert_keys(final_env)
- end
- local mt = getmetatable(final_env)
- if mt and type(mt.__index) == "table" then
- insert_keys(mt.__index)
- end
-
- table.sort(keys)
-
- return keys, #prefix_match_pat
-end
-
-vim._expand_pat_get_parts = function(lua_string)
- local parts = {}
-
- local accumulator, search_index = '', 1
- local in_brackets, bracket_end = false, -1
- local string_char = nil
- for idx = 1, #lua_string do
- local s = lua_string:sub(idx, idx)
-
- if not in_brackets and (s == "." or s == ":") then
- table.insert(parts, accumulator)
- accumulator = ''
-
- search_index = idx + 1
- elseif s == "[" then
- in_brackets = true
-
- table.insert(parts, accumulator)
- accumulator = ''
-
- search_index = idx + 1
- elseif in_brackets then
- if idx == bracket_end then
- in_brackets = false
- search_index = idx + 1
-
- if string_char == "VAR" then
- table.insert(parts, { accumulator })
- accumulator = ''
-
- string_char = nil
- end
- elseif not string_char then
- bracket_end = string.find(lua_string, ']', idx, true)
-
- if s == '"' or s == "'" then
- string_char = s
- elseif s ~= ' ' then
- string_char = "VAR"
- accumulator = s
- end
- elseif string_char then
- if string_char ~= s then
- accumulator = accumulator .. s
- else
- table.insert(parts, accumulator)
- accumulator = ''
-
- string_char = nil
- end
- end
- else
- accumulator = accumulator .. s
- end
- end
-
- parts = vim.tbl_filter(function(val) return #val > 0 end, parts)
-
- return parts, search_index
-end
-
----Prints given arguments in human-readable format.
----Example:
----<pre>
---- -- Print highlight group Normal and store it's contents in a variable.
---- local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
----</pre>
----@see |vim.inspect()|
----@return given arguments.
-function vim.pretty_print(...)
- local objects = {}
- for i = 1, select('#', ...) do
- local v = select(i, ...)
- table.insert(objects, vim.inspect(v))
- end
-
- print(table.concat(objects, ' '))
- return ...
-end
-
-
-require('vim._meta')
-
-return vim
diff --git a/src/nvim/main.c b/src/nvim/main.c
index ae25ff63dd..7281809c06 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -823,7 +823,6 @@ static void command_line_scan(mparm_T *parmp)
bool had_stdin_file = false; // found explicit "-" argument
bool had_minmin = false; // found "--" argument
int want_argument; // option argument with argument
- int c;
long n;
argc--;
@@ -845,7 +844,7 @@ static void command_line_scan(mparm_T *parmp)
// Optional argument.
} else if (argv[0][0] == '-' && !had_minmin) {
want_argument = false;
- c = argv[0][argv_idx++];
+ char c = argv[0][argv_idx++];
switch (c) {
case NUL: // "nvim -" read from stdin
if (exmode_active) {
diff --git a/src/nvim/map.c b/src/nvim/map.c
index 091d653046..4e39eb8c07 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -178,6 +178,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0)
MAP_IMPL(String, int, DEFAULT_INITIALIZER)
+MAP_IMPL(int, String, DEFAULT_INITIALIZER)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index c9c89bf2fd..00f72386a7 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -47,6 +47,7 @@ MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int)
MAP_DECLS(String, handle_T)
MAP_DECLS(String, int)
+MAP_DECLS(int, String)
MAP_DECLS(ColorKey, ColorItem)
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 918db8b76c..d2354dbf6b 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -558,8 +558,8 @@ void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_
{
// TODO(bfredl): clean up this mess and re-instantiate &= and |= forms
// once we upgrade to a non-broken version of gcc in functionaltest-lua CI
- rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK);
- rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags
+ rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK);
+ rawkey(itr).flags = (uint16_t)(rawkey(itr).flags
| (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET)
| (uint16_t)(key.flags & MT_FLAG_DECOR_MASK));
rawkey(itr).decor_full = key.decor_full;
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 30f5aacebc..440816930b 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -87,6 +87,11 @@ static inline bool mt_end(mtkey_t key)
return key.flags & MT_FLAG_END;
}
+static inline bool mt_start(mtkey_t key)
+{
+ return mt_paired(key) && !mt_end(key);
+}
+
static inline bool mt_right(mtkey_t key)
{
return key.flags & MT_FLAG_RIGHT_GRAVITY;
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 004ef36b36..59f57aa667 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -303,7 +303,7 @@ int ml_open(buf_T *buf)
b0p->b0_id[0] = BLOCK0_ID0;
b0p->b0_id[1] = BLOCK0_ID1;
- b0p->b0_magic_long = (long)B0_MAGIC_LONG;
+ b0p->b0_magic_long = B0_MAGIC_LONG;
b0p->b0_magic_int = (int)B0_MAGIC_INT;
b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
b0p->b0_magic_char = B0_MAGIC_CHAR;
@@ -3709,7 +3709,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
static int b0_magic_wrong(ZERO_BL *b0p)
{
- return b0p->b0_magic_long != (long)B0_MAGIC_LONG
+ return b0p->b0_magic_long != B0_MAGIC_LONG
|| b0p->b0_magic_int != (int)B0_MAGIC_INT
|| b0p->b0_magic_short != (short)B0_MAGIC_SHORT
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index b5c7020dee..23db7fe5a3 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1023,6 +1023,60 @@ static int stuff_yank(int regname, char_u *p)
static int execreg_lastc = NUL;
+/// When executing a register as a series of ex-commands, if the
+/// line-continuation character is used for a line, then join it with one or
+/// more previous lines. Note that lines are processed backwards starting from
+/// the last line in the register.
+///
+/// @param lines list of lines in the register
+/// @param idx index of the line starting with \ or "\. Join this line with all the immediate
+/// predecessor lines that start with a \ and the first line that doesn't start
+/// with a \. Lines that start with a comment "\ character are ignored.
+/// @returns the concatenated line. The index of the line that should be
+/// processed next is returned in idx.
+static char_u *execreg_line_continuation(char_u **lines, size_t *idx)
+{
+ size_t i = *idx;
+ assert(i > 0);
+ const size_t cmd_end = i;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char_u), 400);
+
+ char_u *p;
+
+ // search backwards to find the first line of this command.
+ // Any line not starting with \ or "\ is the start of the
+ // command.
+ while (--i > 0) {
+ p = skipwhite(lines[i]);
+ if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) {
+ break;
+ }
+ }
+ const size_t cmd_start = i;
+
+ // join all the lines
+ ga_concat(&ga, (char *)lines[cmd_start]);
+ for (size_t j = cmd_start + 1; j <= cmd_end; j++) {
+ p = skipwhite(lines[j]);
+ if (*p == '\\') {
+ // Adjust the growsize to the current length to
+ // speed up concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
+ }
+ ga_concat(&ga, (char *)(p + 1));
+ }
+ }
+ ga_append(&ga, NUL);
+ char_u *str = vim_strsave(ga.ga_data);
+ ga_clear(&ga);
+
+ *idx = i;
+ return str;
+}
+
/// Execute a yank register: copy it into the stuff buffer
///
/// @param colon insert ':' before each line
@@ -1111,7 +1165,21 @@ int do_execreg(int regname, int colon, int addcr, int silent)
return FAIL;
}
}
- escaped = vim_strsave_escape_ks(reg->y_array[i]);
+
+ // Handle line-continuation for :@<register>
+ char_u *str = reg->y_array[i];
+ bool free_str = false;
+ if (colon && i > 0) {
+ p = skipwhite(str);
+ if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) {
+ str = execreg_line_continuation(reg->y_array, &i);
+ free_str = true;
+ }
+ }
+ escaped = vim_strsave_escape_ks(str);
+ if (free_str) {
+ xfree(str);
+ }
retval = ins_typebuf(escaped, remap, 0, true, silent);
xfree(escaped);
if (retval == FAIL) {
@@ -1504,20 +1572,20 @@ int op_delete(oparg_T *oap)
yankreg_T *reg = NULL;
int did_yank = false;
if (oap->regname != 0) {
- // yank without message
- did_yank = op_yank(oap, false, true);
- if (!did_yank) {
- // op_yank failed, don't do anything
+ // check for read-only register
+ if (!valid_yank_reg(oap->regname, true)) {
+ beep_flush();
return OK;
}
+ reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg
+ op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message
+ did_yank = true;
}
- /*
- * Put deleted text into register 1 and shift number registers if the
- * delete contains a line break, or when a regname has been specified.
- */
- if (oap->regname != 0 || oap->motion_type == kMTLineWise
- || oap->line_count > 1 || oap->use_reg_one) {
+ // Put deleted text into register 1 and shift number registers if the
+ // delete contains a line break, or when using a specific operator (Vi
+ // compatible)
+ if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname));
reg = &y_regs[1];
op_yank_reg(oap, false, reg, false);
@@ -2579,12 +2647,12 @@ void free_register(yankreg_T *reg)
/// Yanks the text between "oap->start" and "oap->end" into a yank register.
/// If we are to append (uppercase register), we first yank into a new yank
/// register and then concatenate the old and the new one.
+/// Do not call this from a delete operation. Use op_yank_reg() instead.
///
/// @param oap operator arguments
/// @param message show message when more than `&report` lines are yanked.
-/// @param deleting whether the function was called from a delete operation.
/// @returns whether the operation register was writable.
-bool op_yank(oparg_T *oap, bool message, int deleting)
+bool op_yank(oparg_T *oap, bool message)
FUNC_ATTR_NONNULL_ALL
{
// check for read-only register
@@ -2598,11 +2666,8 @@ bool op_yank(oparg_T *oap, bool message, int deleting)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
- // op_delete will set_clipboard and do_autocmd
- if (!deleting) {
- set_clipboard(oap->regname, reg);
- do_autocmd_textyankpost(oap, reg);
- }
+ set_clipboard(oap->regname, reg);
+ do_autocmd_textyankpost(oap, reg);
return true;
}
@@ -6630,7 +6695,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
} else {
curwin->w_p_lbr = lbr_saved;
oap->excl_tr_ws = cap->cmdchar == 'z';
- (void)op_yank(oap, !gui_yank, false);
+ (void)op_yank(oap, !gui_yank);
}
check_cursor_col();
break;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 9068c90dd1..a0706dfa14 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3851,7 +3851,7 @@ static bool parse_winhl_opt(win_T *wp)
if (strncmp("Normal", p, nlen) == 0) {
w_hl_id_normal = hl_id;
} else {
- for (hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
+ for (hlf = 0; hlf < HLF_COUNT; hlf++) {
if (strlen(hlf_names[hlf]) == nlen
&& strncmp(hlf_names[hlf], p, nlen) == 0) {
w_hl_ids[hlf] = hl_id;
@@ -5267,7 +5267,7 @@ static void showoptions(int all, int opt_flags)
&& Columns + GAP >= INT_MIN + 3
&& (Columns + GAP - 3) / INC >= INT_MIN
&& (Columns + GAP - 3) / INC <= INT_MAX);
- cols = (int)((Columns + GAP - 3) / INC);
+ cols = (Columns + GAP - 3) / INC;
if (cols == 0) {
cols = 1;
}
@@ -5666,11 +5666,11 @@ void comp_col(void)
assert(sc_col >= 0
&& INT_MIN + sc_col <= Columns
&& Columns - sc_col <= INT_MAX);
- sc_col = (int)(Columns - sc_col);
+ sc_col = Columns - sc_col;
assert(ru_col >= 0
&& INT_MIN + ru_col <= Columns
&& Columns - ru_col <= INT_MAX);
- ru_col = (int)(Columns - ru_col);
+ ru_col = Columns - ru_col;
if (sc_col <= 0) { // screen too narrow, will become a mess
sc_col = 1;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 6a6c915094..b7f11b2de0 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -468,6 +468,8 @@ static int toggle_Magic(int x)
#define EMSG_RET_FAIL(m) return (emsg(m), rc_did_emsg = true, FAIL)
#define EMSG2_RET_NULL(m, c) \
return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL)
+#define EMSG3_RET_NULL(m, c, a) \
+ return (semsg((const char *)(m), (c) ? "" : "\\", (a)), rc_did_emsg = true, (void *)NULL)
#define EMSG2_RET_FAIL(m, c) \
return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL)
#define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \
@@ -1762,13 +1764,9 @@ static char_u *regpiece(int *flagp)
if (re_multi_type(peekchr()) != NOT_MULTI) {
// Can't have a multi follow a multi.
if (peekchr() == Magic('*')) {
- snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"),
- reg_magic >= MAGIC_ON ? "" : "\\");
- } else {
- snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"),
- reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr()));
+ EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON);
}
- EMSG_RET_NULL((char *)IObuff);
+ EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr()));
}
return ret;
@@ -1927,10 +1925,8 @@ static char_u *regatom(int *flagp)
case Magic('{'):
case Magic('*'):
c = no_Magic(c);
- snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"),
- (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL)
- ? "" : "\\", c);
- EMSG_RET_NULL((char *)IObuff);
+ EMSG3_RET_NULL(_("E64: %s%c follows nothing"),
+ (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c);
// NOTREACHED
case Magic('~'): /* previous substitute pattern */
@@ -2537,7 +2533,9 @@ do_multibyte:
static bool re_mult_next(char *what)
{
if (re_multi_type(peekchr()) == MULTI_MULT) {
- EMSG2_RET_FAIL(_("E888: (NFA regexp) cannot repeat %s"), what);
+ semsg(_("E888: (NFA regexp) cannot repeat %s"), what);
+ rc_did_emsg = true;
+ return false;
}
return true;
}
@@ -3149,9 +3147,7 @@ static int read_limits(long *minval, long *maxval)
regparse++; // Allow either \{...} or \{...\}
}
if (*regparse != '}') {
- snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"),
- reg_magic == MAGIC_ALL ? "" : "\\");
- EMSG_RET_FAIL((char *)IObuff);
+ EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL);
}
/*
@@ -5043,8 +5039,7 @@ static bool regmatch(
} else {
MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr);
if (limit > 0
- && (long)(behind_pos.rs_u.ptr
- - rp->rs_un.regsave.rs_u.ptr) > limit) {
+ && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) {
no = FAIL;
}
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 7fafe3dd6e..1cdee7c972 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2208,6 +2208,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
buf_T *buf = wp->w_buffer;
bool end_fill = (lnum == buf->b_ml.ml_line_count+1);
+ has_decor = decor_redraw_line(buf, lnum-1, &decor_state);
+
if (!number_only) {
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
@@ -2229,9 +2231,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- has_decor = decor_redraw_line(wp->w_buffer, lnum-1,
- &decor_state);
-
for (size_t k = 0; k < kv_size(*providers); k++) {
DecorProvider *p = kv_A(*providers, k);
if (p && p->redraw_line != LUA_NOREF) {
@@ -2460,6 +2459,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
memset(sattrs, 0, sizeof(sattrs));
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
+ if (decor_state.has_sign_decor) {
+ decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs);
+ }
// If this line has a sign with line highlighting set line_attr.
// TODO(bfredl, vigoux): this should not take priority over decoration!
@@ -2888,9 +2890,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
} else if (wp->w_p_cul
&& lnum == wp->w_cursor.lnum
&& (wp->w_p_culopt_flags & CULOPT_NBR)
- && (row == startrow
- || wp->w_p_culopt_flags & CULOPT_LINE)
- && filler_todo == 0) {
+ && (row == startrow + filler_lines
+ || (row > startrow + filler_lines
+ && wp->w_p_culopt_flags & CULOPT_LINE))) {
// When 'cursorline' is set highlight the line number of
// the current line differently.
// When 'cursorlineopt' has "screenline" only highlight
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 682fa417a9..e6b47e75b2 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -1071,7 +1071,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
* Find out the direction of the search.
*/
if (dirc == 0) {
- dirc = spats[0].off.dir;
+ dirc = (char_u)spats[0].off.dir;
} else {
spats[0].off.dir = dirc;
set_vv_searchforward();
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 8b41781c98..6d0ac30003 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -196,7 +196,8 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
if (next != NULL) {
next->se_prev = newsign;
}
- buf->b_signcols_valid = false;
+
+ buf_signcols_add_check(buf, newsign);
if (prev == NULL) {
// When adding first sign need to redraw the windows to create the
@@ -506,6 +507,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
if (sp->sn_num_hl != 0) {
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
}
+ // Store the priority so we can mesh in extmark signs later
+ sattr.sat_prio = sign->se_priority;
}
sattrs[nr_matches] = sattr;
@@ -539,7 +542,6 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
sign_entry_T *next; // the next sign in a b_signlist
linenr_T lnum; // line number whose sign was deleted
- buf->b_signcols_valid = false;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
@@ -552,6 +554,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
next->se_prev = sign->se_prev;
}
lnum = sign->se_lnum;
+ buf_signcols_del_check(buf, lnum, lnum);
if (sign->se_group != NULL) {
sign_group_unref(sign->se_group->sg_name);
}
@@ -673,7 +676,7 @@ void buf_delete_signs(buf_T *buf, char_u *group)
lastp = &sign->se_next;
}
}
- buf->b_signcols_valid = false;
+ buf_signcols_del_check(buf, 1, MAXLNUM);
}
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
@@ -735,14 +738,19 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
int is_fixed = 0;
int signcol = win_signcol_configured(curwin, &is_fixed);
- curbuf->b_signcols_valid = false;
+ bool delete = amount == MAXLNUM;
+
+ if (delete) {
+ buf_signcols_del_check(curbuf, line1, line2);
+ }
+
lastp = &curbuf->b_signlist;
for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
next = sign->se_next;
new_lnum = sign->se_lnum;
if (sign->se_lnum >= line1 && sign->se_lnum <= line2) {
- if (amount != MAXLNUM) {
+ if (!delete) {
new_lnum += amount;
} else if (!is_fixed || signcol >= 2) {
*lastp = next;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index c734502878..6d28c1849a 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -40,6 +40,7 @@ typedef struct sign_attrs_S {
int sat_linehl;
int sat_culhl;
int sat_numhl;
+ int sat_prio; // Used for inserting extmark signs
} sign_attrs_T;
#define SIGN_SHOW_MAX 9
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 1296d410f6..fb644daa39 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3824,7 +3824,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// At end of a prefix or at start of prefixtree: check for
// following word.
- if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) {
+ if (byts[arridx] == 0 || n == STATE_NOPREFIX) {
// Set su->su_badflags to the caps type at this position.
// Use the caps type until here for the prefix itself.
n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 291138ef23..9d4c64e4b1 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -354,7 +354,7 @@ char *strcase_save(const char *const orig, bool upper)
int l = utf_ptr2len((const char_u *)p);
if (c == 0) {
// overlong sequence, use only the first byte
- c = *p;
+ c = (char_u)(*p);
l = 1;
}
int uc = upper ? mb_toupper(c) : mb_tolower(c);
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index db10b71d38..54fce3d968 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -121,11 +121,11 @@ static int include_link = 0; // when 2 include "nvim/link" and "clear"
/// The "term", "cterm" and "gui" arguments can be any combination of the
/// following names, separated by commas (but no spaces!).
static char *(hl_name_table[]) =
-{ "bold", "standout", "underline", "undercurl",
- "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" };
+{ "bold", "standout", "underline", "underlineline", "undercurl", "underdot",
+ "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" };
static int hl_attr_table[] =
-{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE,
- HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
+{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH,
+ HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
static char e_illegal_arg[] = N_("E390: Illegal argument: %s");
@@ -4208,7 +4208,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
p = flagtab[fidx].name;
int i;
for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) {
- if (arg[len] != p[i] && arg[len] != p[i + 1]) {
+ if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) {
break;
}
}
diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim
index 53ed4617f7..4229095f9f 100644
--- a/src/nvim/testdir/test_autochdir.vim
+++ b/src/nvim/testdir/test_autochdir.vim
@@ -26,6 +26,54 @@ func Test_set_filename()
call delete('samples/Xtest')
endfunc
+func Test_set_filename_other_window()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ call test_autochdir()
+ call mkdir('Xa')
+ call mkdir('Xb')
+ call mkdir('Xc')
+ try
+ args Xa/aaa.txt Xb/bbb.txt
+ set acd
+ let winid = win_getid()
+ snext
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt')
+ call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', ''))
+ finally
+ set noacd
+ call chdir(cwd)
+ call delete('Xa', 'rf')
+ call delete('Xb', 'rf')
+ call delete('Xc', 'rf')
+ bwipe! aaa.txt
+ bwipe! bbb.txt
+ bwipe! ccc.txt
+ endtry
+endfunc
+
+func Test_acd_win_execute()
+ CheckFunction test_autochdir
+ let cwd = getcwd()
+ set acd
+ call test_autochdir()
+
+ call mkdir('Xfile')
+ let winid = win_getid()
+ new Xfile/file
+ call assert_match('testdir.Xfile$', getcwd())
+ cd ..
+ call assert_match('testdir$', getcwd())
+ call win_execute(winid, 'echo')
+ call assert_match('testdir$', getcwd())
+
+ bwipe!
+ set noacd
+ call chdir(cwd)
+ call delete('Xfile', 'rf')
+endfunc
+
func Test_verbose_pwd()
CheckFunction test_autochdir
let cwd = getcwd()
@@ -42,20 +90,27 @@ func Test_verbose_pwd()
set acd
wincmd w
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
- execute 'lcd' cwd
- call assert_match('\[window\].*testdir$', execute('verbose pwd'))
execute 'tcd' cwd
call assert_match('\[tabpage\].*testdir$', execute('verbose pwd'))
execute 'cd' cwd
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
+ execute 'lcd' cwd
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
edit
call assert_match('\[autochdir\].*testdir$', execute('verbose pwd'))
+ enew
+ wincmd w
+ call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
+ wincmd w
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
set noacd
call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd'))
wincmd w
- call assert_match('\[global\].*testdir', execute('verbose pwd'))
+ call assert_match('\[window\].*testdir$', execute('verbose pwd'))
+ execute 'cd' cwd
+ call assert_match('\[global\].*testdir$', execute('verbose pwd'))
wincmd w
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd'))
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 482d39056f..10eb979b45 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -1017,6 +1017,32 @@ func Test_diff_with_cursorline()
call delete('Xtest_diff_cursorline')
endfunc
+func Test_diff_with_cursorline_number()
+ CheckScreendump
+
+ let lines =<< trim END
+ hi CursorLine ctermbg=red ctermfg=white
+ hi CursorLineNr ctermbg=white ctermfg=black cterm=underline
+ set cursorline number
+ call setline(1, ["baz", "foo", "foo", "bar"])
+ 2
+ vnew
+ call setline(1, ["foo", "foo", "bar"])
+ windo diffthis
+ 1wincmd w
+ END
+ call writefile(lines, 'Xtest_diff_cursorline_number')
+ let buf = RunVimInTerminal('-S Xtest_diff_cursorline_number', {})
+
+ call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {})
+ call term_sendkeys(buf, ":set cursorlineopt=number\r")
+ call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('Xtest_diff_cursorline_number')
+endfunc
+
func Test_diff_with_cursorline_breakindent()
CheckScreendump
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 23e39eba35..f78b748d71 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -62,7 +62,6 @@ func Test_display_registers()
call assert_match('^\nType Name Content\n'
\ . ' c "" a\n'
\ . ' c "0 ba\n'
- \ . ' c "1 b\n'
\ . ' c "a b\n'
\ . '.*'
\ . ' c "- a\n'
@@ -85,6 +84,90 @@ func Test_display_registers()
let g:clipboard = save_clipboard
endfunc
+func Test_register_one()
+ " delete a line goes into register one
+ new
+ call setline(1, "one")
+ normal dd
+ call assert_equal("one\n", @1)
+
+ " delete a word does not change register one, does change "-
+ call setline(1, "two")
+ normal de
+ call assert_equal("one\n", @1)
+ call assert_equal("two", @-)
+
+ " delete a word with a register does not change register one
+ call setline(1, "three")
+ normal "ade
+ call assert_equal("three", @a)
+ call assert_equal("one\n", @1)
+
+ " delete a word with register DOES change register one with one of a list of
+ " operators
+ " %
+ call setline(1, ["(12)3"])
+ normal "ad%
+ call assert_equal("(12)", @a)
+ call assert_equal("(12)", @1)
+
+ " (
+ call setline(1, ["first second"])
+ normal $"ad(
+ call assert_equal("first secon", @a)
+ call assert_equal("first secon", @1)
+
+ " )
+ call setline(1, ["First Second."])
+ normal gg0"ad)
+ call assert_equal("First Second.", @a)
+ call assert_equal("First Second.", @1)
+
+ " `
+ call setline(1, ["start here."])
+ normal gg0fhmx0"ad`x
+ call assert_equal("start ", @a)
+ call assert_equal("start ", @1)
+
+ " /
+ call setline(1, ["searchX"])
+ exe "normal gg0\"ad/X\<CR>"
+ call assert_equal("search", @a)
+ call assert_equal("search", @1)
+
+ " ?
+ call setline(1, ["Ysearch"])
+ exe "normal gg$\"ad?Y\<CR>"
+ call assert_equal("Ysearc", @a)
+ call assert_equal("Ysearc", @1)
+
+ " n
+ call setline(1, ["Ynext"])
+ normal gg$"adn
+ call assert_equal("Ynex", @a)
+ call assert_equal("Ynex", @1)
+
+ " N
+ call setline(1, ["prevY"])
+ normal gg0"adN
+ call assert_equal("prev", @a)
+ call assert_equal("prev", @1)
+
+ " }
+ call setline(1, ["one", ""])
+ normal gg0"ad}
+ call assert_equal("one\n", @a)
+ call assert_equal("one\n", @1)
+
+ " {
+ call setline(1, ["", "two"])
+ normal 2G$"ad{
+ call assert_equal("\ntw", @a)
+ call assert_equal("\ntw", @1)
+
+ bwipe!
+endfunc
+
func Test_recording_status_in_ex_line()
norm qx
redraw!
@@ -482,6 +565,82 @@ func Test_v_register()
bwipe!
endfunc
+" Test for executing the contents of a register as an Ex command with line
+" continuation.
+func Test_execute_reg_as_ex_cmd()
+ " Line continuation with just two lines
+ let code =<< trim END
+ let l = [
+ \ 1]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1], l)
+
+ " Line continuation with more than two lines
+ let code =<< trim END
+ let l = [
+ \ 1,
+ \ 2,
+ \ 3]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2, 3], l)
+
+ " use comments interspersed with code
+ let code =<< trim END
+ let l = [
+ "\ one
+ \ 1,
+ "\ two
+ \ 2,
+ "\ three
+ \ 3]
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2, 3], l)
+
+ " use line continuation in the middle
+ let code =<< trim END
+ let a = "one"
+ let l = [
+ \ 1,
+ \ 2]
+ let b = "two"
+ END
+ let @r = code->join("\n")
+ let l = []
+ @r
+ call assert_equal([1, 2], l)
+ call assert_equal("one", a)
+ call assert_equal("two", b)
+
+ " only one line with a \
+ let @r = "\\let l = 1"
+ call assert_fails('@r', 'E10:')
+
+ " only one line with a "\
+ let @r = ' "\ let i = 1'
+ @r
+ call assert_false(exists('i'))
+
+ " first line also begins with a \
+ let @r = "\\let l = [\n\\ 1]"
+ call assert_fails('@r', 'E10:')
+
+ " Test with a large number of lines
+ let @r = "let str = \n"
+ let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312)
+ let @r ..= ' \ ""'
+ @r
+ call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str)
+endfunc
+
func Test_ve_blockpaste()
new
set ve=all
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 58061f020d..1ad82b7290 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -528,7 +528,7 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
return a1.cterm_fg_color != a2.cterm_fg_color
|| a1.cterm_bg_color != a2.cterm_bg_color
|| a1.cterm_ae_attr != a2.cterm_ae_attr
- || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)
+ || (a1.cterm_ae_attr & HL_ANY_UNDERLINE
&& a1.rgb_sp_color != a2.rgb_sp_color);
}
}
@@ -552,15 +552,27 @@ static void update_attrs(UI *ui, int attr_id)
bool strikethrough = attr & HL_STRIKETHROUGH;
bool underline;
+ bool underlineline;
bool undercurl;
+ bool underdot;
+ bool underdash;
if (data->unibi_ext.set_underline_style != -1) {
underline = attr & HL_UNDERLINE;
+ underlineline = attr & HL_UNDERLINELINE;
undercurl = attr & HL_UNDERCURL;
+ underdash = attr & HL_UNDERDASH;
+ underdot = attr & HL_UNDERDOT;
} else {
- underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL);
+ underline = attr & HL_ANY_UNDERLINE;
+ underlineline = false;
undercurl = false;
+ underdot = false;
+ underdash = false;
}
+ bool has_any_underline = undercurl || underline
+ || underdot || underdash || underlineline;
+
if (unibi_get_str(data->ut, unibi_set_attributes)) {
if (bold || reverse || underline || standout) {
UNIBI_SET_NUM_VAR(data->params[0], standout);
@@ -599,11 +611,24 @@ static void update_attrs(UI *ui, int attr_id)
if (strikethrough && data->unibi_ext.enter_strikethrough_mode != -1) {
unibi_out_ext(ui, data->unibi_ext.enter_strikethrough_mode);
}
+ if (underlineline && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 2);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
if (undercurl && data->unibi_ext.set_underline_style != -1) {
UNIBI_SET_NUM_VAR(data->params[0], 3);
unibi_out_ext(ui, data->unibi_ext.set_underline_style);
}
- if ((undercurl || underline) && data->unibi_ext.set_underline_color != -1) {
+ if (underdot && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 4);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
+ if (underdash && data->unibi_ext.set_underline_style != -1) {
+ UNIBI_SET_NUM_VAR(data->params[0], 5);
+ unibi_out_ext(ui, data->unibi_ext.set_underline_style);
+ }
+
+ if (has_any_underline && data->unibi_ext.set_underline_color != -1) {
int color = attrs.rgb_sp_color;
if (color != -1) {
UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red
@@ -652,13 +677,13 @@ static void update_attrs(UI *ui, int attr_id)
data->default_attr = fg == -1 && bg == -1
- && !bold && !italic && !underline && !undercurl && !reverse && !standout
+ && !bold && !italic && !has_any_underline && !reverse && !standout
&& !strikethrough;
// Non-BCE terminals can't clear with non-default background color. Some BCE
// terminals don't support attributes either, so don't rely on it. But assume
// italic and bold has no effect if there is no text.
- data->can_clear_attr = !reverse && !standout && !underline && !undercurl
+ data->can_clear_attr = !reverse && !standout && !has_any_underline
&& !strikethrough && (data->bce || bg == -1);
}
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 31b9614c34..7c67c058b0 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -639,8 +639,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
}
} else {
// non-positive indicates no request
- wp->w_height_request = (int)MAX(height, 0);
- wp->w_width_request = (int)MAX(width, 0);
+ wp->w_height_request = MAX(height, 0);
+ wp->w_width_request = MAX(width, 0);
win_set_inner_size(wp);
}
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 83048d911f..d5299202b0 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6763,9 +6763,6 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display)
curwin = switchwin->sw_curwin;
curbuf = curwin->w_buffer;
}
- // If called by win_execute() and executing the command changed the
- // directory, it now has to be restored.
- fix_current_dir();
}
/// Make "buf" the current buffer.
diff --git a/src/nvim/window.h b/src/nvim/window.h
index e2fd2c515d..42701f72b4 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -5,6 +5,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/mark.h"
+#include "nvim/os/os.h"
// Values for file_name_in_line()
#define FNAME_MESS 1 // give error message
@@ -47,12 +48,36 @@ typedef struct {
do { \
win_T *const wp_ = (wp); \
const pos_T curpos_ = wp_->w_cursor; \
+ char_u cwd_[MAXPATHL]; \
+ char_u autocwd_[MAXPATHL]; \
+ bool apply_acd_ = false; \
+ int cwd_status_ = FAIL; \
+ /* Getting and setting directory can be slow on some systems, only do */ \
+ /* this when the current or target window/tab have a local directory or */ \
+ /* 'acd' is set. */ \
+ if (curwin != wp \
+ && (curwin->w_localdir != NULL || wp->w_localdir != NULL \
+ || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \
+ || p_acd)) { \
+ cwd_status_ = os_dirname(cwd_, MAXPATHL); \
+ } \
+ /* If 'acd' is set, check we are using that directory. If yes, then */ \
+ /* apply 'acd' afterwards, otherwise restore the current directory. */ \
+ if (cwd_status_ == OK && p_acd) { \
+ do_autochdir(); \
+ apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \
+ } \
switchwin_T switchwin_; \
if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \
check_cursor(); \
block; \
} \
restore_win_noblock(&switchwin_, true); \
+ if (apply_acd_) { \
+ do_autochdir(); \
+ } else if (cwd_status_ == OK) { \
+ os_chdir((char *)cwd_); \
+ } \
/* Update the status line if the cursor moved. */ \
if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \
wp_->w_redr_status = true; \