diff options
Diffstat (limited to 'src')
127 files changed, 1928 insertions, 1345 deletions
diff --git a/src/clint.py b/src/clint.py index a6649763c2..ee2d0ecc3c 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2244,6 +2244,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): r'(?<!\bkbtree_t)' r'(?<!\bkbitr_t)' r'(?<!\bPMap)' + r'(?<!\bSet)' r'(?<!\bArrayOf)' r'(?<!\bDictionaryOf)' r'(?<!\bDict)' diff --git a/src/klib/khash.h b/src/klib/khash.h index 57a41f9c13..eb1714c471 100644 --- a/src/klib/khash.h +++ b/src/klib/khash.h @@ -185,41 +185,46 @@ typedef khint_t khiter_t; #define __ac_HASH_UPPER 0.77 -#define __KHASH_TYPE(name, khkey_t, khval_t) \ +// This is only used for stack temporaries. Heap allocation is done with precise sizes. +#define KHASH_MAX_VAL_SIZE 32 + +#define __KHASH_TYPE(name, khkey_t) \ typedef struct { \ khint_t n_buckets, size, n_occupied, upper_bound; \ khint32_t *flags; \ khkey_t *keys; \ - khval_t *vals; \ + char *vals_buf; \ } kh_##name##_t; -#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ +#define __KHASH_PROTOTYPES(name, khkey_t) \ extern kh_##name##_t *kh_init_##name(void); \ extern void kh_dealloc_##name(kh_##name##_t *h); \ extern void kh_destroy_##name(kh_##name##_t *h); \ extern void kh_clear_##name(kh_##name##_t *h); \ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ - extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ - extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets, size_t val_size); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret, size_t val_size); \ extern void kh_del_##name(kh_##name##_t *h, khint_t x); -#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, \ - __hash_equal) \ +#define kh_bval(h, x) (&(h)->vals_buf[val_size*(x)]) +#define kh_copyval(to, from) memcpy(to, from, val_size) + +#define __KHASH_IMPL(name, SCOPE, khkey_t, __hash_func, __hash_equal) \ SCOPE kh_##name##_t *kh_init_##name(void) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t *)kcalloc(1, sizeof(kh_##name##_t)); \ } \ SCOPE void kh_dealloc_##name(kh_##name##_t *h) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE void kh_dealloc_##name(kh_##name##_t *h) \ { \ kfree(h->keys); \ kfree(h->flags); \ - kfree(h->vals); \ + kfree(h->vals_buf); \ } \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ @@ -228,7 +233,7 @@ typedef khint_t khiter_t; } \ } \ SCOPE void kh_clear_##name(kh_##name##_t *h) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ @@ -237,7 +242,7 @@ typedef khint_t khiter_t; } \ } \ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ @@ -257,9 +262,9 @@ typedef khint_t khiter_t; return 0; \ } \ } \ - SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ - REAL_FATTR_UNUSED; \ - SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets, size_t val_size) \ + REAL_FATTR_UNUSED; \ + SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets, size_t val_size) \ { /* This function uses 0.25*n_buckets bytes of working space instead of */ \ /* [sizeof(key_t+val_t)+.25]*n_buckets. */ \ khint32_t *new_flags = 0; \ @@ -280,23 +285,23 @@ typedef khint_t khiter_t; if (h->n_buckets < new_n_buckets) { /* expand */ \ khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ h->keys = new_keys; \ - if (kh_is_map) { \ - khval_t *new_vals = \ - (khval_t *)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ - h->vals = new_vals; \ + if (val_size) { \ + char *new_vals = krealloc( h->vals_buf, new_n_buckets * val_size); \ + h->vals_buf = new_vals; \ } \ } /* otherwise shrink */ \ } \ } \ + char cval[KHASH_MAX_VAL_SIZE]; \ + char ctmp[KHASH_MAX_VAL_SIZE]; \ if (j) { /* rehashing is needed */ \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ - khval_t val; \ khint_t new_mask; \ new_mask = new_n_buckets - 1; \ - if (kh_is_map) { \ - val = h->vals[j]; \ + if (val_size) { \ + kh_copyval(cval, kh_bval(h, j)); \ } \ __ac_set_isdel_true(h->flags, j); \ /* kick-out process; sort of like in Cuckoo hashing */ \ @@ -315,17 +320,17 @@ typedef khint_t khiter_t; h->keys[i] = key; \ key = tmp; \ } \ - if (kh_is_map) { \ - khval_t tmp = h->vals[i]; \ - h->vals[i] = val; \ - val = tmp; \ + if (val_size) { \ + kh_copyval(ctmp, kh_bval(h, i)); \ + kh_copyval(kh_bval(h, i), cval); \ + kh_copyval(cval, ctmp); \ } \ /* mark it as deleted in the old hash table */ \ __ac_set_isdel_true(h->flags, i); \ } else { /* write the element and jump out of the loop */ \ h->keys[i] = key; \ - if (kh_is_map) { \ - h->vals[i] = val; \ + if (val_size) { \ + kh_copyval(kh_bval(h, i), cval); \ } \ break; \ } \ @@ -335,9 +340,8 @@ typedef khint_t khiter_t; if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ h->keys = (khkey_t *)krealloc((void *)h->keys, \ new_n_buckets * sizeof(khkey_t)); \ - if (kh_is_map) { \ - h->vals = (khval_t *)krealloc((void *)h->vals, \ - new_n_buckets * sizeof(khval_t)); \ + if (val_size) { \ + h->vals_buf = krealloc((void *)h->vals_buf, new_n_buckets * val_size); \ } \ } \ kfree(h->flags); /* free the working space */ \ @@ -347,16 +351,16 @@ typedef khint_t khiter_t; h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ } \ - SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ - REAL_FATTR_UNUSED; \ - SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret, size_t val_size) \ + REAL_FATTR_UNUSED; \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret, size_t val_size) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ if (h->n_buckets > (h->size << 1)) { \ - kh_resize_##name(h, h->n_buckets - 1); /* clear "deleted" elements */ \ + kh_resize_##name(h, h->n_buckets - 1, val_size); /* clear "deleted" elements */ \ } else { \ - kh_resize_##name(h, h->n_buckets + 1); /* expand the hash table */ \ + kh_resize_##name(h, h->n_buckets + 1, val_size); /* expand the hash table */ \ } \ } /* TODO: implement automatically shrinking; */ \ /* resize() already support shrinking */ \ @@ -407,7 +411,7 @@ typedef khint_t khiter_t; return x; \ } \ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ - REAL_FATTR_UNUSED; \ + REAL_FATTR_UNUSED; \ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ @@ -416,16 +420,16 @@ typedef khint_t khiter_t; } \ } -#define KHASH_DECLARE(name, khkey_t, khval_t) \ - __KHASH_TYPE(name, khkey_t, khval_t) \ - __KHASH_PROTOTYPES(name, khkey_t, khval_t) +#define KHASH_DECLARE(khkey_t) \ + __KHASH_TYPE(khkey_t, khkey_t) \ + __KHASH_PROTOTYPES(khkey_t, khkey_t) -#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, __hash_func, __hash_equal) \ __KHASH_TYPE(name, khkey_t, khval_t) \ - __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, __hash_func, __hash_equal) -#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ - KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) +#define KHASH_INIT(name, khkey_t, khval_t, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, __hash_func, __hash_equal) // --- BEGIN OF HASH FUNCTIONS --- @@ -542,7 +546,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) the bucket has been deleted [int*] @return Iterator to the inserted element [khint_t] */ -#define kh_put(name, h, k, r) kh_put_##name(h, k, r) +#define kh_put(name, h, k, r, vs) kh_put_##name(h, k, r, vs) /*! @function @abstract Retrieve a key from the hash table. @@ -584,12 +588,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) @return Value [type of values] @discussion For hash sets, calling this results in segfault. */ -#define kh_val(h, x) ((h)->vals[x]) - -/*! @function - @abstract Alias of kh_val() - */ -#define kh_value(h, x) ((h)->vals[x]) +#define kh_val(type, h, x) (*(type *)(&(h)->vals_buf[(x)*(sizeof (type))])) /*! @function @abstract Get the start iterator @@ -626,13 +625,15 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) @param vvar Variable to which value will be assigned @param code Block of code to execute */ -#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ - for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ - if (!kh_exist(h, __i)) continue; \ - (kvar) = kh_key(h, __i); \ - (vvar) = kh_val(h, __i); \ - code; \ - } } +#define kh_foreach(type, h, kvar, vvar, code) { \ + khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(type, h,__i); \ + code; \ + } \ +} /*! @function @abstract Iterate over the values in the hash table @@ -640,12 +641,14 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) @param vvar Variable to which value will be assigned @param code Block of code to execute */ -#define kh_foreach_value(h, vvar, code) { khint_t __i; \ - for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ - if (!kh_exist(h, __i)) continue; \ - (vvar) = kh_val(h, __i); \ - code; \ - } } +#define kh_foreach_value(type, h, vvar, code) { \ + khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(type, h,__i); \ + code; \ + } \ +} /*! @function @abstract Iterate over the keys in the hash table @@ -725,6 +728,6 @@ typedef const char *kh_cstr_t; .upper_bound = 0, \ .flags = NULL, \ .keys = NULL, \ - .vals = NULL, \ + .vals_buf = NULL, \ }) #endif // NVIM_LIB_KHASH_H diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index c9a46ecfa1..47f32abc07 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -9,7 +9,7 @@ if(TARGET libuv::uv_a) else() # Fall back to find module for older libuv versions that don't provide config file find_package(Libuv 1.28.0 REQUIRED MODULE) - target_include_directories(libuv SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIRS}) + target_include_directories(libuv SYSTEM BEFORE INTERFACE ${LIBUV_INCLUDE_DIR}) target_link_libraries(libuv INTERFACE ${LIBUV_LIBRARIES}) endif() @@ -21,9 +21,9 @@ elseif(APPLE) set_target_properties(nlua0 PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") endif() -find_package(Libluv 1.43.0 REQUIRED) -target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBLUV_INCLUDE_DIR}) -target_link_libraries(main_lib INTERFACE ${LIBLUV_LIBRARY}) +find_package(Luv 1.43.0 REQUIRED) +target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUV_INCLUDE_DIR}) +target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY}) find_package(Iconv REQUIRED) find_package(Lpeg REQUIRED) @@ -61,11 +61,11 @@ if(PREFER_LUA) find_package(Luajit) else() find_package(Luajit REQUIRED) - target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUAJIT_INCLUDE_DIRS}) - target_link_libraries(main_lib INTERFACE ${LUAJIT_LIBRARIES}) - target_include_directories(nlua0 SYSTEM BEFORE PUBLIC ${LUAJIT_INCLUDE_DIRS}) + target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LUAJIT_INCLUDE_DIR}) + target_link_libraries(main_lib INTERFACE ${LUAJIT_LIBRARY}) + target_include_directories(nlua0 SYSTEM BEFORE PUBLIC ${LUAJIT_INCLUDE_DIR}) if(WIN32) - target_link_libraries(nlua0 PUBLIC ${LUAJIT_LIBRARIES}) + target_link_libraries(nlua0 PUBLIC ${LUAJIT_LIBRARY}) endif() endif() @@ -292,6 +292,7 @@ set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lu 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(LUAJIT_RUNTIME_DIR ${DEPS_PREFIX}/share/luajit-2.1.0-beta3/jit) glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt) glob_wrapper(API_HEADERS api/*.h) @@ -798,6 +799,15 @@ install(DIRECTORY ${BINARY_LIB_DIR} DESTINATION ${CMAKE_INSTALL_LIBDIR}/nvim/ USE_SOURCE_PERMISSIONS) +if(NOT PREFER_LUA) + # install luajit runtime files if bundled + if(EXISTS ${LUAJIT_RUNTIME_DIR}) + install(DIRECTORY ${LUAJIT_RUNTIME_DIR} + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/lua + USE_SOURCE_PERMISSIONS) + endif() +endif() + add_library(libnvim STATIC EXCLUDE_FROM_ALL) if(MSVC) set(LIBNVIM_NAME libnvim) @@ -826,6 +836,7 @@ if(ENABLE_ASAN_UBSAN) -fsanitize=address -fsanitize=undefined) target_link_libraries(nvim PRIVATE -fsanitize=address -fsanitize=undefined) + target_compile_definitions(nvim PRIVATE ENABLE_ASAN_UBSAN) elseif(ENABLE_MSAN) message(STATUS "Enabling memory sanitizer for nvim.") target_compile_options(nvim PRIVATE @@ -877,18 +888,13 @@ add_glob_target( EXCLUDE tui/terminfo_defs.h) -add_custom_target(uncrustify-version - COMMAND ${CMAKE_COMMAND} - -D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG} - -D CONFIG_FILE=${PROJECT_SOURCE_DIR}/src/uncrustify.cfg - -P ${PROJECT_SOURCE_DIR}/cmake/CheckUncrustifyVersion.cmake) - +set(UNCRUSTIFY_PRG ${DEPS_BIN_DIR}/uncrustify) add_glob_target( TARGET lintc-uncrustify COMMAND ${UNCRUSTIFY_PRG} FLAGS -c "${PROJECT_SOURCE_DIR}/src/uncrustify.cfg" -q --check FILES ${LINT_NVIM_SOURCES}) -add_dependencies(lintc-uncrustify uncrustify-version) +add_dependencies(lintc-uncrustify uncrustify) add_custom_target(lintc) add_dependencies(lintc lintc-clint lintc-uncrustify) @@ -899,7 +905,7 @@ add_custom_target(formatc -D LANG=c -P ${PROJECT_SOURCE_DIR}/cmake/Format.cmake WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) -add_dependencies(formatc uncrustify-version) +add_dependencies(formatc uncrustify) add_custom_target(generated-sources DEPENDS ${NVIM_GENERATED_FOR_SOURCES} diff --git a/src/nvim/README.md b/src/nvim/README.md index cbd5daba4e..75155fb9c6 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -71,9 +71,8 @@ Create a directory to store logs: Configure the sanitizer(s) via these environment variables: # Change to detect_leaks=1 to detect memory leaks (slower, noisier). - export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan,handle_abort=1,handle_sigill=1" + export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" # Show backtraces in the logs. - export UBSAN_OPTIONS=print_stacktrace=1 export MSAN_OPTIONS="log_path=${HOME}/logs/msan" export TSAN_OPTIONS="log_path=${HOME}/logs/tsan" diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 6ecbff2606..14937cfd8f 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -293,17 +293,17 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", STRING_OBJ(cstr_as_string(callback_to_string(cb)))); + PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb))); break; default: abort(); } } else { - PUT(autocmd_info, "command", STRING_OBJ(cstr_as_string(xstrdup(ac->exec.callable.cmd)))); + PUT(autocmd_info, "command", CSTR_TO_OBJ(ac->exec.callable.cmd)); } - PUT(autocmd_info, "pattern", STRING_OBJ(cstr_to_string(ap->pat))); - PUT(autocmd_info, "event", STRING_OBJ(cstr_to_string(event_nr2name(event)))); + PUT(autocmd_info, "pattern", CSTR_TO_OBJ(ap->pat)); + PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event))); PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); if (ap->buflocal_nr) { @@ -475,7 +475,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc } if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); + ADD(patterns, STATIC_CSTR_TO_OBJ("*")); } VALIDATE_R((event_array.size > 0), "event", { @@ -587,7 +587,7 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) // When we create the autocmds, we want to say that they are all matched, so that's * // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + ADD(patterns, STATIC_CSTR_TO_OBJ("")); } // If we didn't pass any events, that means clear all events. @@ -763,7 +763,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) } if (patterns.size == 0) { - ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + ADD(patterns, STATIC_CSTR_TO_OBJ("")); } if (HAS_KEY(opts->data)) { @@ -894,7 +894,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(*patterns, STRING_OBJ(cstr_to_string(pattern_buflocal))); + ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal)); } return true; diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 10c684941c..82a62c3192 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -518,7 +518,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In { MAXSIZE_TEMP_ARRAY(scratch, 1); if (replacement.size == 0) { - ADD_C(scratch, STRING_OBJ(STATIC_CSTR_AS_STRING(""))); + ADD_C(scratch, STATIC_CSTR_AS_OBJ("")); replacement = scratch; } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 3df80e3fed..6d715bcf46 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -245,7 +245,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) Dictionary filter = ARRAY_DICT_INIT; PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat) - : STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + : STATIC_CSTR_TO_OBJ("")); PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); PUT(mods, "filter", DICTIONARY_OBJ(filter)); @@ -438,7 +438,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error break; } - ADD(args, STRING_OBJ(cstr_as_string(data_str))); + ADD(args, CSTR_AS_OBJ(data_str)); } bool argc_valid; @@ -916,6 +916,7 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin /// - args: (string) The args passed to the command, if any |<args>| /// - fargs: (table) The args split by unescaped whitespace (when more than one /// argument is allowed), if any |<f-args>| +/// - nargs: (string) Number of arguments |:command-nargs| /// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>| /// - line1: (number) The starting line of the command range |<line1>| /// - line2: (number) The final line of the command range |<line2>| diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 0ca505e7b2..a7e5b32d49 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -8,6 +8,7 @@ #include "nvim/api/buffer.h" #include "nvim/api/deprecated.h" #include "nvim/api/extmark.h" +#include "nvim/api/options.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/validate.h" @@ -522,3 +523,222 @@ Dictionary nvim_get_option_info(String name, Error *err) { return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err); } + +/// Sets the global value of an option. +/// +/// @deprecated +/// @param channel_id +/// @param name Option name +/// @param value New option value +/// @param[out] err Error details, if any +void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) + FUNC_API_SINCE(1) +{ + set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err); +} + +/// Gets the global value of an option. +/// +/// @deprecated +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value (global) +Object nvim_get_option(String name, Arena *arena, Error *err) + FUNC_API_SINCE(1) +{ + return get_option_from(NULL, SREQ_GLOBAL, name, err); +} + +/// Gets a buffer option value +/// +/// @deprecated +/// @param buffer Buffer handle, or 0 for current buffer +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value +Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err) + FUNC_API_SINCE(1) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return (Object)OBJECT_INIT; + } + + return get_option_from(buf, SREQ_BUF, name, err); +} + +/// Sets a buffer option value. Passing `nil` as value deletes the option (only +/// works if there's a global fallback) +/// +/// @deprecated +/// @param channel_id +/// @param buffer Buffer handle, or 0 for current buffer +/// @param name Option name +/// @param value Option value +/// @param[out] err Error details, if any +void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err) + FUNC_API_SINCE(1) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return; + } + + set_option_to(channel_id, buf, SREQ_BUF, name, value, err); +} + +/// Gets a window option value +/// +/// @deprecated +/// @param window Window handle, or 0 for current window +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value +Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err) + FUNC_API_SINCE(1) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return (Object)OBJECT_INIT; + } + + return get_option_from(win, SREQ_WIN, name, err); +} + +/// Sets a window option value. Passing `nil` as value deletes the option (only +/// works if there's a global fallback) +/// +/// @deprecated +/// @param channel_id +/// @param window Window handle, or 0 for current window +/// @param name Option name +/// @param value Option value +/// @param[out] err Error details, if any +void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err) + FUNC_API_SINCE(1) +{ + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return; + } + + set_option_to(channel_id, win, SREQ_WIN, name, value, err); +} + +/// Gets the value of a global or local (buffer, window) option. +/// +/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer +/// to the window or buffer. +/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` +/// @param name The option name +/// @param[out] err Details of an error that may have occurred +/// @return the option value +static Object get_option_from(void *from, int type, String name, Error *err) +{ + Object rv = OBJECT_INIT; + + VALIDATE_S(name.size > 0, "option name", "<empty>", { + return rv; + }); + + // Return values + int64_t numval; + char *stringval = NULL; + + int flags = get_option_value_strict(name.data, &numval, &stringval, type, from); + VALIDATE_S(flags != 0, "option name", name.data, { + return rv; + }); + + if (flags & SOPT_BOOL) { + rv.type = kObjectTypeBoolean; + rv.data.boolean = numval ? true : false; + } else if (flags & SOPT_NUM) { + rv.type = kObjectTypeInteger; + rv.data.integer = numval; + } else if (flags & SOPT_STRING) { + if (!stringval) { + api_set_error(err, kErrorTypeException, "Failed to get option '%s'", name.data); + return rv; + } + rv.type = kObjectTypeString; + rv.data.string.data = stringval; + rv.data.string.size = strlen(stringval); + } else { + api_set_error(err, kErrorTypeException, "Unknown type for option '%s'", name.data); + } + + return rv; +} + +/// Sets the value of a global or local (buffer, window) option. +/// +/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer +/// to the window or buffer. +/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` +/// @param name The option name +/// @param[out] err Details of an error that may have occurred +static void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, + Error *err) +{ + VALIDATE_S(name.size > 0, "option name", "<empty>", { + return; + }); + + int flags = get_option_value_strict(name.data, NULL, NULL, type, to); + VALIDATE_S(flags != 0, "option name", name.data, { + return; + }); + + if (value.type == kObjectTypeNil) { + if (type == SREQ_GLOBAL) { + api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", name.data); + return; + } else if (!(flags & SOPT_GLOBAL)) { + api_set_error(err, kErrorTypeException, + "Cannot unset option '%s' because it doesn't have a global value", + name.data); + return; + } else { + unset_global_local_option(name.data, to); + return; + } + } + + long numval = 0; + char *stringval = NULL; + + if (flags & SOPT_BOOL) { + VALIDATE(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, { + return; + }); + numval = value.data.boolean; + } else if (flags & SOPT_NUM) { + VALIDATE(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, { + return; + }); + VALIDATE((value.data.integer <= INT_MAX && value.data.integer >= INT_MIN), + "Option '%s' value is out of range", name.data, { + return; + }); + numval = (int)value.data.integer; + } else { + VALIDATE(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, { + return; + }); + stringval = value.data.string.data; + } + + // For global-win-local options -> setlocal + // For win-local options -> setglobal and setlocal (opt_flags == 0) + const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) ? 0 : + (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; + + WITH_SCRIPT_CONTEXT(channel_id, { + access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err); + }); +} diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 87232a8a93..299413e510 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -33,12 +33,10 @@ void api_extmark_free_all_mem(void) { String name; - handle_T id; - map_foreach(&namespace_ids, name, id, { - (void)id; + map_foreach_key(&namespace_ids, name, { xfree(name.data); }) - map_destroy(String, handle_T)(&namespace_ids); + map_destroy(String, &namespace_ids); } /// Creates a new namespace or gets an existing one. \*namespace\* @@ -77,7 +75,7 @@ Dictionary nvim_get_namespaces(void) String name; handle_T id; - map_foreach(&namespace_ids, name, id, { + map_foreach(handle_T, &namespace_ids, name, id, { PUT(retval, name.data, INTEGER_OBJ(id)); }) @@ -88,7 +86,7 @@ const char *describe_ns(NS ns_id) { String name; handle_T id; - map_foreach(&namespace_ids, name, id, { + map_foreach(handle_T, &namespace_ids, name, id, { if ((NS)id == ns_id && name.size) { return name.data; } @@ -108,7 +106,7 @@ bool ns_initialized(uint32_t ns) static Object hl_group_name(int hl_id, bool hl_name) { if (hl_name) { - return STRING_OBJ(cstr_to_string(syn_id2name(hl_id))); + return CSTR_TO_OBJ(syn_id2name(hl_id)); } else { return INTEGER_OBJ(hl_id); } @@ -142,7 +140,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol)); } if (decor->hl_mode) { - PUT(dict, "hl_mode", STRING_OBJ(cstr_to_string(hl_mode_str[decor->hl_mode]))); + PUT(dict, "hl_mode", CSTR_TO_OBJ(hl_mode_str[decor->hl_mode])); } if (kv_size(decor->virt_text)) { @@ -150,7 +148,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict for (size_t i = 0; i < decor->virt_text.size; i++) { Array chunk = ARRAY_DICT_INIT; VirtTextChunk *vtc = &decor->virt_text.items[i]; - ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); + ADD(chunk, CSTR_TO_OBJ(vtc->text)); if (vtc->hl_id > 0) { ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); } @@ -162,7 +160,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict PUT(dict, "virt_text_win_col", INTEGER_OBJ(decor->col)); } PUT(dict, "virt_text_pos", - STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos]))); + CSTR_TO_OBJ(virt_text_pos_str[decor->virt_text_pos])); } if (decor->ui_watched) { @@ -179,7 +177,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict for (size_t j = 0; j < vt->size; j++) { Array chunk = ARRAY_DICT_INIT; VirtTextChunk *vtc = &vt->items[j]; - ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); + ADD(chunk, CSTR_TO_OBJ(vtc->text)); if (vtc->hl_id > 0) { ADD(chunk, hl_group_name(vtc->hl_id, hl_name)); } @@ -193,7 +191,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict } if (decor->sign_text) { - PUT(dict, "sign_text", STRING_OBJ(cstr_to_string(decor->sign_text))); + PUT(dict, "sign_text", CSTR_TO_OBJ(decor->sign_text)); } // uncrustify:off @@ -479,6 +477,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - "overlay": display over the specified column, without /// shifting the underlying text. /// - "right_align": display right aligned in the window. +/// - "inline": display at the specified column, and +/// shift the buffer text to the right as needed /// - virt_text_win_col : position the virtual text at a fixed /// window column (starting from the first /// text column) @@ -697,6 +697,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer decor.virt_text_pos = kVTOverlay; } else if (strequal("right_align", str.data)) { decor.virt_text_pos = kVTRightAlign; + } else if (strequal("inline", str.data)) { + decor.virt_text_pos = kVTInline; } else { VALIDATE_S(false, "virt_text_pos", "", { goto error; diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h index a6586e3031..3c979fa4f6 100644 --- a/src/nvim/api/extmark.h +++ b/src/nvim/api/extmark.h @@ -6,7 +6,6 @@ #include "nvim/decoration.h" #include "nvim/macros.h" #include "nvim/map.h" -#include "nvim/map_defs.h" #include "nvim/types.h" EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 2d1b170d2d..e18312a6dc 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -188,7 +188,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) switch (result) { case gov_string: - rv = STRING_OBJ(cstr_as_string(stringval)); + rv = CSTR_AS_OBJ(stringval); break; case gov_number: rv = INTEGER_OBJ(numval); @@ -341,218 +341,6 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) return get_vimoption(name, scope, buf, win, err); } -/// Sets the global value of an option. -/// -/// @param channel_id -/// @param name Option name -/// @param value New option value -/// @param[out] err Error details, if any -void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) - FUNC_API_SINCE(1) -{ - set_option_to(channel_id, NULL, SREQ_GLOBAL, name, value, err); -} - -/// Gets the global value of an option. -/// -/// @param name Option name -/// @param[out] err Error details, if any -/// @return Option value (global) -Object nvim_get_option(String name, Arena *arena, Error *err) - FUNC_API_SINCE(1) -{ - return get_option_from(NULL, SREQ_GLOBAL, name, err); -} - -/// Gets a buffer option value -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param name Option name -/// @param[out] err Error details, if any -/// @return Option value -Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err) - FUNC_API_SINCE(1) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return (Object)OBJECT_INIT; - } - - return get_option_from(buf, SREQ_BUF, name, err); -} - -/// Sets a buffer option value. Passing `nil` as value deletes the option (only -/// works if there's a global fallback) -/// -/// @param channel_id -/// @param buffer Buffer handle, or 0 for current buffer -/// @param name Option name -/// @param value Option value -/// @param[out] err Error details, if any -void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object value, Error *err) - FUNC_API_SINCE(1) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return; - } - - set_option_to(channel_id, buf, SREQ_BUF, name, value, err); -} - -/// Gets a window option value -/// -/// @param window Window handle, or 0 for current window -/// @param name Option name -/// @param[out] err Error details, if any -/// @return Option value -Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err) - FUNC_API_SINCE(1) -{ - win_T *win = find_window_by_handle(window, err); - - if (!win) { - return (Object)OBJECT_INIT; - } - - return get_option_from(win, SREQ_WIN, name, err); -} - -/// Sets a window option value. Passing `nil` as value deletes the option (only -/// works if there's a global fallback) -/// -/// @param channel_id -/// @param window Window handle, or 0 for current window -/// @param name Option name -/// @param value Option value -/// @param[out] err Error details, if any -void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object value, Error *err) - FUNC_API_SINCE(1) -{ - win_T *win = find_window_by_handle(window, err); - - if (!win) { - return; - } - - set_option_to(channel_id, win, SREQ_WIN, name, value, err); -} - -/// Gets the value of a global or local (buffer, window) option. -/// -/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return the option value -static Object get_option_from(void *from, int type, String name, Error *err) -{ - Object rv = OBJECT_INIT; - - VALIDATE_S(name.size > 0, "option name", "<empty>", { - return rv; - }); - - // Return values - int64_t numval; - char *stringval = NULL; - - int flags = get_option_value_strict(name.data, &numval, &stringval, type, from); - VALIDATE_S(flags != 0, "option name", name.data, { - return rv; - }); - - if (flags & SOPT_BOOL) { - rv.type = kObjectTypeBoolean; - rv.data.boolean = numval ? true : false; - } else if (flags & SOPT_NUM) { - rv.type = kObjectTypeInteger; - rv.data.integer = numval; - } else if (flags & SOPT_STRING) { - if (!stringval) { - api_set_error(err, kErrorTypeException, "Failed to get option '%s'", name.data); - return rv; - } - rv.type = kObjectTypeString; - rv.data.string.data = stringval; - rv.data.string.size = strlen(stringval); - } else { - api_set_error(err, kErrorTypeException, "Unknown type for option '%s'", name.data); - } - - return rv; -} - -/// Sets the value of a global or local (buffer, window) option. -/// -/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err) -{ - VALIDATE_S(name.size > 0, "option name", "<empty>", { - return; - }); - - int flags = get_option_value_strict(name.data, NULL, NULL, type, to); - VALIDATE_S(flags != 0, "option name", name.data, { - return; - }); - - if (value.type == kObjectTypeNil) { - if (type == SREQ_GLOBAL) { - api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", name.data); - return; - } else if (!(flags & SOPT_GLOBAL)) { - api_set_error(err, kErrorTypeException, - "Cannot unset option '%s' because it doesn't have a global value", - name.data); - return; - } else { - unset_global_local_option(name.data, to); - return; - } - } - - long numval = 0; - char *stringval = NULL; - - if (flags & SOPT_BOOL) { - VALIDATE(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, { - return; - }); - numval = value.data.boolean; - } else if (flags & SOPT_NUM) { - VALIDATE(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, { - return; - }); - VALIDATE((value.data.integer <= INT_MAX && value.data.integer >= INT_MIN), - "Option '%s' value is out of range", name.data, { - return; - }); - numval = (int)value.data.integer; - } else { - VALIDATE(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, { - return; - }); - stringval = value.data.string.data; - } - - // For global-win-local options -> setlocal - // For win-local options -> setglobal and setlocal (opt_flags == 0) - const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) ? 0 : - (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; - - WITH_SCRIPT_CONTEXT(channel_id, { - access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err); - }); -} - static getoption_T access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get, Error *err) { @@ -571,8 +359,8 @@ static getoption_T access_option_value(char *key, long *numval, char **stringval } } -static getoption_T access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, - int opt_type, void *from, bool get, Error *err) +getoption_T access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, + int opt_type, void *from, bool get, Error *err) { bool need_switch = false; switchwin_T switchwin; diff --git a/src/nvim/api/options.h b/src/nvim/api/options.h index 869826e443..7be72d3708 100644 --- a/src/nvim/api/options.h +++ b/src/nvim/api/options.h @@ -3,6 +3,7 @@ #include "nvim/api/keysets.h" #include "nvim/api/private/defs.h" +#include "nvim/option.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/options.h.generated.h" diff --git a/src/nvim/api/private/converter.h b/src/nvim/api/private/converter.h index 80ee640295..28ae71983b 100644 --- a/src/nvim/api/private/converter.h +++ b/src/nvim/api/private/converter.h @@ -2,7 +2,7 @@ #define NVIM_API_PRIVATE_CONVERTER_H #include "nvim/api/private/defs.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/converter.h.generated.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 02060a8950..2544809553 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -660,10 +660,10 @@ static void init_ui_event_metadata(Dictionary *metadata) msgpack_unpacked_destroy(&unpacked); PUT(*metadata, "ui_events", ui_events); Array ui_options = ARRAY_DICT_INIT; - ADD(ui_options, STRING_OBJ(cstr_to_string("rgb"))); + ADD(ui_options, CSTR_TO_OBJ("rgb")); for (UIExtension i = 0; i < kUIExtCount; i++) { if (ui_ext_names[i][0] != '_') { - ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); + ADD(ui_options, CSTR_TO_OBJ(ui_ext_names[i])); } } PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); @@ -692,17 +692,17 @@ static void init_type_metadata(Dictionary *metadata) Dictionary buffer_metadata = ARRAY_DICT_INIT; PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT)); - PUT(buffer_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_buf_"))); + PUT(buffer_metadata, "prefix", CSTR_TO_OBJ("nvim_buf_")); Dictionary window_metadata = ARRAY_DICT_INIT; PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT)); - PUT(window_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_win_"))); + PUT(window_metadata, "prefix", CSTR_TO_OBJ("nvim_win_")); Dictionary tabpage_metadata = ARRAY_DICT_INIT; PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)); - PUT(tabpage_metadata, "prefix", STRING_OBJ(cstr_to_string("nvim_tabpage_"))); + PUT(tabpage_metadata, "prefix", CSTR_TO_OBJ("nvim_tabpage_")); PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 2623c97c9d..a9cfaeae22 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -34,6 +34,7 @@ .type = kObjectTypeString, \ .data.string = s }) +#define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s)) #define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s)) #define BUFFER_OBJ(s) ((Object) { \ @@ -103,6 +104,9 @@ .data = xmemdupz(s, sizeof(s) - 1), \ .size = sizeof(s) - 1 }) +#define STATIC_CSTR_AS_OBJ(s) STRING_OBJ(STATIC_CSTR_AS_STRING(s)) +#define STATIC_CSTR_TO_OBJ(s) STRING_OBJ(STATIC_CSTR_TO_STRING(s)) + // Helpers used by the generated msgpack-rpc api wrappers #define api_init_boolean #define api_init_integer @@ -122,13 +126,13 @@ #define api_free_window(value) #define api_free_tabpage(value) -EXTERN PMap(handle_T) buffer_handles INIT(= MAP_INIT); -EXTERN PMap(handle_T) window_handles INIT(= MAP_INIT); -EXTERN PMap(handle_T) tabpage_handles INIT(= MAP_INIT); +EXTERN PMap(int) buffer_handles INIT(= MAP_INIT); +EXTERN PMap(int) window_handles INIT(= MAP_INIT); +EXTERN PMap(int) tabpage_handles INIT(= MAP_INIT); -#define handle_get_buffer(h) pmap_get(handle_T)(&buffer_handles, (h)) -#define handle_get_window(h) pmap_get(handle_T)(&window_handles, (h)) -#define handle_get_tabpage(h) pmap_get(handle_T)(&tabpage_handles, (h)) +#define handle_get_buffer(h) pmap_get(int)(&buffer_handles, (h)) +#define handle_get_window(h) pmap_get(int)(&window_handles, (h)) +#define handle_get_tabpage(h) pmap_get(int)(&tabpage_handles, (h)) /// Structure used for saving state for :try /// diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index bd3482c85f..e98c589189 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -111,7 +111,7 @@ void remote_ui_disconnect(uint64_t channel_id) } UIData *data = ui->data; kv_destroy(data->call_buf); - pmap_del(uint64_t)(&connected_uis, channel_id); + pmap_del(uint64_t)(&connected_uis, channel_id, NULL); ui_detach_impl(ui, channel_id); // Destroy `ui`. @@ -795,7 +795,7 @@ void remote_ui_put(UI *ui, const char *cell) UIData *data = ui->data; data->client_col++; Array args = data->call_buf; - ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell))); + ADD_C(args, CSTR_AS_OBJ((char *)cell)); push_call(ui, "put", args); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d47f47e638..36163859eb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -531,7 +531,7 @@ static void find_runtime_cb(char *fname, void *cookie) { Array *rv = (Array *)cookie; if (fname != NULL) { - ADD(*rv, STRING_OBJ(cstr_to_string(fname))); + ADD(*rv, CSTR_TO_OBJ(fname)); } } @@ -1383,7 +1383,7 @@ Dictionary nvim_get_mode(void) get_mode(modestr); bool blocked = input_blocking(); - PUT(rv, "mode", STRING_OBJ(cstr_to_string(modestr))); + PUT(rv, "mode", CSTR_TO_OBJ(modestr)); PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); return rv; @@ -1926,7 +1926,7 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E } ret = arena_array(arena, 3); size_t off = g->line_offset[(size_t)row] + (size_t)col; - ADD_C(ret, STRING_OBJ(cstr_as_string((char *)g->chars[off]))); + ADD_C(ret, CSTR_AS_OBJ((char *)g->chars[off])); int attr = g->attrs[off]; ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err))); // will not work first time @@ -2035,7 +2035,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) ADD(rv, INTEGER_OBJ(row)); ADD(rv, INTEGER_OBJ(col)); ADD(rv, INTEGER_OBJ(bufnr)); - ADD(rv, STRING_OBJ(cstr_to_string(filename))); + ADD(rv, CSTR_TO_OBJ(filename)); if (allocated) { xfree(filename); diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 208aa165c9..1a67be8860 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -502,7 +502,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E }; err_dict.items[0] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("message"), - .value = STRING_OBJ(cstr_to_string(east.err.msg)), + .value = CSTR_TO_OBJ(east.err.msg), }; if (east.err.arg == NULL) { err_dict.items[1] = (KeyValuePair) { @@ -539,7 +539,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line); chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col); chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col); - chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group)); + chunk_arr.items[3] = CSTR_TO_OBJ(chunk.group); hl.items[i] = ARRAY_OBJ(chunk_arr); } ret.items[ret.size++] = (KeyValuePair) { @@ -616,7 +616,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E kv_drop(ast_conv_stack, 1); ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("type"), - .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])), + .value = CSTR_TO_OBJ(east_node_type_tab[node->type]), }; Array start_array = { .items = xmalloc(2 * sizeof(start_array.items[0])), @@ -701,11 +701,11 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E case kExprNodeComparison: ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("cmp_type"), - .value = STRING_OBJ(cstr_to_string(eltkn_cmp_type_tab[node->data.cmp.type])), + .value = CSTR_TO_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]), }; ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("ccs_strategy"), - .value = STRING_OBJ(cstr_to_string(ccs_tab[node->data.cmp.ccs])), + .value = CSTR_TO_OBJ(ccs_tab[node->data.cmp.ccs]), }; ret_node->items[ret_node->size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("invert"), diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index c267fee39a..8e4fbb6779 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -267,7 +267,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) PUT(rv, "bufpos", ARRAY_OBJ(pos)); } } - PUT(rv, "anchor", STRING_OBJ(cstr_to_string(float_anchor_str[config->anchor]))); + PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor])); PUT(rv, "row", FLOAT_OBJ(config->row)); PUT(rv, "col", FLOAT_OBJ(config->col)); PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); @@ -283,7 +283,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) char *hi_name = syn_id2name(hi_id); if (hi_name[0]) { ADD(tuple, STRING_OBJ(s)); - ADD(tuple, STRING_OBJ(cstr_to_string(hi_name))); + ADD(tuple, CSTR_TO_OBJ(hi_name)); ADD(border, ARRAY_OBJ(tuple)); } else { ADD(border, STRING_OBJ(s)); @@ -297,7 +297,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) Array tuple = ARRAY_DICT_INIT; ADD(tuple, CSTR_TO_OBJ(title_datas.items[i].text)); if (title_datas.items[i].hl_id > 0) { - ADD(tuple, STRING_OBJ(cstr_to_string(syn_id2name(title_datas.items[i].hl_id)))); + ADD(tuple, CSTR_TO_OBJ(syn_id2name(title_datas.items[i].hl_id))); } ADD(titles, ARRAY_OBJ(tuple)); } @@ -317,7 +317,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) const char *rel = (wp->w_floating && !config->external ? float_relative_str[config->relative] : ""); - PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); + PUT(rv, "relative", CSTR_TO_OBJ(rel)); return rv; } diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h index b2e0f411d4..cd34ca10c4 100644 --- a/src/nvim/arglist.h +++ b/src/nvim/arglist.h @@ -1,7 +1,7 @@ #ifndef NVIM_ARGLIST_H #define NVIM_ARGLIST_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2d5d8e262b..17a3fd33f1 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -108,14 +108,13 @@ static Map(int, String) map_augroup_id_to_name = MAP_INIT; static void augroup_map_del(int id, const char *name) { if (name != NULL) { - String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name)); - map_del(String, int)(&map_augroup_name_to_id, key); + String key; + map_del(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name), &key); api_free_string(key); } if (id > 0) { - String mapped = map_get(int, String)(&map_augroup_id_to_name, id); + String mapped = map_del(int, String)(&map_augroup_id_to_name, id, NULL); api_free_string(mapped); - map_del(int, String)(&map_augroup_id_to_name, id); } } @@ -543,7 +542,7 @@ void do_augroup(char *arg, int del_group) String name; int value; - map_foreach(&map_augroup_name_to_id, name, value, { + map_foreach(int, &map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -572,18 +571,15 @@ void free_all_autocmds(void) // Delete the augroup_map, including free the data String name; - int id; - map_foreach(&map_augroup_name_to_id, name, id, { - (void)id; + map_foreach_key(&map_augroup_name_to_id, name, { api_free_string(name); }) - map_destroy(String, int)(&map_augroup_name_to_id); + map_destroy(String, &map_augroup_name_to_id); - map_foreach(&map_augroup_id_to_name, id, name, { - (void)id; + map_foreach_value(String, &map_augroup_id_to_name, name, { api_free_string(name); }) - map_destroy(int, String)(&map_augroup_id_to_name); + map_destroy(int, &map_augroup_id_to_name); // aucmd_win[] is freed in win_free_all() } @@ -1311,7 +1307,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) block_autocmds(); // We don't want BufEnter/WinEnter autocommands. if (need_append) { win_append(lastwin, auc_win); - pmap_put(handle_T)(&window_handles, auc_win->handle, auc_win); + pmap_put(int)(&window_handles, auc_win->handle, auc_win); win_config_float(auc_win, auc_win->w_float_config); } // Prevent chdir() call in win_enter_ext(), through do_autochdir() @@ -1367,7 +1363,7 @@ win_found: } // Remove the window. win_remove(curwin, NULL); - pmap_del(handle_T)(&window_handles, curwin->handle); + pmap_del(int)(&window_handles, curwin->handle, NULL); if (curwin->w_grid_alloc.chars != NULL) { ui_comp_remove_grid(&curwin->w_grid_alloc); ui_call_win_hide(curwin->w_grid_alloc.handle); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8d730733d0..11b79fcede 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -833,7 +833,7 @@ void buf_freeall(buf_T *buf, int flags) /// itself (not the file, that must have been done already). static void free_buffer(buf_T *buf) { - pmap_del(handle_T)(&buffer_handles, buf->b_fnum); + pmap_del(int)(&buffer_handles, buf->b_fnum, NULL); buf_free_count++; // b:changedtick uses an item in buf_T. free_buffer_stuff(buf, kBffClearWinInfo); @@ -1084,11 +1084,11 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b if (deleted == 0) { if (command == DOBUF_UNLOAD) { - STRCPY(IObuff, _("E515: No buffers were unloaded")); + xstrlcpy(IObuff, _("E515: No buffers were unloaded"), IOSIZE); } else if (command == DOBUF_DEL) { - STRCPY(IObuff, _("E516: No buffers were deleted")); + xstrlcpy(IObuff, _("E516: No buffers were deleted"), IOSIZE); } else { - STRCPY(IObuff, _("E517: No buffers were wiped out")); + xstrlcpy(IObuff, _("E517: No buffers were wiped out"), IOSIZE); } errormsg = IObuff; } else if (deleted >= p_report) { @@ -1459,16 +1459,11 @@ int do_buffer(int action, int start, int dir, int count, int forceit) // make "buf" the current buffer if (action == DOBUF_SPLIT) { // split window first - // If 'switchbuf' contains "useopen": jump to first window containing - // "buf" if one exists - if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) { - return OK; - } - // If 'switchbuf' contains "usetab": jump to first window in any tab - // page containing "buf" if one exists - if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) { + // If 'switchbuf' is set jump to the window containing "buf". + if (swbuf_goto_win_with_buf(buf) != NULL) { return OK; } + if (win_split(0, 0) == FAIL) { return FAIL; } @@ -1870,7 +1865,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags) lastbuf = buf; buf->b_fnum = top_file_num++; - pmap_put(handle_T)(&buffer_handles, buf->b_fnum, buf); + pmap_put(int)(&buffer_handles, buf->b_fnum, buf); if (top_file_num < 0) { // wrap around (may cause duplicates) emsg(_("W14: Warning: List of file names overflow")); if (emsg_silent == 0 && !in_assert_fails) { @@ -2072,17 +2067,8 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) } if (options & GETF_SWITCH) { - // If 'switchbuf' contains "useopen": jump to first window containing - // "buf" if one exists - if (swb_flags & SWB_USEOPEN) { - wp = buf_jump_open_win(buf); - } - - // If 'switchbuf' contains "usetab": jump to first window in any tab - // page containing "buf" if one exists - if (wp == NULL && (swb_flags & SWB_USETAB)) { - wp = buf_jump_open_tab(buf); - } + // If 'switchbuf' is set jump to the window containing "buf". + wp = swbuf_goto_win_with_buf(buf); // If 'switchbuf' contains "split", "vsplit" or "newtab" and the // current buffer isn't empty: open new tab or window @@ -3528,23 +3514,20 @@ bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file) return false; } - char *p = buf + strlen(buf); // go to the end of the buffer - - // Early out if the string is getting too long - if (p - buf + 35 >= buflen) { - return false; + const char *msg; + switch ((wp->w_arg_idx_invalid ? 1 : 0) + (add_file ? 2 : 0)) { + case 0: + msg = _(" (%d of %d)"); break; + case 1: + msg = _(" ((%d) of %d)"); break; + case 2: + msg = _(" (file %d of %d)"); break; + case 3: + msg = _(" (file (%d) of %d)"); break; } - *p++ = ' '; - *p++ = '('; - if (add_file) { - STRCPY(p, "file "); - p += 5; - } - vim_snprintf(p, (size_t)(buflen - (p - buf)), - wp->w_arg_idx_invalid - ? "(%d) of %d)" - : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); + char *p = buf + strlen(buf); // go to the end of the buffer + vim_snprintf(p, (size_t)(buflen - (p - buf)), msg, wp->w_arg_idx + 1, ARGCOUNT); return true; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ce8ee21882..f3f98bbd17 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -17,7 +17,7 @@ typedef struct { #include "klib/kvec.h" #include "nvim/api/private/defs.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/garray.h" #include "nvim/grid_defs.h" #include "nvim/hashtab.h" @@ -808,6 +808,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces + size_t b_virt_text_inline; // number of inline virtual texts size_t b_virt_line_blocks; // number of virt_line blocks size_t b_signs; // number of sign extmarks size_t b_signs_with_text; // number of sign extmarks with text diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c index 84c1276b8b..cfa3ea5bf3 100644 --- a/src/nvim/bufwrite.c +++ b/src/nvim/bufwrite.c @@ -745,7 +745,7 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o // the ones from the original file. // First find a file name that doesn't exist yet (use some // arbitrary numbers). - STRCPY(IObuff, fname); + xstrlcpy(IObuff, fname, IOSIZE); for (int i = 4913;; i += 123) { char *tail = path_tail(IObuff); size_t size = (size_t)(tail - IObuff); @@ -1749,24 +1749,24 @@ restore_backup: add_quoted_fname(IObuff, IOSIZE, buf, fname); bool insert_space = false; if (write_info.bw_conv_error) { - STRCAT(IObuff, _(" CONVERSION ERROR")); + xstrlcat(IObuff, _(" CONVERSION ERROR"), IOSIZE); insert_space = true; if (write_info.bw_conv_error_lnum != 0) { vim_snprintf_add(IObuff, IOSIZE, _(" in line %" PRId64 ";"), (int64_t)write_info.bw_conv_error_lnum); } } else if (notconverted) { - STRCAT(IObuff, _("[NOT converted]")); + xstrlcat(IObuff, _("[NOT converted]"), IOSIZE); insert_space = true; } else if (converted) { - STRCAT(IObuff, _("[converted]")); + xstrlcat(IObuff, _("[converted]"), IOSIZE); insert_space = true; } if (device) { - STRCAT(IObuff, _("[Device]")); + xstrlcat(IObuff, _("[Device]"), IOSIZE); insert_space = true; } else if (newfile) { - STRCAT(IObuff, new_file_message()); + xstrlcat(IObuff, new_file_message(), IOSIZE); insert_space = true; } if (no_eol) { @@ -1780,9 +1780,9 @@ restore_backup: msg_add_lines(insert_space, (long)lnum, nchars); // add line/char count if (!shortmess(SHM_WRITE)) { if (append) { - STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended")); + xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"), IOSIZE); } else { - STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written")); + xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"), IOSIZE); } } diff --git a/src/nvim/change.c b/src/nvim/change.c index 1d6869990e..9e1767c2f3 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -43,6 +43,7 @@ #include "nvim/plines.h" #include "nvim/pos.h" #include "nvim/search.h" +#include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/textformat.h" @@ -247,11 +248,25 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T wp->w_redr_type = UPD_VALID; } + linenr_T last = lnume + xtra - 1; // last line after the change + + // Reset "w_skipcol" if the topline length has become smaller to + // such a degree that nothing will be visible anymore, accounting + // for 'smoothscroll' <<< or 'listchars' "precedes" marker. + if (wp->w_skipcol > 0 + && (last < wp->w_topline + || (wp->w_topline >= lnum + && wp->w_topline < lnume + && win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), (colnr_T)MAXCOL) + <= (unsigned)(wp->w_skipcol + sms_marker_overlap(wp, win_col_off(wp) + - win_col_off2(wp)))))) { + wp->w_skipcol = 0; + } + // Check if a change in the buffer has invalidated the cached // values for the cursor. // Update the folds for this window. Can't postpone this, because // a following operator might work on the whole fold: ">>dd". - linenr_T last = lnume + xtra - 1; // last line after the change foldUpdate(wp, lnum, last); // The change may cause lines above or below the change to become @@ -379,6 +394,15 @@ void changed_bytes(linenr_T lnum, colnr_T col) { changedOneline(curbuf, lnum); changed_common(lnum, col, lnum + 1, 0); + // When text has been changed at the end of the line, possibly the start of + // the next line may have SpellCap that should be removed or it needs to be + // displayed. Schedule the next line for redrawing just in case. + // Don't do this when displaying '$' at the end of changed text. + if (spell_check_window(curwin) + && lnum < curbuf->b_ml.ml_line_count + && vim_strchr(p_cpo, CPO_DOLLAR) == NULL) { + redrawWinline(curwin, lnum + 1); + } // notify any channels that are watching buf_updates_send_changes(curbuf, lnum, 1, 1); diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 820ce534e1..569d3f5887 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -57,7 +57,7 @@ void channel_teardown(void) { Channel *channel; - map_foreach_value(&channels, channel, { + pmap_foreach_value(&channels, channel, { channel_close(channel->id, kChannelPartAll, NULL); }); } @@ -279,7 +279,7 @@ static void free_channel_event(void **argv) callback_reader_free(&chan->on_stderr); callback_free(&chan->on_exit); - pmap_del(uint64_t)(&channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id, NULL); multiqueue_free(chan->events); xfree(chan); } @@ -289,7 +289,7 @@ static void channel_destroy_early(Channel *chan) if ((chan->id != --next_chan_id)) { abort(); } - pmap_del(uint64_t)(&channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id, NULL); chan->id = 0; if ((--chan->refcount != 0)) { @@ -884,14 +884,14 @@ Dictionary channel_info(uint64_t id) stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); - PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); + PUT(info, "pty", CSTR_TO_OBJ(name)); } char **p = chan->stream.proc.argv; Array argv = ARRAY_DICT_INIT; if (p != NULL) { while (*p != NULL) { - ADD(argv, STRING_OBJ(cstr_to_string(*p))); + ADD(argv, CSTR_TO_OBJ(*p)); p++; } } @@ -918,7 +918,7 @@ Dictionary channel_info(uint64_t id) default: abort(); } - PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc))); + PUT(info, "stream", CSTR_TO_OBJ(stream_desc)); if (chan->is_rpc) { mode_desc = "rpc"; @@ -929,7 +929,7 @@ Dictionary channel_info(uint64_t id) } else { mode_desc = "bytes"; } - PUT(info, "mode", STRING_OBJ(cstr_to_string(mode_desc))); + PUT(info, "mode", CSTR_TO_OBJ(mode_desc)); return info; } @@ -938,7 +938,7 @@ Array channel_all_info(void) { Channel *channel; Array ret = ARRAY_DICT_INIT; - map_foreach_value(&channels, channel, { + pmap_foreach_value(&channels, channel, { ADD(ret, DICTIONARY_OBJ(channel_info(channel->id))); }); return ret; diff --git a/src/nvim/channel.h b/src/nvim/channel.h index ca6c75b411..deb693373c 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -5,7 +5,6 @@ #include <stdint.h> #include <stdlib.h> -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/event/libuv_process.h" #include "nvim/event/multiqueue.h" @@ -16,7 +15,6 @@ #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/map.h" -#include "nvim/map_defs.h" #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/os/pty_process.h" #include "nvim/terminal.h" diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 8cae831881..49890a460a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } chartabsize_T cts; + bool on_NUL = false; init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); // This function is used very often, do some speed optimizations. @@ -1052,8 +1053,9 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // make sure we don't go past the end of the line if (*cts.cts_ptr == NUL) { - // NUL at end of line only takes one column - incr = 1; + // NUL at end of line only takes one column, unless there is virtual text + incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); + on_NUL = true; break; } @@ -1079,8 +1081,6 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - // cursor is after inserted text - vcol += cts.cts_cur_text_width; if ((*ptr == TAB) && (State & MODE_NORMAL) && !wp->w_p_list @@ -1089,6 +1089,13 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // cursor at end *cursor = vcol + incr - 1; } else { + if (!on_NUL) { + // cursor is after inserted text, unless on the NUL + vcol += cts.cts_cur_text_width_left; + if ((State & MODE_INSERT) == 0) { + vcol += cts.cts_cur_text_width_right; + } + } // cursor at start *cursor = vcol + head; } diff --git a/src/nvim/charset.h b/src/nvim/charset.h index f98ed94b87..258e95bec0 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -4,7 +4,7 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/option_defs.h" #include "nvim/pos.h" #include "nvim/strings.h" diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h index 810e289f7c..32c23c5d66 100644 --- a/src/nvim/cmdexpand.h +++ b/src/nvim/cmdexpand.h @@ -1,7 +1,7 @@ #ifndef NVIM_CMDEXPAND_H #define NVIM_CMDEXPAND_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/types.h" diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 81b93e5304..fc84cecc1a 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -641,9 +641,10 @@ void ex_history(exarg_T *eap) } for (; !got_int && histype1 <= histype2; histype1++) { - STRCPY(IObuff, "\n # "); + xstrlcpy(IObuff, "\n # ", IOSIZE); assert(history_names[histype1] != NULL); - STRCAT(STRCAT(IObuff, history_names[histype1]), " history"); + xstrlcat(IObuff, history_names[histype1], IOSIZE); + xstrlcat(IObuff, " history", IOSIZE); msg_puts_title(IObuff); int idx = hisidx[histype1]; histentry_T *hist = history[histype1]; @@ -669,7 +670,7 @@ void ex_history(exarg_T *eap) trunc_string(hist[i].hisstr, IObuff + strlen(IObuff), Columns - 10, IOSIZE - (int)strlen(IObuff)); } else { - STRCAT(IObuff, hist[i].hisstr); + xstrlcat(IObuff, hist[i].hisstr, IOSIZE); } msg_outtrans(IObuff); } diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h index f86a2f855c..a0f2ab6934 100644 --- a/src/nvim/cmdhist.h +++ b/src/nvim/cmdhist.h @@ -1,7 +1,6 @@ #ifndef NVIM_CMDHIST_H #define NVIM_CMDHIST_H -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/os/time.h" diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 428f9f28e4..4e5886406a 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -62,8 +62,8 @@ Array mode_style_array(Arena *arena) for (int i = 0; i < SHAPE_IDX_COUNT; i++) { cursorentry_T *cur = &shape_table[i]; Dictionary dic = arena_dict(arena, 3 + ((cur->used_for & SHAPE_CURSOR) ? 9 : 0)); - PUT_C(dic, "name", STRING_OBJ(cstr_as_string(cur->full_name))); - PUT_C(dic, "short_name", STRING_OBJ(cstr_as_string(cur->name))); + PUT_C(dic, "name", CSTR_AS_OBJ(cur->full_name)); + PUT_C(dic, "short_name", CSTR_AS_OBJ(cur->name)); if (cur->used_for & SHAPE_MOUSE) { PUT_C(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 87e4441f32..ce1af290c1 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -83,6 +83,9 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) if (decor && decor_virt_pos(*decor)) { redraw_buf_line_later(buf, row1 + 1, false); + if (decor->virt_text_pos == kVTInline) { + changed_line_display_buf(buf); + } } if (decor && kv_size(decor->virt_lines)) { @@ -95,6 +98,10 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { decor_redraw(buf, row, row2, decor); if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + assert(buf->b_virt_text_inline > 0); + buf->b_virt_text_inline--; + } if (kv_size(decor->virt_lines)) { assert(buf->b_virt_line_blocks > 0); buf->b_virt_line_blocks--; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 92001d496d..95c9655742 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -23,9 +23,11 @@ typedef enum { kVTOverlay, kVTWinCol, kVTRightAlign, + kVTInline, } VirtTextPos; -EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" }); +EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align", + "inline" }); typedef enum { kHlModeUnknown, diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index e6cdf3d60d..d2d39e8ca8 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2,7 +2,7 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com // drawline.c: Functions for drawing window lines on the screen. -// This is the middle level, drawscreen.c is the top and grid.c/screen.c the lower level. +// This is the middle level, drawscreen.c is the top and grid.c the lower level. #include <assert.h> #include <limits.h> @@ -98,6 +98,7 @@ typedef struct { int char_attr; ///< attributes for next character int n_extra; ///< number of extra bytes + int n_attr; ///< chars with special attr char *p_extra; ///< string of extra chars, plus NUL, only used ///< when c_extra and c_final are NUL char *p_extra_free; ///< p_extra buffer that needs to be freed @@ -105,9 +106,12 @@ typedef struct { int c_extra; ///< extra chars, all the same int c_final; ///< final char, mandatory if set + bool extra_for_extmark; + // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra; char *saved_p_extra; + bool saved_extra_for_extmark; int saved_c_extra; int saved_c_final; int saved_char_attr; @@ -120,6 +124,14 @@ typedef struct { int filler_lines; ///< nr of filler lines to be drawn int filler_todo; ///< nr of filler lines still to do + 1 SignTextAttrs sattrs[SIGN_SHOW_MAX]; ///< sign attributes for the sign column + + VirtText virt_inline; + size_t virt_inline_i; + + bool reset_extra_attr; + + int skip_cells; // nr of cells to skip for virtual text + int skipped_cells; // nr of skipped virtual text cells } winlinevars_T; /// for line_putchar. Contains the state that needs to be remembered from @@ -342,12 +354,20 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, schar_T dummy[2]; int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], max_col - col, false, vcol); - // if we failed to emit a char, we still need to advance - cells = MAX(cells, 1); - + // If we failed to emit a char, we still need to put a space and advance. + if (cells < 1) { + schar_from_ascii(linebuf_char[col], ' '); + cells = 1; + } for (int c = 0; c < cells; c++) { linebuf_attr[col++] = attr; } + if (col < max_col && linebuf_char[col][0] == 0) { + // If the left half of a double-width char is overwritten, + // change the right half to a space so that grid redraws properly, + // but don't advance the current column. + schar_from_ascii(linebuf_char[col], ' '); + } vcol += cells; } return col; @@ -843,6 +863,73 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) } } +static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool *do_save) +{ + while (true) { + // we could already be inside an existing inline text with multiple chunks + if (!(wlv->virt_inline_i < kv_size(wlv->virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->win_col >= -1 && item->start_col == v) { + wlv->virt_inline = item->decor.virt_text; + wlv->virt_inline_i = 0; + item->win_col = -2; + break; + } + } + } + + if (wlv->n_extra == 0 || !wlv->extra_for_extmark) { + wlv->reset_extra_attr = false; + } + + if (wlv->n_extra <= 0 && wlv->virt_inline_i < kv_size(wlv->virt_inline)) { + VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i); + wlv->p_extra = vtc.text; + wlv->n_extra = (int)strlen(wlv->p_extra); + wlv->extra_for_extmark = true; + wlv->c_extra = NUL; + wlv->c_final = NUL; + wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; + wlv->n_attr = mb_charlen(vtc.text); + wlv->virt_inline_i++; + *do_save = true; + // If the text didn't reach until the first window + // column we need to skip cells. + if (wlv->skip_cells > 0) { + int virt_text_len = wlv->n_attr; + if (virt_text_len > wlv->skip_cells) { + int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells); + wlv->n_extra -= len; + wlv->p_extra += len; + wlv->n_attr -= wlv->skip_cells; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += wlv->skip_cells; + wlv->skip_cells = 0; + } else { + // the whole text is left of the window, drop + // it and advance to the next one + wlv->skip_cells -= virt_text_len; + // Skipped cells needed to be accounted for in vcol. + wlv->skipped_cells += virt_text_len; + wlv->n_attr = 0; + wlv->n_extra = 0; + + // go to the start so the next virtual text chunk can be selected. + continue; + } + } + } + break; + } +} + static bool check_mb_utf8(int *c, int *u8cc) { if (utf_char2len(*c) > 1) { @@ -909,6 +996,7 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) wlv->draw_state = WL_START; wlv->saved_n_extra = wlv->n_extra; wlv->saved_p_extra = wlv->p_extra; + wlv->saved_extra_for_extmark = wlv->extra_for_extmark; wlv->saved_c_extra = wlv->c_extra; wlv->saved_c_final = wlv->c_final; wlv->saved_char_attr = wlv->char_attr; @@ -923,6 +1011,8 @@ static void win_line_continue(winlinevars_T *wlv) if (wlv->saved_n_extra > 0) { // Continue item from end of wrapped line. wlv->n_extra = wlv->saved_n_extra; + wlv->saved_n_extra = 0; + wlv->extra_for_extmark = wlv->saved_extra_for_extmark; wlv->c_extra = wlv->saved_c_extra; wlv->c_final = wlv->saved_c_final; wlv->p_extra = wlv->saved_p_extra; @@ -938,7 +1028,7 @@ static void win_line_continue(winlinevars_T *wlv) /// @param lnum line to display /// @param startrow first row relative to window grid /// @param endrow last grid row to be redrawn -/// @param nochange not updating for changed text +/// @param mod_top top line updated for changed text /// @param number_only only update the number column /// @param foldinfo fold info for this line /// @param[in, out] providers decoration providers active this line @@ -946,7 +1036,7 @@ static void win_line_continue(winlinevars_T *wlv) /// or explicitly return `false`. /// /// @return the number of last row the line occupies. -int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool number_only, +int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bool number_only, foldinfo_T foldinfo, DecorProviders *providers, char **provider_err) { winlinevars_T wlv; // variables passed between functions @@ -959,14 +1049,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, static char *at_end_str = ""; // used for p_extra when displaying curwin->w_p_lcs_chars.eol // at end-of-line - bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; + const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; - int n_attr = 0; // chars with special attr int saved_attr2 = 0; // char_attr saved for n_attr int n_attr3 = 0; // chars with overruling special attr int saved_attr3 = 0; // char_attr saved for n_attr3 - int n_skip = 0; // nr of chars to skip for 'nowrap' + int n_skip = 0; // nr of chars to skip for 'nowrap' or + // concealing int fromcol_prev = -2; // start of inverting after cursor bool noinvcur = false; // don't invert the cursor @@ -978,10 +1068,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool area_highlighting = false; // Visual or incsearch highlighting in this line int vi_attr = 0; // attributes for Visual and incsearch highlighting int area_attr = 0; // attributes desired by highlighting + int saved_area_attr = 0; // idem for area_attr int search_attr = 0; // attributes desired by 'hlsearch' + int saved_search_attr = 0; // search_attr to be used when n_extra + // goes to zero int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int syntax_attr = 0; // attributes desired by syntax bool has_syntax = false; // this buffer has syntax highl. + int folded_attr = 0; // attributes for folded line int save_did_emsg; int eol_hl_off = 0; // 1 if highlighted char after EOL bool draw_color_col = false; // highlight colorcolumn @@ -1016,6 +1110,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, int prev_c1 = 0; // first composing char for prev_c bool search_attr_from_match = false; // if search_attr is from :match + bool saved_search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration int win_col_offset = 0; // offset for window columns @@ -1065,7 +1160,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.vcol_sbr = -1; buf_T *buf = wp->w_buffer; - bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); + const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, @@ -1109,12 +1204,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } - if (wp->w_p_spell - && !has_fold - && !end_fill - && *wp->w_s->b_p_spl != NUL - && !GA_EMPTY(&wp->w_s->b_langp) - && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { + if (!has_fold && !end_fill && spell_check_window(wp)) { // Prepare for spell checking. has_spell = true; extra_check = true; @@ -1137,12 +1227,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // When there was a sentence end in the previous line may require a // word starting with capital in this line. In line 1 always check - // the first word. - if (lnum != capcol_lnum) { - cap_col = -1; - } - if (lnum == 1) { + // the first word. Also check for sentence end in the line above + // when updating the first row in a window, the top line with + // changed text in a window, or if the previous line is folded. + if (lnum == 1 + || ((startrow == 0 || mod_top == lnum + || hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL)) + && check_need_cap(wp, lnum, 0))) { cap_col = 0; + } else if (lnum != capcol_lnum) { + cap_col = -1; } capcol_lnum = 0; } @@ -1428,6 +1522,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } + // If there the text doesn't reach to the desired column, need to skip + // "skip_cells" cells when virtual text follows. + if (!wp->w_p_wrap && v > wlv.vcol) { + wlv.skip_cells = (int)(v - wlv.vcol); + } + // Adjust for when the inverted text is before the screen, // and when the start of the inverted text is before the screen. if (wlv.tocol <= wlv.vcol) { @@ -1594,6 +1694,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, // Draw the 'statuscolumn' if option is set. if (statuscol.draw) { if (statuscol.textp == NULL) { + v = (ptr - line); get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); if (!end_fill) { // Get the line again as evaluating 'statuscolumn' may free it. @@ -1628,7 +1729,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.draw_state = WL_LINE; if (has_decor && wlv.row == startrow + wlv.filler_lines) { // hide virt_text on text hidden by 'nowrap' - decor_redraw_col(wp, wlv.vcol, wlv.off, true, &decor_state); + decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, true, &decor_state); } win_line_continue(&wlv); // use wlv.saved_ values } @@ -1664,7 +1765,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wlv.col == win_col_offset && wlv.n_extra == 0 && wlv.row == startrow + wlv.filler_lines) { - wlv.char_attr = win_hl_attr(wp, HLF_FL); + wlv.char_attr = folded_attr = win_hl_attr(wp, HLF_FL); linenr_T lnume = lnum + foldinfo.fi_lines - 1; memset(buf_fold, ' ', FOLD_TEXT_LEN); @@ -1705,7 +1806,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = 0; } - if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell)) { + int extmark_attr = 0; + if (wlv.draw_state == WL_LINE + && (area_highlighting || has_spell || extra_check)) { // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol || (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0 @@ -1723,39 +1826,65 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, area_active = false; } - if (!wlv.n_extra) { - // Check for start/end of 'hlsearch' and other matches. - // After end, check for start/end of next match. - // When another match, have to check for start again. - v = (ptr - line); - search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, - &has_match_conc, &match_conc, lcs_eol_one, - &on_last_col, &search_attr_from_match); - ptr = line + v; // "line" may have been changed - - // Do not allow a conceal over EOL otherwise EOL will be missed - // and bad things happen. - if (*ptr == NUL) { - has_match_conc = 0; + if (!has_fold) { + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, + selected, &decor_state); + + bool do_save = false; + handle_inline_virtual_text(wp, &wlv, v, &do_save); + if (do_save) { + // restore search_attr and area_attr when n_extra is down to zero + // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. + saved_search_attr = search_attr; + saved_area_attr = area_attr; + saved_search_attr_from_match = search_attr_from_match; + search_attr_from_match = false; + search_attr = 0; + area_attr = 0; + extmark_attr = 0; + n_skip = 0; + } } - } - if (wlv.diff_hlf != (hlf_T)0) { - if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start - && wlv.n_extra == 0) { - wlv.diff_hlf = HLF_TXD; // changed text - } - if (wlv.diff_hlf == HLF_TXD && ptr - line > change_end - && wlv.n_extra == 0) { - wlv.diff_hlf = HLF_CHD; // changed line + if (wlv.n_extra == 0) { + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. + v = (ptr - line); + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, + &has_match_conc, &match_conc, lcs_eol_one, + &on_last_col, &search_attr_from_match); + ptr = line + v; // "line" may have been changed + + // Do not allow a conceal over EOL otherwise EOL will be missed + // and bad things happen. + if (*ptr == NUL) { + has_match_conc = 0; + } } - wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); - // Overlay CursorLine onto diff-mode highlight. - if (wlv.cul_attr) { - wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine - ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), - hl_get_underline()) - : hl_combine_attr(wlv.line_attr, wlv.cul_attr); + + if (wlv.diff_hlf != (hlf_T)0) { + // When there is extra text (eg: virtual text) it gets the + // diff highlighting for the line, but not for changed text. + if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start + && wlv.n_extra == 0) { + wlv.diff_hlf = HLF_TXD; // changed text + } + if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) + || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { + wlv.diff_hlf = HLF_CHD; // changed line + } + wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); + // Overlay CursorLine onto diff-mode highlight. + if (wlv.cul_attr) { + wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine + ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), + hl_get_underline()) + : hl_combine_attr(wlv.line_attr, wlv.cul_attr); + } } } @@ -1786,6 +1915,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.char_attr = 0; } } + + if (folded_attr != 0) { + wlv.char_attr = hl_combine_attr(folded_attr, wlv.char_attr); + } } // Get the next character to put on the screen. @@ -1847,6 +1980,26 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.p_extra++; } wlv.n_extra--; + + // Only restore search_attr and area_attr after "n_extra" in + // the next screen line is also done. + if (wlv.n_extra <= 0) { + if (wlv.saved_n_extra <= 0) { + if (search_attr == 0) { + search_attr = saved_search_attr; + } + if (area_attr == 0 && *ptr != NUL) { + area_attr = saved_area_attr; + } + + if (wlv.extra_for_extmark) { + // wlv.extra_attr should be used at this position but not + // any further. + wlv.reset_extra_attr = true; + } + } + wlv.extra_for_extmark = false; + } } else if (foldinfo.fi_lines > 0) { // skip writing the buffer line itself c = NUL; @@ -1898,7 +2051,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = NUL; wlv.c_final = NUL; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr } @@ -1953,7 +2106,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_final = NUL; c = ' '; if (area_attr == 0 && search_attr == 0) { - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_AT); saved_attr2 = wlv.char_attr; // save current attr } @@ -2018,10 +2171,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } if (has_decor && v > 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - int extmark_attr = decor_redraw_col(wp, (colnr_T)v - 1, wlv.off, - selected, &decor_state); if (extmark_attr != 0) { if (!attr_pri) { wlv.char_attr = hl_combine_attr(wlv.char_attr, extmark_attr); @@ -2045,15 +2194,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, v = (ptr - line); if (has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; - if (!attr_pri) { - wlv.char_attr = hl_combine_attr(wlv.char_attr, syntax_attr); - } - if (c != 0 && ((!has_syntax && !no_plain_buffer) || can_spell)) { - char *prev_ptr; + char *prev_ptr = ptr - mb_l; + // do not calculate cap_col at the end of the line or when + // only white space is following + if (c != 0 && (*skipwhite(prev_ptr) != NUL) + && ((!has_syntax && !no_plain_buffer) || can_spell)) { char *p; - int len; hlf_T spell_hlf = HLF_COUNT; - prev_ptr = ptr - mb_l; v -= mb_l - 1; // Use nextline[] if possible, it has the start of the @@ -2064,9 +2211,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, p = prev_ptr; } cap_col -= (int)(prev_ptr - line); - size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange); + size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, mod_top == 0); assert(tmplen <= INT_MAX); - len = (int)tmplen; + int len = (int)tmplen; word_end = (int)v + len; // In Insert mode only highlight a word that @@ -2129,7 +2276,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p); + // do not want virtual text to be counted here + cts.cts_has_virt_text = false; wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1; + clear_chartabsize_arg(&cts); // We have just drawn the showbreak value, no need to add // space for it again. @@ -2161,7 +2311,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = ' '; } } - clear_chartabsize_arg(&cts); } in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' '); @@ -2192,7 +2341,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } else { c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2215,7 +2364,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, c = wp->w_p_lcs_chars.space; } - n_attr = 1; + wlv.n_attr = 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2331,7 +2480,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = wp->w_p_lcs_chars.tab2; } wlv.c_final = wp->w_p_lcs_chars.tab3; - n_attr = tab_len + 1; + wlv.n_attr = tab_len + 1; wlv.extra_attr = win_hl_attr(wp, HLF_0); saved_attr2 = wlv.char_attr; // save current attr mb_c = c; @@ -2358,15 +2507,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && wlv.line_attr == 0 && wlv.line_attr_lowprio == 0) { // In virtualedit, visual selections may extend beyond end of line - if (area_highlighting && virtual_active() - && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol) { - wlv.n_extra = 0; - } else { + if (!(area_highlighting && virtual_active() + && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { wlv.p_extra = at_end_str; - wlv.n_extra = 1; - wlv.c_extra = NUL; - wlv.c_final = NUL; } + wlv.n_extra = 0; } if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { c = wp->w_p_lcs_chars.eol; @@ -2376,7 +2521,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, lcs_eol_one = -1; ptr--; // put it back at the NUL wlv.extra_attr = win_hl_attr(wp, HLF_AT); - n_attr = 1; + wlv.n_attr = 1; mb_c = c; mb_utf8 = check_mb_utf8(&c, u8cc); } else if (c != NUL) { @@ -2405,7 +2550,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.n_extra = byte2cells(c) - 1; c = (uint8_t)(*wlv.p_extra++); } - n_attr = wlv.n_extra + 1; + wlv.n_attr = wlv.n_extra + 1; wlv.extra_attr = win_hl_attr(wp, HLF_8); saved_attr2 = wlv.char_attr; // save current attr mb_utf8 = false; // don't draw as UTF-8 @@ -2465,7 +2610,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } else if (n_skip == 0) { is_concealing = true; n_skip = 1; @@ -2500,8 +2645,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // Don't override visual selection highlighting. - if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) { wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr); + if (wlv.reset_extra_attr) { + wlv.reset_extra_attr = false; + wlv.extra_attr = 0; + // search_attr_from_match can be restored now that the extra_attr has been applied + search_attr_from_match = saved_search_attr_from_match; + } } // Handle the case where we are in column 0 but not on the first @@ -2521,7 +2672,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.c_extra = MB_FILLER_CHAR; wlv.c_final = NUL; wlv.n_extra = 1; - n_attr = 2; + wlv.n_attr = 2; wlv.extra_attr = win_hl_attr(wp, HLF_AT); } mb_c = c; @@ -2737,7 +2888,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, && !has_fold && (*ptr != NUL || lcs_eol_one > 0 - || (wlv.n_extra && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { c = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; @@ -2808,6 +2959,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.col++; // UTF-8: Put a 0 in the second screen char. linebuf_char[wlv.off][0] = 0; + linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1]; if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) { wlv.vcol++; } @@ -2857,7 +3009,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, wlv.boguscols += wlv.n_extra; } wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } if (utf_char2cells(mb_c) > 1) { @@ -2882,13 +3034,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, if (wlv.n_extra > 0) { wlv.vcol += wlv.n_extra; wlv.n_extra = 0; - n_attr = 0; + wlv.n_attr = 0; } } } else { n_skip--; } + // The skipped cells need to be accounted for in vcol. + if (wlv.draw_state > WL_STC && wlv.skipped_cells > 0) { + wlv.vcol += wlv.skipped_cells; + wlv.skipped_cells = 0; + } + // Only advance the "wlv.vcol" when after the 'number' or // 'relativenumber' column. if (wlv.draw_state > WL_STC @@ -2906,7 +3064,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, } // restore attributes after last 'listchars' or 'number' char - if (n_attr > 0 && wlv.draw_state == WL_LINE && --n_attr == 0) { + if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) { wlv.char_attr = saved_attr2; } diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 7f7c721379..4f79ba87af 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -2222,9 +2222,8 @@ static void win_update(win_T *wp, DecorProviders *providers) } // Display one line - row = win_line(wp, lnum, srow, - foldinfo.fi_lines ? srow : wp->w_grid.rows, - mod_top == 0, false, foldinfo, &line_providers, &provider_err); + row = win_line(wp, lnum, srow, foldinfo.fi_lines ? srow : wp->w_grid.rows, + mod_top, false, foldinfo, &line_providers, &provider_err); if (foldinfo.fi_lines == 0) { wp->w_lines[idx].wl_folded = false; @@ -2261,7 +2260,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // text doesn't need to be drawn, but the number column does. foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); - (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true, + (void)win_line(wp, lnum, srow, wp->w_grid.rows, mod_top, true, info, &line_providers, &provider_err); } @@ -2359,7 +2358,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // for ml_line_count+1 and only draw filler lines foldinfo_T info = { 0 }; row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, - false, false, info, &line_providers, &provider_err); + mod_top, false, info, &line_providers, &provider_err); } } else if (dollar_vcol == -1) { wp->w_botline = lnum; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2078fc4251..d6d5ff8ac7 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -53,6 +53,7 @@ #include "nvim/popupmenu.h" #include "nvim/pos.h" #include "nvim/search.h" +#include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" @@ -232,7 +233,7 @@ static void insert_enter(InsertState *s) stop_insert_mode = false; // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } @@ -876,12 +877,12 @@ static int insert_handle_key(InsertState *s) state_handle_k_event(); goto check_pum; - case K_COMMAND: // some command + case K_COMMAND: // <Cmd>command<CR> do_cmdline(NULL, getcmdkeycmd, NULL, 0); goto check_pum; case K_LUA: - map_execute_lua(); + map_execute_lua(false); check_pum: // nvim_select_popupmenu_item() can be called from the handling of @@ -1526,10 +1527,11 @@ void edit_unputchar(void) } } -// Called when p_dollar is set: display a '$' at the end of the changed text -// Only works when cursor is in the line that changes. -void display_dollar(colnr_T col) +/// Called when "$" is in 'cpoptions': display a '$' at the end of the changed +/// text. Only works when cursor is in the line that changes. +void display_dollar(colnr_T col_arg) { + colnr_T col = col_arg < 0 ? 0 : col_arg; colnr_T save_col; if (!redrawing()) { @@ -2527,6 +2529,7 @@ int oneleft(void) } curwin->w_set_curswant = true; + adjust_skipcol(); return OK; } @@ -3469,7 +3472,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = MODE_NORMAL; may_trigger_modechanged(); // need to position cursor again when on a TAB - if (gchar_cursor() == TAB) { + if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 13299b0253..52924bf9a5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3677,9 +3677,6 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv } else if (n2 >= len) { n2 = len; } - if (exclusive) { - n2--; - } if (n1 >= len || n2 < 0 || n1 > n2) { v = NULL; } else { @@ -4523,7 +4520,7 @@ bool garbage_collect(bool testing) // Channels { Channel *data; - map_foreach_value(&channels, data, { + pmap_foreach_value(&channels, data, { set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -4533,7 +4530,7 @@ bool garbage_collect(bool testing) // Timers { timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { set_ref_in_callback(&timer->callback, copyID, NULL, NULL); }) } @@ -5989,7 +5986,7 @@ void add_timer_info_all(typval_T *rettv) { tv_list_alloc_ret(rettv, map_size(&timers)); timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { if (!timer->stopped || timer->refcount > 1) { add_timer_info(rettv, timer); } @@ -6087,7 +6084,7 @@ static void timer_close_cb(TimeWatcher *tw, void *data) timer_T *timer = (timer_T *)data; multiqueue_free(timer->tw.events); callback_free(&timer->callback); - pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id); + pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id, NULL); timer_decref(timer); } @@ -6101,7 +6098,7 @@ static void timer_decref(timer_T *timer) void timer_stop_all(void) { timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { timer_stop(timer); }) } @@ -8657,7 +8654,7 @@ bool eval_has_provider(const char *feat) return false; } - char name[32]; // Normalized: "python_compiled" => "python". + char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. @@ -8715,7 +8712,7 @@ void ex_checkhealth(exarg_T *eap) { Error err = ERROR_INIT; MAXSIZE_TEMP_ARRAY(args, 1); - ADD_C(args, STRING_OBJ(cstr_as_string(eap->arg))); + ADD_C(args, CSTR_AS_OBJ(eap->arg)); NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); if (!ERROR_SET(&err)) { return; diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h index f1be5a1f69..7455130221 100644 --- a/src/nvim/eval/decode.h +++ b/src/nvim/eval/decode.h @@ -4,8 +4,8 @@ #include <msgpack.h> #include <stddef.h> -#include "nvim/eval/typval.h" -#include "nvim/globals.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/decode.h.generated.h" diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h index 42abf77f4a..e8bf147245 100644 --- a/src/nvim/eval/executor.h +++ b/src/nvim/eval/executor.h @@ -1,7 +1,7 @@ #ifndef NVIM_EVAL_EXECUTOR_H #define NVIM_EVAL_EXECUTOR_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" extern char *e_list_index_out_of_range_nr; diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 1ae031a952..65a95196de 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -6,7 +6,6 @@ #include "nvim/api/private/dispatch.h" #include "nvim/buffer_defs.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/types.h" diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h index 3185750c3b..66e959ced5 100644 --- a/src/nvim/eval/gc.h +++ b/src/nvim/eval/gc.h @@ -1,7 +1,6 @@ #ifndef NVIM_EVAL_GC_H #define NVIM_EVAL_GC_H -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" extern dict_T *gc_first_dict; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 5755178b18..cb8f8ce44d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2166,6 +2166,16 @@ varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, return tv_get_number(&di->di_tv); } +varnumber_T tv_dict_get_bool(const dict_T *const d, const char *const key, const int def) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return def; + } + return tv_get_bool(&di->di_tv); +} + /// Converts a dict to an environment char **tv_dict_to_env(dict_T *denv) { @@ -4049,6 +4059,18 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) return (ret_error == NULL ? -1 : 0); } +varnumber_T tv_get_bool(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + return tv_get_number_chk(tv, NULL); +} + +varnumber_T tv_get_bool_chk(const typval_T *const tv, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + return tv_get_number_chk(tv, ret_error); +} + /// Get the line number from VimL object /// /// @param[in] tv Object to get value from. Is expected to be a number or diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 84e4067f9d..767fd706b3 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -567,8 +567,4 @@ EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE); # include "eval/typval.h.generated.h" #endif -#define tv_get_bool tv_get_number -#define tv_get_bool_chk tv_get_number_chk -#define tv_dict_get_bool tv_dict_get_number_def - #endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 2f19144da3..171b0417d0 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -11,7 +11,7 @@ #include <string.h> #include "klib/kvec.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" /// Type of the stack entry diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index fbb5e8d10c..a52b8d3f18 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -467,12 +467,10 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b /// @param name function name void emsg_funcname(const char *errmsg, const char *name) { - char *p; + char *p = (char *)name; - if ((uint8_t)(*name) == K_SPECIAL) { + if ((uint8_t)name[0] == K_SPECIAL && name[1] != NUL && name[2] != NUL) { p = concat_str("<SNR>", name + 3); - } else { - p = (char *)name; } semsg(_(errmsg), p); @@ -1863,8 +1861,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part // Check for hard coded <SNR>: already translated function ID (from a user // command). - if ((unsigned char)(*pp)[0] == K_SPECIAL && (unsigned char)(*pp)[1] == KS_EXTRA - && (*pp)[2] == KE_SNR) { + if ((uint8_t)(*pp)[0] == K_SPECIAL && (uint8_t)(*pp)[1] == KS_EXTRA && (*pp)[2] == KE_SNR) { *pp += 3; len = get_id_len((const char **)pp) + 3; return xmemdupz(start, (size_t)len); @@ -2140,7 +2137,6 @@ void ex_function(exarg_T *eap) char *theline; char *line_to_free = NULL; char c; - int saved_did_emsg; bool saved_wait_return = need_wait_return; char *name = NULL; char *p; @@ -2234,7 +2230,7 @@ void ex_function(exarg_T *eap) // An error in a function call during evaluation of an expression in magic // braces should not cause the function not to be defined. - saved_did_emsg = did_emsg; + const int saved_did_emsg = did_emsg; did_emsg = false; // @@ -2896,9 +2892,9 @@ char *get_user_func_name(expand_T *xp, int idx) cat_func_name(IObuff, IOSIZE, fp); if (xp->xp_context != EXPAND_USER_FUNC) { - STRCAT(IObuff, "("); + xstrlcat(IObuff, "(", IOSIZE); if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) { - STRCAT(IObuff, ")"); + xstrlcat(IObuff, ")", IOSIZE); } } return IObuff; @@ -3509,7 +3505,7 @@ char *get_return_cmd(void *rettv) s = ""; } - STRCPY(IObuff, ":return "); + xstrlcpy(IObuff, ":return ", IOSIZE); xstrlcpy(IObuff + 8, s, IOSIZE - 8); if (strlen(s) + 8 >= IOSIZE) { STRCPY(IObuff + IOSIZE - 4, "..."); diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 4a98afb00e..562c549b4b 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -5,7 +5,6 @@ #include <stddef.h> #include "nvim/eval.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index e0057faffb..39fed08c77 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -5,7 +5,6 @@ #include <stddef.h> #include <stdint.h> -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index dbfd1088d2..1d8c3c0cf4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -54,7 +54,6 @@ #include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/input.h" -#include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -1926,6 +1925,9 @@ void do_wqall(exarg_T *eap) int save_forceit = eap->forceit; if (eap->cmdidx == CMD_xall || eap->cmdidx == CMD_wqall) { + if (before_quit_all(eap) == FAIL) { + return; + } exiting = true; } @@ -4801,29 +4803,3 @@ void ex_oldfiles(exarg_T *eap) } } } - -void ex_trust(exarg_T *eap) -{ - const char *const p = skiptowhite(eap->arg); - char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); - const char *action = "allow"; - const char *path = skipwhite(p); - - if (strcmp(arg1, "++deny") == 0) { - action = "deny"; - } else if (strcmp(arg1, "++remove") == 0) { - action = "remove"; - } else if (*arg1 != '\0') { - semsg(e_invarg2, arg1); - goto theend; - } - - if (path[0] == '\0') { - path = NULL; - } - - nlua_trust(action, path); - -theend: - xfree(arg1); -} diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 39bff3e35d..148065e096 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/os/time.h" diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 2acedb5ec3..568d4d38ba 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -4,7 +4,7 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/normal.h" #include "nvim/pos.h" #include "nvim/regexp_defs.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index adb17f2cfd..9666d80de2 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1454,7 +1454,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er } // Fail if command is invalid if (eap->cmdidx == CMD_SIZE) { - STRCPY(IObuff, _(e_not_an_editor_command)); + xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE); // If the modifier was parsed OK the error must be in the following command char *cmdname = after_modifier ? after_modifier : cmdline; append_command(cmdname); @@ -2044,7 +2044,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // Check for wrong commands. if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { - STRCPY(IObuff, _(e_not_an_editor_command)); + xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE); // If the modifier was parsed OK the error must be in the following // command char *cmdname = after_modifier ? after_modifier : *cmdlinep; @@ -2321,7 +2321,7 @@ doend: if (errormsg != NULL && *errormsg != NUL && !did_emsg) { if (flags & DOCMD_VERBOSE) { if (errormsg != IObuff) { - STRCPY(IObuff, errormsg); + xstrlcpy(IObuff, errormsg, IOSIZE); errormsg = IObuff; } append_command(*ea.cmdlinep); @@ -2888,7 +2888,7 @@ static void append_command(char *cmd) d -= utf_head_off(IObuff, d); STRCPY(d, "..."); } - STRCAT(IObuff, ": "); + xstrlcat(IObuff, ": ", IOSIZE); d = IObuff + strlen(IObuff); while (*s != NUL && d - IObuff + 5 < IOSIZE) { if ((uint8_t)s[0] == 0xc2 && (uint8_t)s[1] == 0xa0) { @@ -4589,8 +4589,9 @@ static void ex_cquit(exarg_T *eap) getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE); } -/// ":qall": try to quit all windows -static void ex_quit_all(exarg_T *eap) +/// Do preparations for "qall" and "wqall". +/// Returns FAIL when quitting should be aborted. +int before_quit_all(exarg_T *eap) { if (cmdwin_type != 0) { if (eap->forceit) { @@ -4598,19 +4599,28 @@ static void ex_quit_all(exarg_T *eap) } else { cmdwin_result = K_XF2; } - return; + return FAIL; } // Don't quit while editing the command line. if (text_locked()) { text_locked_msg(); - return; + return FAIL; } if (before_quit_autocmds(curwin, true, eap->forceit)) { - return; + return FAIL; } + return OK; +} + +/// ":qall": try to quit all windows +static void ex_quit_all(exarg_T *eap) +{ + if (before_quit_all(eap) == FAIL) { + return; + } exiting = true; if (eap->forceit || !check_changed_any(false, false)) { getout(0); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index af2ec3356f..dd23f6ece9 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -127,6 +127,7 @@ typedef struct command_line_state { int break_ctrl_c; expand_T xpc; long *b_im_ptr; + buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid } CommandLineState; typedef struct cmdpreview_win_info { @@ -736,7 +737,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool clea } else { s->b_im_ptr = &curbuf->b_p_imsearch; } - + s->b_im_ptr_buf = curbuf; if (*s->b_im_ptr == B_IMODE_LMAP) { State |= MODE_LANGMAP; } @@ -1140,7 +1141,7 @@ static int command_line_execute(VimState *state, int key) } else if (s->c == K_COMMAND) { do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT); } else { - map_execute_lua(); + map_execute_lua(false); } // nvim_select_popupmenu_item() can be called from the handling of @@ -1538,20 +1539,21 @@ static int command_line_erase_chars(CommandLineState *s) /// language :lmap mappings and/or Input Method. static void command_line_toggle_langmap(CommandLineState *s) { + long *b_im_ptr = buf_valid(s->b_im_ptr_buf) ? s->b_im_ptr : NULL; if (map_to_exists_mode("", MODE_LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. State ^= MODE_LANGMAP; - if (s->b_im_ptr != NULL) { + if (b_im_ptr != NULL) { if (State & MODE_LANGMAP) { - *s->b_im_ptr = B_IMODE_LMAP; + *b_im_ptr = B_IMODE_LMAP; } else { - *s->b_im_ptr = B_IMODE_NONE; + *b_im_ptr = B_IMODE_NONE; } } } - if (s->b_im_ptr != NULL) { - if (s->b_im_ptr == &curbuf->b_p_iminsert) { + if (b_im_ptr != NULL) { + if (b_im_ptr == &curbuf->b_p_iminsert) { set_iminsert_global(curbuf); } else { set_imsearch_global(curbuf); @@ -3385,7 +3387,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line) } else { Array item = arena_array(&arena, 2); ADD_C(item, INTEGER_OBJ(0)); - ADD_C(item, STRING_OBJ(cstr_as_string(line->cmdbuff))); + ADD_C(item, CSTR_AS_OBJ(line->cmdbuff)); content = arena_array(&arena, 1); ADD_C(content, ARRAY_OBJ(item)); } @@ -3412,7 +3414,7 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) Array item = ARRAY_DICT_INIT; ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_as_string(buf))); + ADD(item, CSTR_AS_OBJ(buf)); Array content = ARRAY_DICT_INIT; ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 61ac4b69c5..b21fb0506d 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include "klib/kvec.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 727be1562c..f4a6a02682 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -50,11 +50,6 @@ # include "extmark.c.generated.h" #endif -static uint32_t *buf_ns_ref(buf_T *buf, uint32_t ns_id, bool put) -{ - return map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, put); -} - /// Create or update an extmark /// /// must not be used during iteration! @@ -62,7 +57,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity, ExtmarkOp op, Error *err) { - uint32_t *ns = buf_ns_ref(buf, ns_id, true); + uint32_t *ns = map_put_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL, NULL); uint32_t id = idp ? *idp : 0; bool decor_full = false; @@ -153,6 +148,9 @@ revised: } if (decor) { + if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) { + buf->b_virt_text_inline++; + } if (kv_size(decor->virt_lines)) { buf->b_virt_line_blocks++; } @@ -237,7 +235,7 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r bool all_ns = (ns_id == 0); uint32_t *ns = NULL; if (!all_ns) { - ns = buf_ns_ref(buf, ns_id, false); + ns = map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL); if (!ns) { // nothing to do return false; @@ -258,15 +256,14 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r || (mark.pos.row == u_row && mark.pos.col > u_col)) { break; } - ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark), - false); + ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark), NULL); if (del_status) { marktree_del_itr(buf->b_marktree, itr, false); if (*del_status >= 0) { // we had a decor_id DecorItem it = kv_A(decors, *del_status); decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } - map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark)); + map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark), NULL); continue; } @@ -294,7 +291,7 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r } uint64_t id; ssize_t decor_id; - map_foreach(&delete_set, id, decor_id, { + map_foreach(ssize_t, &delete_set, id, decor_id, { mtkey_t mark = marktree_lookup(buf->b_marktree, id, itr); assert(marktree_itr_valid(itr)); marktree_del_itr(buf->b_marktree, itr, false); @@ -303,7 +300,7 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } }); - map_clear(uint64_t, ssize_t)(&delete_set); + map_clear(uint64_t, &delete_set); kv_size(decors) = 0; return marks_cleared; } @@ -424,8 +421,8 @@ void extmark_free_all(buf_T *buf) marktree_clear(buf->b_marktree); - map_destroy(uint32_t, uint32_t)(buf->b_extmark_ns); - map_init(uint32_t, uint32_t, buf->b_extmark_ns); + map_destroy(uint32_t, buf->b_extmark_ns); + *buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT; } /// Save info for undo/redo of set marks diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d659987686..e60007bf88 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1693,22 +1693,22 @@ failed: #ifdef UNIX if (S_ISFIFO(perm)) { // fifo - STRCAT(IObuff, _("[fifo]")); + xstrlcat(IObuff, _("[fifo]"), IOSIZE); c = true; } if (S_ISSOCK(perm)) { // or socket - STRCAT(IObuff, _("[socket]")); + xstrlcat(IObuff, _("[socket]"), IOSIZE); c = true; } # ifdef OPEN_CHR_FILES if (S_ISCHR(perm)) { // or character special - STRCAT(IObuff, _("[character special]")); + xstrlcat(IObuff, _("[character special]"), IOSIZE); c = true; } # endif #endif if (curbuf->b_p_ro) { - STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")); + xstrlcat(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"), IOSIZE); c = true; } if (read_no_eol_lnum) { @@ -1716,18 +1716,18 @@ failed: c = true; } if (ff_error == EOL_DOS) { - STRCAT(IObuff, _("[CR missing]")); + xstrlcat(IObuff, _("[CR missing]"), IOSIZE); c = true; } if (split) { - STRCAT(IObuff, _("[long lines split]")); + xstrlcat(IObuff, _("[long lines split]"), IOSIZE); c = true; } if (notconverted) { - STRCAT(IObuff, _("[NOT converted]")); + xstrlcat(IObuff, _("[NOT converted]"), IOSIZE); c = true; } else if (converted) { - STRCAT(IObuff, _("[converted]")); + xstrlcat(IObuff, _("[converted]"), IOSIZE); c = true; } if (conv_error != 0) { @@ -1739,7 +1739,7 @@ failed: _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte); c = true; } else if (error) { - STRCAT(IObuff, _("[READ ERRORS]")); + xstrlcat(IObuff, _("[READ ERRORS]"), IOSIZE); c = true; } if (msg_add_fileformat(fileformat)) { @@ -2128,17 +2128,17 @@ bool msg_add_fileformat(int eol_type) { #ifndef USE_CRNL if (eol_type == EOL_DOS) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]")); + xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"), IOSIZE); return true; } #endif if (eol_type == EOL_MAC) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]")); + xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"), IOSIZE); return true; } #ifdef USE_CRNL if (eol_type == EOL_UNIX) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]")); + xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"), IOSIZE); return true; } #endif @@ -2170,8 +2170,7 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars) /// Append message for missing line separator to IObuff. void msg_add_eol(void) { - STRCAT(IObuff, - shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]")); + xstrlcat(IObuff, shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]"), IOSIZE); } bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST @@ -3482,8 +3481,7 @@ void vim_deltempdir(void) char *vim_gettempdir(void) { static int notfound = 0; - bool exists = false; - if (vim_tempdir == NULL || !(exists = os_isdir(vim_tempdir))) { + if (vim_tempdir == NULL || !os_isdir(vim_tempdir)) { if (vim_tempdir != NULL) { notfound++; if (notfound == 1) { diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index a4566d754b..5eb2689233 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -2,7 +2,6 @@ #define NVIM_FILEIO_H #include "nvim/buffer_defs.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/garray.h" #include "nvim/os/os.h" diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index 76b372eac2..b9fae7d0fe 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -55,6 +55,7 @@ defsfile:write(string.format([[ #include "nvim/help.h" #include "nvim/indent.h" #include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/match.h" diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index a0e8350287..5c1366c5b2 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -92,8 +92,8 @@ static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 }; static int typeahead_char = 0; // typeahead char that's not flushed -// when block_redo is true redo buffer will not be changed -// used by edit() to repeat insertions and 'V' command for redoing +/// When block_redo is true the redo buffer will not be changed. +/// Used by edit() to repeat insertions. static int block_redo = false; static int KeyNoremap = 0; // remapping flags @@ -134,6 +134,10 @@ static size_t last_recorded_len = 0; // number of last recorded chars #endif static const char e_recursive_mapping[] = N_("E223: Recursive mapping"); +static const char e_cmd_mapping_must_end_with_cr[] + = N_("E1255: <Cmd> mapping must end with <CR>"); +static const char e_cmd_mapping_must_end_with_cr_before_second_cmd[] + = N_("E1136: <Cmd> mapping must end with <CR> before second <Cmd>"); // Free and clear a buffer. void free_buff(buffheader_T *buf) @@ -550,6 +554,25 @@ void AppendToRedobuffLit(const char *str, int len) } } +/// Append "s" to the redo buffer, leaving 3-byte special key codes unmodified +/// and escaping other K_SPECIAL bytes. +void AppendToRedobuffSpec(const char *s) +{ + if (block_redo) { + return; + } + + while (*s != NUL) { + if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { + // Insert special key literally. + add_buff(&redobuff, s, 3L); + s += 3; + } else { + add_char_buff(&redobuff, mb_cptr2char_adv(&s)); + } + } +} + /// Append a character to the redo buffer. /// Translates special keys, NUL, K_SPECIAL and multibyte characters. void AppendCharToRedobuff(int c) @@ -2884,7 +2907,8 @@ int fix_input_buffer(uint8_t *buf, int len) return len; } -/// Get command argument for <Cmd> key +/// Function passed to do_cmdline() to get the command after a <Cmd> key from +/// typeahead. char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) { garray_T line_ga; @@ -2894,6 +2918,7 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) ga_init(&line_ga, 1, 32); + // no mapping for these characters no_mapping++; got_int = false; @@ -2903,16 +2928,17 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) if (vgetorpeek(false) == NUL) { // incomplete <Cmd> is an error, because there is not much the user // could do in this state. - emsg(e_cmdmap_err); + emsg(_(e_cmd_mapping_must_end_with_cr)); aborted = true; break; } // Get one character at a time. c1 = vgetorpeek(true); + // Get two extra bytes for special keys if (c1 == K_SPECIAL) { - c1 = vgetorpeek(true); // no mapping for these chars + c1 = vgetorpeek(true); c2 = vgetorpeek(true); if (c1 == KS_MODIFIER) { cmod = c2; @@ -2928,8 +2954,8 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) } else if (c1 == ESC) { aborted = true; } else if (c1 == K_COMMAND) { - // special case to give nicer error message - emsg(e_cmdmap_repeated); + // give a nicer error message for this special case + emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd)); aborted = true; } else if (c1 == K_SNR) { ga_concat(&line_ga, "<SNR>"); @@ -2960,7 +2986,12 @@ char *getcmdkeycmd(int promptc, void *cookie, int indent, bool do_concat) return line_ga.ga_data; } -bool map_execute_lua(void) +/// Handle a Lua mapping: get its LuaRef from typeahead and execute it. +/// +/// @param may_repeat save the LuaRef for redoing with "." later +/// +/// @return false if getting the LuaRef was aborted, true otherwise +bool map_execute_lua(bool may_repeat) { garray_T line_ga; int c1 = -1; @@ -2992,6 +3023,10 @@ bool map_execute_lua(void) } LuaRef ref = (LuaRef)atoi(line_ga.ga_data); + if (may_repeat) { + repeat_luaref = ref; + } + Error err = ERROR_INIT; Array args = ARRAY_DICT_INIT; nlua_call_ref(ref, NULL, args, false, &err); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 698f4a98c7..bedb529d04 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -993,7 +993,8 @@ EXTERN const char e_notset[] INIT(= N_("E764: Option '%s' is not set")); EXTERN const char e_invalidreg[] INIT(= N_("E850: Invalid register name")); EXTERN const char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); EXTERN const char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); -EXTERN const char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode")); +EXTERN const char e_menu_only_exists_in_another_mode[] +INIT(= N_("E328: Menu only exists in another mode")); EXTERN const char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window")); EXTERN const char e_listarg[] INIT(= N_("E686: Argument of %s must be a List")); EXTERN const char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); @@ -1003,10 +1004,6 @@ EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d")); EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s")); -EXTERN const char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>")); -EXTERN const char e_cmdmap_repeated[] -INIT(= N_("E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); - EXTERN const char e_api_error[] INIT(= N_("E5555: API call: %s")); EXTERN const char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called in a lua loop callback")); diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 037606c38f..76dd2a073a 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -316,8 +316,7 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int // When at the start of the text and overwriting the right half of a // two-cell character in the same grid, truncate that into a '>'. if (ptr == text && col > 0 && grid->chars[off][0] == 0) { - grid->chars[off - 1][0] = '>'; - grid->chars[off - 1][1] = 0; + schar_from_ascii(grid->chars[off - 1], '>'); } schar_copy(grid->chars[off], buf); diff --git a/src/nvim/help.c b/src/nvim/help.c index 633e9df244..d412f3a098 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -22,6 +22,7 @@ #include "nvim/globals.h" #include "nvim/help.h" #include "nvim/macros.h" +#include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index e2f3e2aafa..fad113adc5 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -50,7 +50,7 @@ static Map(int, int) blendthrough_attr_entries = MAP_INIT; /// highlight entries private to a namespace static Map(ColorKey, ColorItem) ns_hls; typedef int NSHlAttr[HLF_COUNT + 1]; -static PMap(handle_T) ns_hl_attr; +static PMap(int) ns_hl_attr; void highlight_init(void) { @@ -207,7 +207,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault) if (!valid_item && p->hl_def != LUA_NOREF && !recursive) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ((Integer)ns_id)); - ADD_C(args, STRING_OBJ(cstr_to_string(syn_id2name(hl_id)))); + ADD_C(args, CSTR_TO_OBJ(syn_id2name(hl_id))); ADD_C(args, BOOLEAN_OBJ(link)); // TODO(bfredl): preload the "global" attr dict? @@ -277,7 +277,7 @@ bool hl_check_ns(void) hl_attr_active = highlight_attr; if (ns > 0) { update_ns_hl(ns); - NSHlAttr *hl_def = (NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns); + NSHlAttr *hl_def = (NSHlAttr *)pmap_get(int)(&ns_hl_attr, ns); if (hl_def) { hl_attr_active = *hl_def; } @@ -335,7 +335,7 @@ void update_window_hl(win_T *wp, bool invalid) if (ns_id != wp->w_ns_hl_active || wp->w_ns_hl_attr == NULL) { wp->w_ns_hl_active = ns_id; - wp->w_ns_hl_attr = *(NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns_id); + wp->w_ns_hl_attr = *(NSHlAttr *)pmap_get(int)(&ns_hl_attr, ns_id); if (!wp->w_ns_hl_attr) { // No specific highlights, use the defaults. wp->w_ns_hl_attr = highlight_attr; @@ -398,6 +398,15 @@ void update_window_hl(win_T *wp, bool invalid) } else { wp->w_hl_attr_normalnc = hl_def[HLF_INACTIVE]; } + + // if blend= attribute is not set, 'winblend' value overrides it. + if (wp->w_floating && wp->w_p_winbl > 0) { + HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normalnc); + if (entry.attr.hl_blend == -1) { + entry.attr.hl_blend = (int)wp->w_p_winbl; + wp->w_hl_attr_normalnc = get_attr_entry(entry); + } + } } void update_ns_hl(int ns_id) @@ -410,7 +419,7 @@ void update_ns_hl(int ns_id) return; } - NSHlAttr **alloc = (NSHlAttr **)pmap_ref(handle_T)(&ns_hl_attr, ns_id, true); + NSHlAttr **alloc = (NSHlAttr **)pmap_put_ref(int)(&ns_hl_attr, ns_id, NULL, NULL); if (*alloc == NULL) { *alloc = xmalloc(sizeof(**alloc)); } @@ -482,28 +491,28 @@ void clear_hl_tables(bool reinit) { if (reinit) { kv_size(attr_entries) = 1; - map_clear(HlEntry, int)(&attr_entry_ids); - map_clear(int, int)(&combine_attr_entries); - map_clear(int, int)(&blend_attr_entries); - map_clear(int, int)(&blendthrough_attr_entries); + map_clear(HlEntry, &attr_entry_ids); + map_clear(int, &combine_attr_entries); + map_clear(int, &blend_attr_entries); + map_clear(int, &blendthrough_attr_entries); memset(highlight_attr_last, -1, sizeof(highlight_attr_last)); highlight_attr_set_all(); highlight_changed(); screen_invalidate_highlights(); } else { kv_destroy(attr_entries); - map_destroy(HlEntry, int)(&attr_entry_ids); - map_destroy(int, int)(&combine_attr_entries); - map_destroy(int, int)(&blend_attr_entries); - map_destroy(int, int)(&blendthrough_attr_entries); - map_destroy(ColorKey, ColorItem)(&ns_hls); + map_destroy(HlEntry, &attr_entry_ids); + map_destroy(int, &combine_attr_entries); + map_destroy(int, &blend_attr_entries); + map_destroy(int, &blendthrough_attr_entries); + map_destroy(ColorKey, &ns_hls); } } void hl_invalidate_blends(void) { - map_clear(int, int)(&blend_attr_entries); - map_clear(int, int)(&blendthrough_attr_entries); + map_clear(int, &blend_attr_entries); + map_clear(int, &blendthrough_attr_entries); highlight_changed(); update_window_hl(curwin, true); } @@ -1132,21 +1141,21 @@ static void hl_inspect_impl(Array *arr, int attr) HlEntry e = kv_A(attr_entries, attr); switch (e.kind) { case kHlSyntax: - PUT(item, "kind", STRING_OBJ(cstr_to_string("syntax"))); + PUT(item, "kind", CSTR_TO_OBJ("syntax")); PUT(item, "hi_name", - STRING_OBJ(cstr_to_string(syn_id2name(e.id1)))); + CSTR_TO_OBJ(syn_id2name(e.id1))); break; case kHlUI: - PUT(item, "kind", STRING_OBJ(cstr_to_string("ui"))); + PUT(item, "kind", CSTR_TO_OBJ("ui")); const char *ui_name = (e.id1 == -1) ? "Normal" : hlf_names[e.id1]; - PUT(item, "ui_name", STRING_OBJ(cstr_to_string(ui_name))); + PUT(item, "ui_name", CSTR_TO_OBJ(ui_name)); PUT(item, "hi_name", - STRING_OBJ(cstr_to_string(syn_id2name(e.id2)))); + CSTR_TO_OBJ(syn_id2name(e.id2))); break; case kHlTerminal: - PUT(item, "kind", STRING_OBJ(cstr_to_string("term"))); + PUT(item, "kind", CSTR_TO_OBJ("term")); break; case kHlCombine: diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index a0f0a947b8..09e7d04e02 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -697,10 +697,18 @@ int load_colors(char *name) char *buf = xmalloc(buflen); apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); snprintf(buf, buflen, "colors/%s.vim", name); - int retval = source_runtime(buf, DIP_START + DIP_OPT); + int retval = source_runtime(buf, 0); if (retval == FAIL) { snprintf(buf, buflen, "colors/%s.lua", name); - retval = source_runtime(buf, DIP_START + DIP_OPT); + retval = source_runtime(buf, 0); + } + if (retval == FAIL) { + snprintf(buf, buflen, "colors/%s.vim", name); + retval = source_runtime(buf, DIP_NORTP + DIP_START + DIP_OPT); + } + if (retval == FAIL) { + snprintf(buf, buflen, "colors/%s.lua", name); + retval = source_runtime(buf, DIP_NORTP + DIP_START + DIP_OPT); } xfree(buf); if (retval == OK) { @@ -1420,7 +1428,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) void free_highlight(void) { ga_clear(&highlight_ga); - map_destroy(cstr_t, int)(&highlight_unames); + map_destroy(cstr_t, &highlight_unames); arena_mem_free(arena_finish(&highlight_arena)); } @@ -1548,8 +1556,11 @@ static bool hlgroup2dict(Dictionary *hl, NS ns_id, int hl_id, Arena *arena) HlAttrs attr = syn_attr2entry(ns_id == 0 ? sgp->sg_attr : ns_get_hl(&ns_id, hl_id, false, sgp->sg_set)); *hl = arena_dict(arena, HLATTRS_DICT_SIZE + 1); + if (attr.rgb_ae_attr & HL_DEFAULT) { + PUT_C(*hl, "default", BOOLEAN_OBJ(true)); + } if (link > 0) { - PUT_C(*hl, "link", STRING_OBJ(cstr_as_string(hl_table[link - 1].sg_name))); + PUT_C(*hl, "link", CSTR_AS_OBJ(hl_table[link - 1].sg_name)); } Dictionary hl_cterm = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(hl, NULL, attr, true, true); diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index ddc0acfbfa..241dd65cc7 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -6,7 +6,7 @@ #include <stdint.h> #include "nvim/api/private/defs.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 9586b56f3c..8c1d8addcd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -109,11 +109,16 @@ typedef enum luv_err_type { kThreadCallback, } luv_err_t; +lua_State *get_global_lstate(void) +{ + return global_lstate; +} + /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. /// @param[in] msg Message base, must contain one `%s`. -static void nlua_error(lua_State *const lstate, const char *const msg) +void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { size_t len; @@ -150,7 +155,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg) /// @param lstate Lua interpreter state /// @param[in] nargs Number of arguments expected by the function being called. /// @param[in] nresults Number of results the function returns. -static int nlua_pcall(lua_State *lstate, int nargs, int nresults) +int nlua_pcall(lua_State *lstate, int nargs, int nresults) { lua_getglobal(lstate, "debug"); lua_getfield(lstate, -1, "traceback"); @@ -336,7 +341,7 @@ static int nlua_init_argv(lua_State *const L, char **argv, int argc, int lua_arg lua_pushstring(L, argv[lua_arg0 - 1]); lua_rawseti(L, -2, 0); // _G.arg[0] = "foo.lua" - for (; lua_arg0 >= 0 && i + lua_arg0 < argc; i++) { + for (; i + lua_arg0 < argc; i++) { lua_pushstring(L, argv[i + lua_arg0]); lua_rawseti(L, -2, i + 1); // _G.arg[i+1] = "--foo" } @@ -836,7 +841,7 @@ void nlua_run_script(char **argv, int argc, int lua_arg0) exit(lua_ok ? 0 : 1); } -lua_State *nlua_init_state(bool thread) +static lua_State *nlua_init_state(bool thread) { // If it is called from the main thread, it will attempt to rebuild the cache. const uv_thread_t self = uv_thread_self(); @@ -910,12 +915,13 @@ static void nlua_common_free_all_mem(lua_State *lstate) if (nlua_track_refs) { // in case there are leaked luarefs, leak the associated memory // to get LeakSanitizer stacktraces on exit - pmap_destroy(handle_T)(&ref_state->ref_markers); + map_destroy(int, &ref_state->ref_markers); } #endif lua_close(lstate); } + static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -1285,7 +1291,7 @@ LuaRef nlua_ref(lua_State *lstate, nlua_ref_state_t *ref_state, int index) #ifdef NLUA_TRACK_REFS if (nlua_track_refs) { // dummy allocation to make LeakSanitizer track our luarefs - pmap_put(handle_T)(&ref_state->ref_markers, ref, xmalloc(3)); + pmap_put(int)(&ref_state->ref_markers, ref, xmalloc(3)); } #endif } @@ -1305,7 +1311,7 @@ void nlua_unref(lua_State *lstate, nlua_ref_state_t *ref_state, LuaRef ref) #ifdef NLUA_TRACK_REFS // NB: don't remove entry from map to track double-unref if (nlua_track_refs) { - xfree(pmap_get(handle_T)(&ref_state->ref_markers, ref)); + xfree(pmap_get(int)(&ref_state->ref_markers, ref)); } #endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); @@ -2275,80 +2281,3 @@ plain: kv_printf(str, "<Lua %d>", ref); return str.items; } - -char *nlua_read_secure(const char *path) -{ - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "read"); - lua_pushstring(lstate, path); - if (nlua_pcall(lstate, 1, 1)) { - nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); - lua_settop(lstate, top); - return NULL; - } - - size_t len = 0; - const char *contents = lua_tolstring(lstate, -1, &len); - char *buf = NULL; - if (contents != NULL) { - // Add one to include trailing null byte - buf = xcalloc(len + 1, sizeof(char)); - memcpy(buf, contents, len + 1); - } - - lua_settop(lstate, top); - return buf; -} - -bool nlua_trust(const char *action, const char *path) -{ - lua_State *const lstate = global_lstate; - const int top = lua_gettop(lstate); - - lua_getglobal(lstate, "vim"); - lua_getfield(lstate, -1, "secure"); - lua_getfield(lstate, -1, "trust"); - - lua_newtable(lstate); - lua_pushstring(lstate, "action"); - lua_pushstring(lstate, action); - lua_settable(lstate, -3); - if (path == NULL) { - lua_pushstring(lstate, "bufnr"); - lua_pushnumber(lstate, 0); - lua_settable(lstate, -3); - } else { - lua_pushstring(lstate, "path"); - lua_pushstring(lstate, path); - lua_settable(lstate, -3); - } - - if (nlua_pcall(lstate, 1, 2)) { - nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); - lua_settop(lstate, top); - return false; - } - - bool success = lua_toboolean(lstate, -2); - const char *msg = lua_tostring(lstate, -1); - if (msg != NULL) { - if (success) { - if (strcmp(action, "allow") == 0) { - smsg("Allowed \"%s\" in trust database.", msg); - } else if (strcmp(action, "deny") == 0) { - smsg("Denied \"%s\" in trust database.", msg); - } else if (strcmp(action, "remove") == 0) { - smsg("Removed \"%s\" from trust database.", msg); - } - } else { - semsg(e_trustfile, msg); - } - } - - lua_settop(lstate, top); - return success; -} diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index c6747833e5..f340d9d0d8 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -8,7 +8,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/assert.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/func_attr.h" #include "nvim/lua/converter.h" @@ -24,7 +24,7 @@ typedef struct { LuaRef empty_dict_ref; int ref_count; #if __has_feature(address_sanitizer) - PMap(handle_T) ref_markers; + PMap(int) ref_markers; #endif } nlua_ref_state_t; diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c new file mode 100644 index 0000000000..30d5a95fc0 --- /dev/null +++ b/src/nvim/lua/secure.c @@ -0,0 +1,118 @@ +// 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 <lauxlib.h> +#include <lua.h> +#include <lualib.h> + +#include "nvim/charset.h" +#include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" +#include "nvim/message.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.c.generated.h" +#endif + +char *nlua_read_secure(const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "read"); + lua_pushstring(lstate, path); + if (nlua_pcall(lstate, 1, 1)) { + nlua_error(lstate, _("Error executing vim.secure.read: %.*s")); + lua_settop(lstate, top); + return NULL; + } + + size_t len = 0; + const char *contents = lua_tolstring(lstate, -1, &len); + char *buf = NULL; + if (contents != NULL) { + // Add one to include trailing null byte + buf = xcalloc(len + 1, sizeof(char)); + memcpy(buf, contents, len + 1); + } + + lua_settop(lstate, top); + return buf; +} + +static bool nlua_trust(const char *action, const char *path) +{ + lua_State *const lstate = get_global_lstate(); + const int top = lua_gettop(lstate); + + lua_getglobal(lstate, "vim"); + lua_getfield(lstate, -1, "secure"); + lua_getfield(lstate, -1, "trust"); + + lua_newtable(lstate); + lua_pushstring(lstate, "action"); + lua_pushstring(lstate, action); + lua_settable(lstate, -3); + if (path == NULL) { + lua_pushstring(lstate, "bufnr"); + lua_pushnumber(lstate, 0); + lua_settable(lstate, -3); + } else { + lua_pushstring(lstate, "path"); + lua_pushstring(lstate, path); + lua_settable(lstate, -3); + } + + if (nlua_pcall(lstate, 1, 2)) { + nlua_error(lstate, _("Error executing vim.secure.trust: %.*s")); + lua_settop(lstate, top); + return false; + } + + bool success = lua_toboolean(lstate, -2); + const char *msg = lua_tostring(lstate, -1); + if (msg != NULL) { + if (success) { + if (strcmp(action, "allow") == 0) { + smsg("Allowed \"%s\" in trust database.", msg); + } else if (strcmp(action, "deny") == 0) { + smsg("Denied \"%s\" in trust database.", msg); + } else if (strcmp(action, "remove") == 0) { + smsg("Removed \"%s\" from trust database.", msg); + } + } else { + semsg(e_trustfile, msg); + } + } + + lua_settop(lstate, top); + return success; +} + +void ex_trust(exarg_T *eap) +{ + const char *const p = skiptowhite(eap->arg); + char *arg1 = xmemdupz(eap->arg, (size_t)(p - eap->arg)); + const char *action = "allow"; + const char *path = skipwhite(p); + + if (strcmp(arg1, "++deny") == 0) { + action = "deny"; + } else if (strcmp(arg1, "++remove") == 0) { + action = "remove"; + } else if (*arg1 != '\0') { + semsg(e_invarg2, arg1); + goto theend; + } + + if (path[0] == '\0') { + path = NULL; + } + + nlua_trust(action, path); + +theend: + xfree(arg1); +} diff --git a/src/nvim/lua/secure.h b/src/nvim/lua/secure.h new file mode 100644 index 0000000000..87c468538e --- /dev/null +++ b/src/nvim/lua/secure.h @@ -0,0 +1,12 @@ +#ifndef NVIM_LUA_SECURE_H +#define NVIM_LUA_SECURE_H + +#include <lua.h> + +#include "nvim/ex_cmds_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/secure.h.generated.h" +#endif + +#endif // NVIM_LUA_SECURE_H diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index da64685a40..a9e7838980 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -20,6 +20,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/buffer_defs.h" #include "nvim/globals.h" +#include "nvim/lua/executor.h" #include "nvim/lua/treesitter.h" #include "nvim/macros.h" #include "nvim/map.h" @@ -43,6 +44,13 @@ typedef struct { int max_match_id; } TSLua_cursor; +typedef struct { + LuaRef cb; + lua_State *lstate; + bool lex; + bool parse; +} TSLuaLoggerOpts; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/treesitter.c.generated.h" #endif @@ -56,6 +64,8 @@ static struct luaL_Reg parser_meta[] = { { "included_ranges", parser_get_ranges }, { "set_timeout", parser_set_timeout }, { "timeout", parser_get_timeout }, + { "_set_logger", parser_set_logger }, + { "_logger", parser_get_logger }, { NULL, NULL } }; @@ -231,9 +241,9 @@ int tslua_remove_lang(lua_State *L) const char *lang_name = luaL_checkstring(L, 1); bool present = pmap_has(cstr_t)(&langs, lang_name); if (present) { - char *key = (char *)pmap_key(cstr_t)(&langs, lang_name); - pmap_del(cstr_t)(&langs, lang_name); - xfree(key); + cstr_t key; + pmap_del(cstr_t)(&langs, lang_name, &key); + xfree((void *)key); } lua_pushboolean(L, present); return 1; @@ -322,6 +332,12 @@ static int parser_gc(lua_State *L) return 0; } + TSLogger logger = ts_parser_logger(*p); + if (logger.log) { + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; + xfree(opts); + } + ts_parser_delete(*p); return 0; } @@ -669,9 +685,82 @@ static int parser_get_timeout(lua_State *L) } lua_pushinteger(L, (long)ts_parser_timeout_micros(*p)); + return 1; +} + +static void logger_cb(void *payload, TSLogType logtype, const char *s) +{ + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)payload; + if ((!opts->lex && logtype == TSLogTypeLex) + || (!opts->parse && logtype == TSLogTypeParse)) { + return; + } + + lua_State *lstate = opts->lstate; + + nlua_pushref(lstate, opts->cb); + lua_pushstring(lstate, logtype == TSLogTypeParse ? "parse" : "lex"); + lua_pushstring(lstate, s); + if (lua_pcall(lstate, 2, 0, 0)) { + luaL_error(lstate, "Error executing treesitter logger callback"); + } +} + +static int parser_set_logger(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + if (!lua_isboolean(L, 2)) { + return luaL_argerror(L, 2, "boolean expected"); + } + + if (!lua_isboolean(L, 3)) { + return luaL_argerror(L, 3, "boolean expected"); + } + + if (!lua_isfunction(L, 4)) { + return luaL_argerror(L, 4, "function expected"); + } + + TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts)); + + *opts = (TSLuaLoggerOpts){ + .lex = lua_toboolean(L, 2), + .parse = lua_toboolean(L, 3), + .cb = nlua_ref_global(L, 4), + .lstate = L + }; + + TSLogger logger = { + .payload = (void *)opts, + .log = logger_cb + }; + + ts_parser_set_logger(*p, logger); return 0; } +static int parser_get_logger(lua_State *L) +{ + TSParser **p = parser_check(L, 1); + if (!p) { + return 0; + } + + TSLogger logger = ts_parser_logger(*p); + if (logger.log) { + TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; + nlua_pushref(L, opts->cb); + } else { + lua_pushnil(L); + } + + return 1; +} + // Tree methods /// push tree interface on lua stack. @@ -1349,6 +1438,11 @@ static int node_rawquery(lua_State *L) } else { cursor = ts_query_cursor_new(); } + +#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH + // reset the start depth + ts_query_cursor_set_max_start_depth(cursor, 0); +#endif ts_query_cursor_set_match_limit(cursor, 256); ts_query_cursor_exec(cursor, query, node); @@ -1360,6 +1454,29 @@ static int node_rawquery(lua_State *L) ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); } + if (lua_gettop(L) >= 6 && !lua_isnil(L, 6)) { + if (!lua_istable(L, 6)) { + return luaL_error(L, "table expected"); + } + lua_pushnil(L); + // stack: [dict, ..., nil] + while (lua_next(L, 6)) { + // stack: [dict, ..., key, value] + if (lua_type(L, -2) == LUA_TSTRING) { + char *k = (char *)lua_tostring(L, -2); + if (strequal("max_start_depth", k)) { + // TODO(lewis6991): remove ifdef when min TS version is 0.20.9 +#ifdef NVIM_TS_HAS_SET_MAX_START_DEPTH + uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1); + ts_query_cursor_set_max_start_depth(cursor, max_start_depth); +#endif + } + } + lua_pop(L, 1); // pop the value; lua_next will pop the key. + // stack: [dict, ..., key] + } + } + TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata] ud->cursor = cursor; ud->predicated_match = -1; diff --git a/src/nvim/main.c b/src/nvim/main.c index ee643adab5..d4fbf8ce93 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -49,6 +49,7 @@ #include "nvim/keycodes.h" #include "nvim/log.h" #include "nvim/lua/executor.h" +#include "nvim/lua/secure.h" #include "nvim/macros.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -443,8 +444,7 @@ int main(int argc, char **argv) // If using the runtime (-u is not NONE), enable syntax & filetype plugins. if (!vimrc_none || params.clean) { - // Sources filetype.lua and filetype.vim unless the user explicitly disabled it with :filetype - // off. + // Sources filetype.lua unless the user explicitly disabled it with :filetype off. filetype_maybe_enable(); // Sources syntax/syntax.vim. We do this *after* the user startup scripts so that users can // disable syntax highlighting with `:syntax off` if they wish. @@ -2227,3 +2227,17 @@ static void check_swap_exists_action(void) } handle_swap_exists(NULL); } + +#ifdef ENABLE_ASAN_UBSAN +const char *__ubsan_default_options(void); +const char *__ubsan_default_options(void) +{ + return "print_stacktrace=1"; +} + +const char *__asan_default_options(void); +const char *__asan_default_options(void) +{ + return "handle_abort=1,handle_sigill=1"; +} +#endif diff --git a/src/nvim/map.c b/src/nvim/map.c index 191a459863..4c8506f468 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -16,7 +16,6 @@ #include "klib/khash.h" #include "nvim/gettext.h" #include "nvim/map.h" -#include "nvim/map_defs.h" #include "nvim/memory.h" #define cstr_t_hash kh_str_hash_func @@ -29,8 +28,6 @@ #define int_eq kh_int_hash_equal #define handle_T_hash kh_int_hash_func #define handle_T_eq kh_int_hash_equal -#define KittyKey_hash kh_int_hash_func -#define KittyKey_eq kh_int_hash_equal #if defined(ARCH_64) # define ptr_t_hash(key) uint64_t_hash((uint64_t)(key)) @@ -45,74 +42,69 @@ #define DEFAULT_INITIALIZER { 0 } #define SSIZE_INITIALIZER { -1 } +#define KEY_IMPL(T) \ + __KHASH_IMPL(T, , T, T##_hash, T##_eq) \ + #define MAP_IMPL(T, U, ...) \ INITIALIZER_DECLARE(T, U, __VA_ARGS__); \ - __KHASH_IMPL(T##_##U##_map, , T, U, 1, T##_hash, T##_eq) \ - void map_##T##_##U##_destroy(Map(T, U) *map) \ - { \ - kh_dealloc(T##_##U##_map, &map->table); \ - } \ U map_##T##_##U##_get(Map(T, U) *map, T key) \ { \ khiter_t k; \ - if ((k = kh_get(T##_##U##_map, &map->table, key)) == kh_end(&map->table)) { \ + if ((k = kh_get(T, &map->table, key)) == kh_end(&map->table)) { \ return INITIALIZER(T, U); \ } \ - return kh_val(&map->table, k); \ - } \ - bool map_##T##_##U##_has(Map(T, U) *map, T key) \ - { \ - return kh_get(T##_##U##_map, &map->table, key) != kh_end(&map->table); \ - } \ - T map_##T##_##U##_key(Map(T, U) *map, T key) \ - { \ - khiter_t k; \ - if ((k = kh_get(T##_##U##_map, &map->table, key)) == kh_end(&map->table)) { \ - abort(); /* Caller must check map_has(). */ \ - } \ - return kh_key(&map->table, k); \ + return kh_val(U, &map->table, k); \ } \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value) \ { \ + STATIC_ASSERT(sizeof(U) <= KHASH_MAX_VAL_SIZE, "increase KHASH_MAX_VAL_SIZE"); \ int ret; \ U rv = INITIALIZER(T, U); \ - khiter_t k = kh_put(T##_##U##_map, &map->table, key, &ret); \ + khiter_t k = kh_put(T, &map->table, key, &ret, sizeof(U)); \ if (!ret) { \ - rv = kh_val(&map->table, k); \ + rv = kh_val(U, &map->table, k); \ } \ - kh_val(&map->table, k) = value; \ + kh_val(U, &map->table, k) = value; \ return rv; \ } \ - U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \ + U *map_##T##_##U##_ref(Map(T, U) *map, T key, T **key_alloc) \ + { \ + khiter_t k = kh_get(T, &map->table, key); \ + if (k == kh_end(&map->table)) { \ + return NULL; \ + } \ + if (key_alloc) { \ + *key_alloc = &kh_key(&map->table, k); \ + } \ + return &kh_val(U, &map->table, k); \ + } \ + U *map_##T##_##U##_put_ref(Map(T, U) *map, T key, T **key_alloc, bool *new_item) \ { \ int ret; \ - khiter_t k; \ - if (put) { \ - k = kh_put(T##_##U##_map, &map->table, key, &ret); \ - if (ret) { \ - kh_val(&map->table, k) = INITIALIZER(T, U); \ - } \ - } else { \ - k = kh_get(T##_##U##_map, &map->table, key); \ - if (k == kh_end(&map->table)) { \ - return NULL; \ - } \ + khiter_t k = kh_put(T, &map->table, key, &ret, sizeof(U)); \ + if (ret) { \ + kh_val(U, &map->table, k) = INITIALIZER(T, U); \ } \ - return &kh_val(&map->table, k); \ + if (new_item) { \ + *new_item = (bool)ret; \ + } \ + if (key_alloc) { \ + *key_alloc = &kh_key(&map->table, k); \ + } \ + return &kh_val(U, &map->table, k); \ } \ - U map_##T##_##U##_del(Map(T, U) *map, T key) \ + U map_##T##_##U##_del(Map(T, U) *map, T key, T *key_alloc) \ { \ U rv = INITIALIZER(T, U); \ khiter_t k; \ - if ((k = kh_get(T##_##U##_map, &map->table, key)) != kh_end(&map->table)) { \ - rv = kh_val(&map->table, k); \ - kh_del(T##_##U##_map, &map->table, k); \ + if ((k = kh_get(T, &map->table, key)) != kh_end(&map->table)) { \ + rv = kh_val(U, &map->table, k); \ + if (key_alloc) { \ + *key_alloc = kh_key(&map->table, k); \ + } \ + kh_del(T, &map->table, k); \ } \ return rv; \ - } \ - void map_##T##_##U##_clear(Map(T, U) *map) \ - { \ - kh_clear(T##_##U##_map, &map->table); \ } static inline khint_t String_hash(String s) @@ -162,7 +154,17 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2) return memcmp(&ae1, &ae2, sizeof(ae1)) == 0; } +KEY_IMPL(int) +KEY_IMPL(cstr_t) +KEY_IMPL(ptr_t) +KEY_IMPL(uint64_t) +KEY_IMPL(uint32_t) +KEY_IMPL(String) +KEY_IMPL(HlEntry) +KEY_IMPL(ColorKey) + MAP_IMPL(int, int, DEFAULT_INITIALIZER) +MAP_IMPL(int, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(int, cstr_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, int, DEFAULT_INITIALIZER) @@ -172,26 +174,19 @@ MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER) MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER) -MAP_IMPL(handle_T, ptr_t, DEFAULT_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) -MAP_IMPL(KittyKey, cstr_t, DEFAULT_INITIALIZER) - /// Deletes a key:value pair from a string:pointer map, and frees the /// storage of both key and value. /// void pmap_del2(PMap(cstr_t) *map, const char *key) { - if (pmap_has(cstr_t)(map, key)) { - void *k = (void *)pmap_key(cstr_t)(map, key); - void *v = pmap_get(cstr_t)(map, key); - pmap_del(cstr_t)(map, key); - xfree(k); - xfree(v); - } + cstr_t key_alloc = NULL; + ptr_t val = pmap_del(cstr_t)(map, key, &key_alloc); + xfree((void *)key_alloc); + xfree(val); } diff --git a/src/nvim/map.h b/src/nvim/map.h index 92f0b32255..cc32a20740 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -7,39 +7,71 @@ #include "klib/khash.h" #include "nvim/api/private/defs.h" -#include "nvim/extmark_defs.h" -#include "nvim/gettext.h" +#include "nvim/assert.h" #include "nvim/highlight_defs.h" -#include "nvim/map_defs.h" -#include "nvim/tui/input_defs.h" #include "nvim/types.h" -#include "nvim/ui_client.h" #if defined(__NetBSD__) # undef uint64_t # define uint64_t uint64_t #endif +typedef const char *cstr_t; +typedef void *ptr_t; + +#define Map(T, U) Map_##T##_##U +#define PMap(T) Map(T, ptr_t) + +#define KEY_DECLS(T) \ + KHASH_DECLARE(T) \ + static inline bool set_put_##T(Set(T) *set, T key, T **key_alloc) { \ + int kh_ret; \ + khiter_t k = kh_put(T, set, key, &kh_ret, 0); \ + if (key_alloc) { \ + *key_alloc = &kh_key(set, k); \ + } \ + return kh_ret; \ + } \ + static inline void set_del_##T(Set(T) *set, T key) \ + { \ + khiter_t k; \ + if ((k = kh_get(T, set, key)) != kh_end(set)) { \ + kh_del(T, set, k); \ + } \ + } \ + static inline bool set_has_##T(Set(T) *set, T key) { \ + return (kh_get(T, set, key) != kh_end(set)); \ + } \ + #define MAP_DECLS(T, U) \ - KHASH_DECLARE(T##_##U##_map, T, U) \ typedef struct { \ - khash_t(T##_##U##_map) table; \ + khash_t(T) table; \ } Map(T, U); \ - Map(T, U) *map_##T##_##U##_new(void); \ - void map_##T##_##U##_free(Map(T, U) *map); \ - void map_##T##_##U##_destroy(Map(T, U) *map); \ U map_##T##_##U##_get(Map(T, U) *map, T key); \ - bool map_##T##_##U##_has(Map(T, U) *map, T key); \ - T map_##T##_##U##_key(Map(T, U) *map, T key); \ + static inline bool map_##T##_##U##_has(Map(T, U) *map, T key) \ + { \ + return kh_get(T, &map->table, key) != kh_end(&map->table); \ + } \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \ - U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \ - U map_##T##_##U##_del(Map(T, U) *map, T key); \ - void map_##T##_##U##_clear(Map(T, U) *map); + U *map_##T##_##U##_ref(Map(T, U) *map, T key, T **key_alloc); \ + U *map_##T##_##U##_put_ref(Map(T, U) *map, T key, T **key_alloc, bool *new_item); \ + U map_##T##_##U##_del(Map(T, U) *map, T key, T *key_alloc); \ -// // NOTE: Keys AND values must be allocated! khash.h does not make a copy. -// + +#define Set(type) khash_t(type) + +KEY_DECLS(int) +KEY_DECLS(cstr_t) +KEY_DECLS(ptr_t) +KEY_DECLS(uint64_t) +KEY_DECLS(uint32_t) +KEY_DECLS(String) +KEY_DECLS(HlEntry) +KEY_DECLS(ColorKey) + MAP_DECLS(int, int) +MAP_DECLS(int, ptr_t) MAP_DECLS(int, cstr_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(cstr_t, int) @@ -49,48 +81,50 @@ MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(uint64_t, ssize_t) MAP_DECLS(uint64_t, uint64_t) MAP_DECLS(uint32_t, uint32_t) - -MAP_DECLS(handle_T, ptr_t) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) - MAP_DECLS(ColorKey, ColorItem) -MAP_DECLS(KittyKey, cstr_t) - -#define MAP_INIT { { 0, 0, 0, 0, NULL, NULL, NULL } } -#define map_init(k, v, map) do { *(map) = (Map(k, v)) MAP_INIT; } while (false) +#define SET_INIT { 0, 0, 0, 0, NULL, NULL, NULL } +#define MAP_INIT { SET_INIT } -#define map_destroy(T, U) map_##T##_##U##_destroy #define map_get(T, U) map_##T##_##U##_get #define map_has(T, U) map_##T##_##U##_has -#define map_key(T, U) map_##T##_##U##_key #define map_put(T, U) map_##T##_##U##_put #define map_ref(T, U) map_##T##_##U##_ref +#define map_put_ref(T, U) map_##T##_##U##_put_ref #define map_del(T, U) map_##T##_##U##_del -#define map_clear(T, U) map_##T##_##U##_clear +#define map_destroy(T, map) kh_dealloc(T, &(map)->table) +#define map_clear(T, map) kh_clear(T, &(map)->table) #define map_size(map) ((map)->table.size) -#define pmap_destroy(T) map_destroy(T, ptr_t) #define pmap_get(T) map_get(T, ptr_t) #define pmap_has(T) map_has(T, ptr_t) -#define pmap_key(T) map_key(T, ptr_t) #define pmap_put(T) map_put(T, ptr_t) #define pmap_ref(T) map_ref(T, ptr_t) +#define pmap_put_ref(T) map_put_ref(T, ptr_t) /// @see pmap_del2 #define pmap_del(T) map_del(T, ptr_t) -#define pmap_clear(T) map_clear(T, ptr_t) -#define pmap_init(k, map) map_init(k, ptr_t, map) -#define map_foreach(map, key, value, block) \ - kh_foreach(&(map)->table, key, value, block) +#define map_foreach(U, map, key, value, block) kh_foreach(U, &(map)->table, key, value, block) -#define map_foreach_value(map, value, block) \ - kh_foreach_value(&(map)->table, value, block) +#define map_foreach_value(U, map, value, block) kh_foreach_value(U, &(map)->table, value, block) +#define map_foreach_key(map, key, block) kh_foreach_key(&(map)->table, key, block) +#define set_foreach(set, key, block) kh_foreach_key(set, key, block) + +#define pmap_foreach_value(map, value, block) map_foreach_value(ptr_t, map, value, block) +#define pmap_foreach(map, key, value, block) map_foreach(ptr_t, map, key, value, block) void pmap_del2(PMap(cstr_t) *map, const char *key); +#define set_has(T, set, key) set_has_##T(set, key) +#define set_put(T, set, key) set_put_##T(set, key, NULL) +#define set_put_ref(T, set, key, key_alloc) set_put_##T(set, key, key_alloc) +#define set_del(T, set, key) set_del_##T(set, key) +#define set_destroy(T, set) kh_dealloc(T, set) +#define set_clear(T, set) kh_clear(T, set) + #endif // NVIM_MAP_H diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h deleted file mode 100644 index 61afedbe50..0000000000 --- a/src/nvim/map_defs.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef NVIM_MAP_DEFS_H -#define NVIM_MAP_DEFS_H - -#include "klib/khash.h" - -typedef const char *cstr_t; -typedef void *ptr_t; - -#define Map(T, U) Map_##T##_##U -#define PMap(T) Map(T, ptr_t) - -#endif // NVIM_MAP_DEFS_H diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index c5449bd66e..e40e67cead 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2096,13 +2096,13 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs : cstr_as_string(str2special_save(mp->m_str, false, true)))); } if (mp->m_desc != NULL) { - PUT(dict, "desc", STRING_OBJ(cstr_to_string(mp->m_desc))); + PUT(dict, "desc", CSTR_TO_OBJ(mp->m_desc)); } - PUT(dict, "lhs", STRING_OBJ(cstr_as_string(lhs))); - PUT(dict, "lhsraw", STRING_OBJ(cstr_to_string(mp->m_keys))); + PUT(dict, "lhs", CSTR_AS_OBJ(lhs)); + PUT(dict, "lhsraw", CSTR_TO_OBJ(mp->m_keys)); if (lhsrawalt != NULL) { // Also add the value for the simplified entry. - PUT(dict, "lhsrawalt", STRING_OBJ(cstr_to_string(lhsrawalt))); + PUT(dict, "lhsrawalt", CSTR_TO_OBJ(lhsrawalt)); } PUT(dict, "noremap", INTEGER_OBJ(noremap_value)); PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); @@ -2115,7 +2115,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs if (mp->m_replace_keycodes) { PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); } - PUT(dict, "mode", STRING_OBJ(cstr_as_string(mapmode))); + PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); return dict; } diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index f9df0028db..cfe19eac6f 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -1,7 +1,7 @@ #ifndef NVIM_MARK_DEFS_H #define NVIM_MARK_DEFS_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/os/time.h" #include "nvim/pos.h" diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 757906d42f..840b6b646e 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -359,7 +359,7 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) } b->n_keys--; - pmap_del(uint64_t)(b->id2node, id); + pmap_del(uint64_t)(b->id2node, id, NULL); // 5. bool itr_dirty = false; @@ -549,8 +549,8 @@ void marktree_clear(MarkTree *b) b->root = NULL; } if (b->id2node->table.keys) { - pmap_destroy(uint64_t)(b->id2node); - pmap_init(uint64_t, b->id2node); + map_destroy(uint64_t, b->id2node); + *b->id2node = (PMap(uint64_t)) MAP_INIT; } b->n_keys = 0; b->n_nodes = 0; diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 29d2abcd46..cd56115b58 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -9,7 +9,6 @@ #include "nvim/assert.h" #include "nvim/garray.h" #include "nvim/map.h" -#include "nvim/map_defs.h" #include "nvim/pos.h" #include "nvim/types.h" diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7d61b918d2..66c26275f1 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2024,6 +2024,24 @@ int mb_charlen(const char *str) return count; } +int mb_charlen2bytelen(const char *str, int charlen) +{ + const char *p = str; + int count = 0; + + if (p == NULL) { + return 0; + } + + for (int i = 0; *p != NUL && i < charlen; i++) { + int b = utfc_ptr2len(p); + p += b; + count += b; + } + + return count; +} + /// Like mb_charlen() but for a string with specified length. int mb_charlen_len(const char *str, int len) { diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 780f33e05b..724a16014d 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -5,7 +5,7 @@ #include <stdint.h> #include <string.h> -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" #include "nvim/mbyte_defs.h" #include "nvim/os/os_defs.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index b9a26e1ac6..1f550ffb01 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -664,6 +664,7 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size) # include "nvim/getchar.h" # include "nvim/grid.h" # include "nvim/mark.h" +# include "nvim/msgpack_rpc/channel.h" # include "nvim/ops.h" # include "nvim/option.h" # include "nvim/os/os.h" @@ -823,6 +824,7 @@ void free_all_mem(void) ui_free_all_mem(); nlua_free_all_mem(); + rpc_free_all_mem(); // should be last, in case earlier free functions deallocates arenas arena_free_reuse_blks(); diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 16fea2ccf1..898e3ddd27 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -559,7 +559,7 @@ static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent) } } else if (*name != NUL) { if (!silent) { - emsg(_(e_menuothermode)); + emsg(_(e_menu_only_exists_in_another_mode)); } return FAIL; } @@ -760,7 +760,7 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes) emsg(_(e_notsubmenu)); return NULL; } else if ((menu->modes & modes) == 0x0) { - emsg(_(e_menuothermode)); + emsg(_(e_menu_only_exists_in_another_mode)); return NULL; } else if (*p == NUL) { // found a full match return menu; diff --git a/src/nvim/message.c b/src/nvim/message.c index 63bcf3e069..1cb4a3cbf4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -181,7 +181,7 @@ void msg_grid_validate(void) msg_grid.dirty_col = xcalloc((size_t)Rows, sizeof(*msg_grid.dirty_col)); // Tricky: allow resize while pager or ex mode is active - int pos = MAX(max_rows - msg_scrolled, 0); + int pos = (State & MODE_ASKMORE) ? 0 : MAX(max_rows - msg_scrolled, 0); msg_grid.throttled = false; // don't throttle in 'cmdheight' area msg_grid_set_pos(pos, msg_scrolled); ui_comp_put_grid(&msg_grid, pos, 0, msg_grid.rows, msg_grid.cols, @@ -1069,7 +1069,7 @@ void ex_messages(void *const eap_p) for (; p != NULL; p = p->next) { if (kv_size(p->multiattr) || (p->msg && p->msg[0])) { Array entry = ARRAY_DICT_INIT; - ADD(entry, STRING_OBJ(cstr_to_string(p->kind))); + ADD(entry, CSTR_TO_OBJ(p->kind)); Array content = ARRAY_DICT_INIT; if (kv_size(p->multiattr)) { for (uint32_t i = 0; i < kv_size(p->multiattr); i++) { @@ -1082,7 +1082,7 @@ void ex_messages(void *const eap_p) } else if (p->msg && p->msg[0]) { Array content_entry = ARRAY_DICT_INIT; ADD(content_entry, INTEGER_OBJ(p->attr)); - ADD(content_entry, STRING_OBJ(cstr_to_string(p->msg))); + ADD(content_entry, CSTR_TO_OBJ(p->msg)); ADD(content, ARRAY_OBJ(content_entry)); } ADD(entry, ARRAY_OBJ(content)); diff --git a/src/nvim/move.c b/src/nvim/move.c index e09a95af70..9e8abbcd96 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -164,19 +164,25 @@ static void redraw_for_cursorcolumn(win_T *wp) } } -/// Calculates how much overlap the smoothscroll marker "<<<" overlaps with -/// buffer text for curwin. +/// Calculates how much the 'listchars' "precedes" or 'smoothscroll' "<<<" +/// marker overlaps with buffer text for window "wp". /// Parameter "extra2" should be the padding on the 2nd line, not the first /// line. /// Returns the number of columns of overlap with buffer text, excluding the /// extra padding on the ledge. -static int smoothscroll_marker_overlap(win_T *wp, int extra2) +int sms_marker_overlap(win_T *wp, int extra2) { - // We don't draw the <<< marker when in showbreak mode, thus no need to + // There is no marker overlap when in showbreak mode, thus no need to // account for it. See grid_put_linebuf(). if (*get_showbreak_value(wp) != NUL) { return 0; } + + // Overlap when 'list' and 'listchars' "precedes" are set is 1. + if (wp->w_p_list && wp->w_p_lcs_chars.prec) { + return 1; + } + return extra2 > 3 ? 0 : 3 - extra2; } @@ -212,8 +218,6 @@ static void reset_skipcol(win_T *wp) // Update curwin->w_topline to move the cursor onto the screen. void update_topline(win_T *wp) { - linenr_T old_topline; - int old_topfill; bool check_botline = false; long *so_ptr = wp->w_p_so >= 0 ? &wp->w_p_so : &p_so; long save_so = *so_ptr; @@ -243,8 +247,8 @@ void update_topline(win_T *wp) *so_ptr = mouse_dragging - 1; } - old_topline = wp->w_topline; - old_topfill = wp->w_topfill; + linenr_T old_topline = wp->w_topline; + int old_topfill = wp->w_topfill; // If the buffer is empty, always set topline to 1. if (buf_is_empty(curbuf)) { // special case - file is empty @@ -271,12 +275,11 @@ void update_topline(win_T *wp) } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) { colnr_T vcol; - // Check that the cursor position is visible. Add columns for the - // smoothscroll marker "<<<" displayed in the top-left if needed. + // Check that the cursor position is visible. Add columns for + // the marker displayed in the top-left if needed. getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL); - int smoothscroll_overlap = smoothscroll_marker_overlap(wp, - win_col_off(wp) - win_col_off2(wp)); - if (wp->w_skipcol + smoothscroll_overlap > vcol) { + int overlap = sms_marker_overlap(wp, win_col_off(wp) - win_col_off2(wp)); + if (wp->w_skipcol + overlap > vcol) { check_topline = true; } } @@ -408,7 +411,13 @@ void update_topline(win_T *wp) || wp->w_topfill != old_topfill) { dollar_vcol = -1; redraw_later(wp, UPD_VALID); - reset_skipcol(wp); + + // When 'smoothscroll' is not set, should reset w_skipcol. + if (!wp->w_p_sms) { + reset_skipcol(wp); + } else if (wp->w_skipcol != 0) { + redraw_later(wp, UPD_SOME_VALID); + } // May need to set w_skipcol when cursor in w_topline. if (wp->w_cursor.lnum == wp->w_topline) { @@ -652,7 +661,7 @@ static void curs_rows(win_T *wp) i--; // hold at inserted lines } } - if (valid && (lnum != wp->w_topline || !win_may_fill(wp))) { + if (valid && (lnum != wp->w_topline || (wp->w_skipcol == 0 && !win_may_fill(wp)))) { lnum = wp->w_lines[i].wl_lastlnum + 1; // Cursor inside folded lines, don't count this row if (lnum > wp->w_cursor.lnum) { @@ -1315,14 +1324,6 @@ bool scrolldown(long line_count, int byfold) return moved; } -/// Return TRUE if scrollup() will scroll by screen line rather than text line. -static int scrolling_screenlines(bool byfold) -{ - return (curwin->w_p_wrap && curwin->w_p_sms) - || (byfold && hasAnyFolding(curwin)) - || (curwin->w_p_diff && !curwin->w_p_wrap); -} - /// Scroll the current window up by "line_count" logical lines. "CTRL-E" /// /// @param line_count number of lines to scroll @@ -1333,7 +1334,7 @@ bool scrollup(long line_count, int byfold) linenr_T botline = curwin->w_botline; int do_sms = curwin->w_p_wrap && curwin->w_p_sms; - if (scrolling_screenlines(byfold) || win_may_fill(curwin)) { + if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { int width1 = curwin->w_width_inner - curwin_col_off(); int width2 = width1 + curwin_col_off2(); unsigned size = 0; @@ -1431,10 +1432,9 @@ bool scrollup(long line_count, int byfold) long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; int space_cols = (curwin->w_height_inner - 1) * width2; - // If we have non-zero scrolloff, just ignore the <<< marker as we are + // If we have non-zero scrolloff, just ignore the marker as we are // going past it anyway. - int smoothscroll_overlap = scrolloff_cols != 0 ? 0 : - smoothscroll_marker_overlap(curwin, extra2); + int overlap = scrolloff_cols != 0 ? 0 : sms_marker_overlap(curwin, extra2); // Make sure the cursor is in a visible part of the line, taking // 'scrolloff' into account, but using screen lines. @@ -1443,13 +1443,13 @@ bool scrollup(long line_count, int byfold) scrolloff_cols = space_cols / 2; } validate_virtcol(); - if (curwin->w_virtcol < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { + if (curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { colnr_T col = curwin->w_virtcol; if (col < width1) { col += width1; } - while (col < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { + while (col < curwin->w_skipcol + overlap + scrolloff_cols) { col += width2; } curwin->w_curswant = col; @@ -1494,19 +1494,20 @@ void adjust_skipcol(void) } validate_virtcol(); + int overlap = sms_marker_overlap(curwin, curwin_col_off() - curwin_col_off2()); while (curwin->w_skipcol > 0 - && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { + && curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { // scroll a screen line down if (curwin->w_skipcol >= width1 + width2) { curwin->w_skipcol -= width2; } else { curwin->w_skipcol -= width1; } - redraw_later(curwin, UPD_NOT_VALID); scrolled = true; - validate_virtcol(); } if (scrolled) { + validate_virtcol(); + redraw_later(curwin, UPD_NOT_VALID); return; // don't scroll in the other direction now } long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; @@ -1870,6 +1871,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) int old_valid = curwin->w_valid; int old_empty_rows = curwin->w_empty_rows; linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number + int do_sms = curwin->w_p_wrap && curwin->w_p_sms; if (set_topbot) { bool set_skipcol = false; @@ -1886,7 +1888,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) break; } if (used + loff.height > curwin->w_height_inner) { - if (curwin->w_p_sms && curwin->w_p_wrap) { + if (do_sms) { // 'smoothscroll' and 'wrap' are set. The above line is // too long to show in its entirety, so we show just a part // of it. @@ -1925,7 +1927,6 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) used = plines_win_nofill(curwin, cln, true); int scrolled = 0; - int min_scrolled = 1; // If the cursor is on or below botline, we will at least scroll by the // height of the cursor line, which is "used". Correct for empty lines, // which are really part of botline. @@ -1934,16 +1935,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (cln == curwin->w_botline) { scrolled -= curwin->w_empty_rows; } - min_scrolled = scrolled; - if (curwin->w_p_sms && curwin->w_p_wrap) { - // 'smoothscroll' and 'wrap' are set - if (cln > curwin->w_botline) { - // add screen lines below w_botline - for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { - min_scrolled += plines_win_nofill(curwin, lnum, true); - } - } - + if (do_sms) { + // 'smoothscroll' and 'wrap' are set. // Calculate how many screen lines the current top line of window // occupies. If it is occupying more than the entire window, we // need to scroll the additional clipped lines to scroll past the @@ -1962,7 +1955,6 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) top_plines -= skip_lines; if (top_plines > curwin->w_height_inner) { scrolled += (top_plines - curwin->w_height_inner); - min_scrolled += (top_plines - curwin->w_height_inner); } } } @@ -2067,21 +2059,11 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // Otherwise put it at 1/2 of the screen. if (line_count >= curwin->w_height_inner && line_count > min_scroll) { scroll_cursor_halfway(false, true); - } else { - // With 'smoothscroll' scroll at least the height of the cursor line, - // unless it would move the cursor. - if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled - && (curwin->w_cursor.lnum < curwin->w_topline - || (curwin->w_virtcol - curwin->w_skipcol >= - curwin->w_width_inner - curwin_col_off()))) { - line_count = min_scrolled; - } - if (line_count > 0) { - if (scrolling_screenlines(true)) { - scrollup(scrolled, true); // TODO(vim): - } else { - scrollup(line_count, true); - } + } else if (line_count > 0) { + if (do_sms) { + scrollup(scrolled, true); // TODO(vim): + } else { + scrollup(line_count, true); } } @@ -2112,38 +2094,38 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) boff.fill = 0; linenr_T topline = loff.lnum; colnr_T skipcol = 0; - bool set_skipcol = false; - int half_height = 0; - bool smooth_scroll = false; - if (curwin->w_p_sms && curwin->w_p_wrap) { + int want_height; + bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + if (do_sms) { // 'smoothscroll' and 'wrap' are set - smooth_scroll = true; - half_height = (curwin->w_height_inner - used) / 2; - used = 0; + if (atend) { + want_height = (curwin->w_height_inner - used) / 2; + used = 0; + } else { + want_height = curwin->w_height_inner; + } } int topfill = 0; while (topline > 1) { // If using smoothscroll, we can precisely scroll to the // exact point where the cursor is halfway down the screen. - if (smooth_scroll) { + if (do_sms) { topline_back_winheight(curwin, &loff, false); if (loff.height == MAXCOL) { break; - } else { - used += loff.height; } - if (used > half_height) { - if (used - loff.height < half_height) { - int plines_offset = used - half_height; - loff.height -= plines_offset; - used = half_height; - + used += loff.height; + if (!atend && boff.lnum < curbuf->b_ml.ml_line_count) { + botline_forw(curwin, &boff); + used += boff.height; + } + if (used > want_height) { + if (used - loff.height < want_height) { topline = loff.lnum; topfill = loff.fill; - skipcol = skipcol_from_plines(curwin, plines_offset); - set_skipcol = true; + skipcol = skipcol_from_plines(curwin, used - want_height); } break; } @@ -2208,12 +2190,12 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) } if (!hasFolding(topline, &curwin->w_topline, NULL) - && (curwin->w_topline != topline || set_skipcol || curwin->w_skipcol != 0)) { + && (curwin->w_topline != topline || skipcol != 0 || curwin->w_skipcol != 0)) { curwin->w_topline = topline; - if (set_skipcol) { + if (skipcol != 0) { curwin->w_skipcol = skipcol; redraw_later(curwin, UPD_NOT_VALID); - } else { + } else if (do_sms) { reset_skipcol(curwin); } } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 96b4bf5c3a..1772b0497b 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -110,7 +110,7 @@ static void log_client_msg(uint64_t channel_id, bool is_request, const char *nam # define log_server_msg(...) #endif -static PMap(cstr_t) event_strings = MAP_INIT; +static Set(cstr_t) event_strings = SET_INIT; static msgpack_sbuffer out_buffer; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -254,14 +254,12 @@ void rpc_subscribe(uint64_t id, char *event) abort(); } - char *event_string = pmap_get(cstr_t)(&event_strings, event); - - if (!event_string) { - event_string = xstrdup(event); - pmap_put(cstr_t)(&event_strings, event_string, event_string); + const char **key_alloc = NULL; + if (set_put_ref(cstr_t, &event_strings, event, &key_alloc)) { + *key_alloc = xstrdup(event); } - pmap_put(cstr_t)(channel->rpc.subscribed_events, event_string, event_string); + set_put(cstr_t, channel->rpc.subscribed_events, *key_alloc); } /// Unsubscribes to event broadcasts @@ -553,9 +551,9 @@ static void broadcast_event(const char *name, Array args) kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; - map_foreach_value(&channels, channel, { + pmap_foreach_value(&channels, channel, { if (channel->is_rpc - && pmap_has(cstr_t)(channel->rpc.subscribed_events, name)) { + && set_has(cstr_t, channel->rpc.subscribed_events, name)) { kv_push(subscribed, channel); } }); @@ -583,24 +581,12 @@ end: static void unsubscribe(Channel *channel, char *event) { - char *event_string = pmap_get(cstr_t)(&event_strings, event); - if (!event_string) { + if (!set_has(cstr_t, &event_strings, event)) { WLOG("RPC: ch %" PRIu64 ": tried to unsubscribe unknown event '%s'", channel->id, event); return; } - pmap_del(cstr_t)(channel->rpc.subscribed_events, event_string); - - map_foreach_value(&channels, channel, { - if (channel->is_rpc - && pmap_has(cstr_t)(channel->rpc.subscribed_events, event_string)) { - return; - } - }); - - // Since the string is no longer used by other channels, release it's memory - pmap_del(cstr_t)(&event_strings, event_string); - xfree(event_string); + set_del(cstr_t, channel->rpc.subscribed_events, event); } /// Mark rpc state as closed, and release its reference to the channel. @@ -630,13 +616,7 @@ void rpc_free(Channel *channel) unpacker_teardown(channel->rpc.unpacker); xfree(channel->rpc.unpacker); - // Unsubscribe from all events - char *event_string; - map_foreach_value(channel->rpc.subscribed_events, event_string, { - unsubscribe(channel, event_string); - }); - - pmap_destroy(cstr_t)(channel->rpc.subscribed_events); + set_destroy(cstr_t, channel->rpc.subscribed_events); kv_destroy(channel->rpc.call_stack); api_free_dictionary(channel->rpc.info); } @@ -648,7 +628,7 @@ static void chan_close_with_error(Channel *channel, char *msg, int loglevel) ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i); frame->returned = true; frame->errored = true; - frame->result = STRING_OBJ(cstr_to_string(msg)); + frame->result = CSTR_TO_OBJ(msg); } channel_close(channel->id, kChannelPartRpc, NULL); @@ -685,7 +665,7 @@ static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler } else { Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(err->type)); - ADD(args, STRING_OBJ(cstr_to_string(err->msg))); + ADD(args, CSTR_TO_OBJ(err->msg)); msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), args, &pac); api_free_array(args); @@ -734,3 +714,12 @@ const char *rpc_client_name(Channel *chan) return NULL; } + +void rpc_free_all_mem(void) +{ + cstr_t key; + set_foreach(&event_strings, key, { + xfree((void *)key); + }); + set_destroy(cstr_t, &event_strings); +} diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 404e68329a..1b5f0bb298 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -31,7 +31,7 @@ typedef struct { } RequestEvent; typedef struct { - PMap(cstr_t) subscribed_events[1]; + Set(cstr_t) subscribed_events[1]; bool closed; Unpacker *unpacker; uint32_t next_request_id; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 890fa0f80a..60fff45323 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -107,6 +107,8 @@ static int VIsual_mode_orig = NUL; // saved Visual mode #endif static const char e_changelist_is_empty[] = N_("E664: Changelist is empty"); +static const char e_cmdline_window_already_open[] + = N_("E1292: Command-line window is already open"); static inline void normal_state_init(NormalState *s) { @@ -2029,7 +2031,7 @@ static void display_showcmd(void) if (len > 0) { // placeholder for future highlight support ADD_C(chunk, INTEGER_OBJ(0)); - ADD_C(chunk, STRING_OBJ(cstr_as_string(showcmd_buf))); + ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf)); ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); @@ -3232,7 +3234,7 @@ static void nv_colon(cmdarg_T *cap) } if (is_lua) { - cmd_result = map_execute_lua(); + cmd_result = map_execute_lua(true); } else { // get a command line and execute it cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, @@ -5272,6 +5274,7 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) curwin->w_valid &= ~VALID_WCOL; } curwin->w_set_curswant = true; + adjust_skipcol(); } /// "g_": to the last non-blank character in the line or <count> lines downward. @@ -6371,6 +6374,10 @@ static void nv_record(cmdarg_T *cap) } if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') { + if (cmdwin_type != 0) { + emsg(_(e_cmdline_window_already_open)); + return; + } stuffcharReadbuff(cap->nchar); stuffcharReadbuff(K_CMDWIN); } else { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index bb66bb5731..de77cdd238 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5777,6 +5777,11 @@ typedef struct { int rv_arg; ///< extra argument } redo_VIsual_T; +static bool is_ex_cmdchar(cmdarg_T *cap) +{ + return cap->cmdchar == ':' || cap->cmdchar == K_COMMAND; +} + /// Handle an operator after Visual mode or when the movement is finished. /// "gui_yank" is true when yanking text for the clipboard. void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) @@ -5831,7 +5836,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if ((redo_yank || oap->op_type != OP_YANK) && ((!VIsual_active || oap->motion_force) // Also redo Operator-pending Visual mode mappings. - || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) + || ((is_ex_cmdchar(cap) || cap->cmdchar == K_LUA) && oap->op_type != OP_COLON)) && cap->cmdchar != 'D' && oap->op_type != OP_FOLD @@ -5851,17 +5856,24 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) AppendToRedobuffLit(cap->searchbuf, -1); } AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + } else if (is_ex_cmdchar(cap)) { // do_cmdline() has stored the first typed line in // "repeat_cmdline". When several lines are typed repeating // won't be possible. if (repeat_cmdline == NULL) { ResetRedobuff(); } else { - AppendToRedobuffLit(repeat_cmdline, -1); + if (cap->cmdchar == ':') { + AppendToRedobuffLit(repeat_cmdline, -1); + } else { + AppendToRedobuffSpec(repeat_cmdline); + } AppendToRedobuff(NL_STR); XFREE_CLEAR(repeat_cmdline); } + } else if (cap->cmdchar == K_LUA) { + AppendNumberToRedobuff(repeat_luaref); + AppendToRedobuff(NL_STR); } } @@ -6017,7 +6029,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) prep_redo(oap->regname, cap->count0, get_op_char(oap->op_type), get_extra_op_char(oap->op_type), oap->motion_force, cap->cmdchar, cap->nchar); - } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { + } else if (!is_ex_cmdchar(cap) && cap->cmdchar != K_LUA) { int opchar = get_op_char(oap->op_type); int extra_opchar = get_extra_op_char(oap->op_type); int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 75ea1853a0..81e006be27 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -4,8 +4,8 @@ #include <stdbool.h> #include <stddef.h> +#include "lauxlib.h" #include "nvim/ascii.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" @@ -126,4 +126,7 @@ static inline int op_reg_index(const int regname) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.h.generated.h" #endif + +EXTERN LuaRef repeat_luaref INIT(= LUA_NOREF); ///< LuaRef for "." + #endif // NVIM_OPS_H diff --git a/src/nvim/option.c b/src/nvim/option.c index fc1fc87b62..722afa64d8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1252,7 +1252,7 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - STRING_OBJ(cstr_as_string(saved_newval))); + CSTR_AS_OBJ(saved_newval)); } } xfree(saved_origval); @@ -1626,7 +1626,7 @@ int do_set(char *arg, int opt_flags) int i = (int)strlen(IObuff) + 2; if (i + (arg - startarg) < IOSIZE) { // append the argument with the error - STRCAT(IObuff, ": "); + xstrlcat(IObuff, ": ", IOSIZE); assert(arg >= startarg); memmove(IObuff + i, startarg, (size_t)(arg - startarg)); IObuff[i + (arg - startarg)] = NUL; @@ -3803,7 +3803,7 @@ void ui_refresh_options(void) value = INTEGER_OBJ(*(long *)varp); } else if (flags & P_STRING) { // cstr_as_string handles NULL string - value = STRING_OBJ(cstr_as_string(*(char **)varp)); + value = CSTR_AS_OBJ(*(char **)varp); } ui_call_option_set(name, value); } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index dc652054e8..ed9acde2b1 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -1,7 +1,7 @@ #ifndef NVIM_OPTION_DEFS_H #define NVIM_OPTION_DEFS_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/macros.h" #include "nvim/types.h" diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 1c75d5bd03..d3f676379d 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -2301,17 +2301,17 @@ static int get_encoded_char_adv(const char **p) /// Handle setting 'listchars' or 'fillchars'. /// Assume monocell characters /// -/// @param value points to either the global or the window-local value. -/// @param opt_lcs is tue for "listchars" and FALSE for "fillchars". +/// @param value points to either the global or the window-local value. +/// @param is_listchars is true for "listchars" and false for "fillchars". /// @param apply if false, do not store the flags, only check for errors. /// @return error message, NULL if it's OK. -static const char *set_chars_option(win_T *wp, const char *value, bool opt_lcs, bool apply) +static const char *set_chars_option(win_T *wp, const char *value, const bool is_listchars, + const bool apply) { const char *last_multispace = NULL; // Last occurrence of "multispace:" const char *last_lmultispace = NULL; // Last occurrence of "leadmultispace:" int multispace_len = 0; // Length of lcs-multispace string int lead_multispace_len = 0; // Length of lcs-leadmultispace string - const bool is_listchars = opt_lcs; struct chars_tab { int *cp; ///< char value @@ -2358,13 +2358,13 @@ static const char *set_chars_option(win_T *wp, const char *value, bool opt_lcs, if (is_listchars) { tab = lcs_tab; entries = ARRAY_SIZE(lcs_tab); - if (opt_lcs && wp->w_p_lcs[0] == NUL) { + if (wp->w_p_lcs[0] == NUL) { value = p_lcs; // local value is empty, use the global value } } else { tab = fcs_tab; entries = ARRAY_SIZE(fcs_tab); - if (!opt_lcs && wp->w_p_fcs[0] == NUL) { + if (wp->w_p_fcs[0] == NUL) { value = p_fcs; // local value is empty, use the global value } } diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 2a248a1e32..98ae251e2b 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -254,7 +254,7 @@ Dictionary os_proc_info(int pid) if (pe.th32ProcessID == (DWORD)pid) { PUT(pinfo, "pid", INTEGER_OBJ(pid)); PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); - PUT(pinfo, "name", STRING_OBJ(cstr_to_string(pe.szExeFile))); + PUT(pinfo, "name", CSTR_TO_OBJ(pe.szExeFile)); } return pinfo; diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 2e850f8c22..a8330acd54 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -6,6 +6,7 @@ #include <stdlib.h> #include "nvim/ascii.h" +#include "nvim/eval/typval.h" #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #include "nvim/memory.h" #include "nvim/os/os.h" diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 5235828f7a..8b62b9e895 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -130,7 +130,7 @@ char *get_xdg_home(const XDGVarType idx) xstrlcpy(IObuff, appname, appname_len + 1); #if defined(MSWIN) if (idx == kXDGDataHome || idx == kXDGStateHome) { - STRCAT(IObuff, "-data"); + xstrlcat(IObuff, "-data", IOSIZE); } #endif dir = concat_fnames_realloc(dir, IObuff, true); diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 8d77e58177..0c3b254b9a 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -74,7 +74,7 @@ void os_delay(uint64_t ms, bool ignoreinput) /// /// This blocks even "fast" events which is quite disruptive. This should only /// be used in debug code. Prefer os_delay() and decide if the delay should be -/// interupted by input or only a CTRL-C. +/// interrupted by input or only a CTRL-C. /// /// @see uv_sleep() (libuv v1.34.0) /// diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3e69e547cb..25c745ae97 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) char *s; unsigned col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, false); - if (*s == NUL) { // empty line - return 1; + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL && !cts.cts_has_virt_text) { + return 1; // be quick for an empty line } - col = win_linetabsize(wp, lnum, s, MAXCOL); + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (unsigned)cts.cts_vcol; // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } + if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right; + } clear_chartabsize_arg(&cts); return cts.cts_vcol; } @@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - } + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (unsigned)cts.cts_vcol; } @@ -292,20 +298,43 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ + for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) { + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); + } + // check for a virtual text on an empty line + if (cts->cts_has_virt_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; + } +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. -/// When "lnum" is zero do not use text properties that insert text. -void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED, - colnr_T col, char *line, char *ptr) +/// When "lnum" is zero do not use inline virtual text. +void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, + char *ptr) { cts->cts_win = wp; cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; - cts->cts_cur_text_width = 0; - // TODO(bfredl): actually lookup inline virtual text here + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; cts->cts_has_virt_text = false; + cts->cts_row = lnum - 1; + + if (cts->cts_row >= 0) { + marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row == cts->cts_row) { + cts->cts_has_virt_text = true; + } + } } /// Free any allocated item in "cts". @@ -369,7 +398,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int mb_added = 0; int numberextra; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; // No 'linebreak', 'showbreak' and 'breakindent': return quickly. if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL @@ -383,7 +413,34 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { - // TODO(bfredl): inline virtual text + int tab_size = size; + int charlen = *s == NUL ? 1 : utf_ptr2len(s); + int col = (int)(s - line); + while (true) { + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row != cts->cts_row || mark.pos.col > col) { + break; + } else if (mark.pos.col >= col && mark.pos.col < col + charlen) { + if (!mt_end(mark)) { + Decoration decor = get_decor(mark); + if (decor.virt_text_pos == kVTInline) { + if (mt_right(mark)) { + cts->cts_cur_text_width_right += decor.virt_text_width; + } else { + cts->cts_cur_text_width_left += decor.virt_text_width; + } + size += decor.virt_text_width; + if (*s == TAB) { + // tab size changes because of the inserted text + size -= tab_size; + tab_size = win_chartabsize(wp, s, vcol + size); + size += tab_size; + } + } + } + } + marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); + } } int c = (uint8_t)(*s); diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 808f6d284e..2ce7133705 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -11,10 +11,12 @@ typedef struct { win_T *cts_win; char *cts_line; // start of the line char *cts_ptr; // current position in line + int cts_row; bool cts_has_virt_text; // true if if a property inserts text - int cts_cur_text_width; // width of current inserted text - // TODO(bfredl): iterator in to the marktree for scanning virt text + int cts_cur_text_width_left; // width of virtual text left of cursor + int cts_cur_text_width_right; // width of virtual text right of cursor + MarkTreeIter cts_iter[1]; int cts_vcol; // virtual column at current position } chartabsize_T; diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index d404aa9647..15d0372c6f 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -164,10 +164,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i Array arr = arena_array(&arena, (size_t)size); for (int i = 0; i < size; i++) { Array item = arena_array(&arena, 4); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_text))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_kind))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_extra))); - ADD_C(item, STRING_OBJ(cstr_as_string(array[i].pum_info))); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_text)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_kind)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_extra)); + ADD_C(item, CSTR_AS_OBJ(array[i].pum_info)); ADD_C(arr, ARRAY_OBJ(item)); } ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, @@ -1083,7 +1083,7 @@ void pum_show_popupmenu(vimmenu_T *menu) // When there are only Terminal mode menus, using "popup Edit" results in // pum_size being zero. if (pum_size <= 0) { - emsg(e_menuothermode); + emsg(_(e_menu_only_exists_in_another_mode)); return; } diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index b4a23d544e..4b27067fb8 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -512,7 +512,7 @@ Array runtime_inspect(void) for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); Array entry = ARRAY_DICT_INIT; - ADD(entry, STRING_OBJ(cstr_to_string(item->path))); + ADD(entry, CSTR_TO_OBJ(item->path)); ADD(entry, BOOLEAN_OBJ(item->after)); if (item->has_lua != kNone) { ADD(entry, BOOLEAN_OBJ(item->has_lua == kTrue)); @@ -568,7 +568,7 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, item->path, pat_item.data.string.data); if (size < buf_len) { if (os_file_is_readable(buf)) { - ADD(rv, STRING_OBJ(cstr_to_string(buf))); + ADD(rv, CSTR_TO_OBJ(buf)); if (!all) { goto done; } @@ -646,21 +646,20 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb return done; } -static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, char *entry, +static void push_path(RuntimeSearchPath *search_path, Set(String) *rtp_used, char *entry, bool after) { - handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string(entry)); - if (h == 0) { - char *allocated = xstrdup(entry); - map_put(String, handle_T)(rtp_used, cstr_as_string(allocated), 1); - kv_push(*search_path, ((SearchPathItem){ allocated, after, kNone })); + String *key_alloc; + if (set_put_ref(String, rtp_used, cstr_as_string(entry), &key_alloc)) { + *key_alloc = cstr_to_string(entry); + kv_push(*search_path, ((SearchPathItem){ key_alloc->data, after, kNone })); } } -static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, - char *entry, bool after) +static void expand_rtp_entry(RuntimeSearchPath *search_path, Set(String) *rtp_used, char *entry, + bool after) { - if (map_get(String, handle_T)(rtp_used, cstr_as_string(entry))) { + if (set_has(String, rtp_used, cstr_as_string(entry))) { return; } @@ -679,7 +678,7 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_ } } -static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, +static void expand_pack_entry(RuntimeSearchPath *search_path, Set(String) *rtp_used, CharVec *after_path, char *pack_entry, size_t pack_entry_len) { static char buf[MAXPATHL]; @@ -712,10 +711,8 @@ static bool path_is_after(char *buf, size_t buflen) RuntimeSearchPath runtime_search_path_build(void) { kvec_t(String) pack_entries = KV_INITIAL_VALUE; - // TODO(bfredl): these should just be sets, when Set(String) is do merge to - // master. Map(String, handle_T) pack_used = MAP_INIT; - Map(String, handle_T) rtp_used = MAP_INIT; + Set(String) rtp_used = SET_INIT; RuntimeSearchPath search_path = KV_INITIAL_VALUE; CharVec after_path = KV_INITIAL_VALUE; @@ -744,7 +741,7 @@ RuntimeSearchPath runtime_search_path_build(void) // fact: &rtp entries can contain wild chars expand_rtp_entry(&search_path, &rtp_used, buf, false); - handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string(buf), false); + handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string(buf), NULL); if (h) { (*h)++; expand_pack_entry(&search_path, &rtp_used, &after_path, buf, buflen); @@ -774,8 +771,8 @@ RuntimeSearchPath runtime_search_path_build(void) // strings are not owned kv_destroy(pack_entries); kv_destroy(after_path); - map_destroy(String, handle_T)(&pack_used); - map_destroy(String, handle_T)(&rtp_used); + map_destroy(String, &pack_used); + set_destroy(String, &rtp_used); return search_path; } @@ -1547,7 +1544,7 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_ xstrlcpy(IObuff, appname, appname_len + 1); #if defined(MSWIN) if (type == kXDGDataHome || type == kXDGStateHome) { - STRCAT(IObuff, "-data"); + xstrlcat(IObuff, "-data", IOSIZE); appname_len += 5; } #endif @@ -2409,7 +2406,7 @@ void f_getscriptinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } if (sid <= 0) { - semsg(e_invargNval, "sid", tv_get_string(&sid_di->di_tv)); + semsg(_(e_invargNval), "sid", tv_get_string(&sid_di->di_tv)); return; } } else { diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index 9a810298f8..188ff7b36c 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -5,7 +5,6 @@ #include "klib/kvec.h" #include "nvim/autocmd.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval_defs.h" diff --git a/src/nvim/search.c b/src/nvim/search.c index d1a31a357f..bcaacede9e 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1519,7 +1519,7 @@ int searchc(cmdarg_T *cap, int t_cmd) } } } else { // repeat previous search - if (*lastc == NUL && lastc_bytelen == 1) { + if (*lastc == NUL && lastc_bytelen <= 1) { return FAIL; } if (dir) { // repeat in opposite direction @@ -1562,7 +1562,7 @@ int searchc(cmdarg_T *cap, int t_cmd) } col -= utf_head_off(p, p + col - 1) + 1; } - if (lastc_bytelen == 1) { + if (lastc_bytelen <= 1) { if (p[col] == c && stop) { break; } diff --git a/src/nvim/search.h b/src/nvim/search.h index 2f140ba840..eeff9f8744 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -5,7 +5,6 @@ #include <stdint.h> #include "nvim/buffer_defs.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/normal.h" #include "nvim/os/time.h" diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c405b8ca5f..db911f4bfd 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -16,7 +16,6 @@ #include <uv.h> #include "auto/config.h" -#include "klib/khash.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" @@ -35,6 +34,7 @@ #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/macros.h" +#include "nvim/map.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -62,19 +62,6 @@ # include ENDIAN_INCLUDE_FILE #endif -// Note: when using bufset hash pointers are intentionally casted to uintptr_t -// and not to khint32_t or khint64_t: this way compiler must give a warning -// (-Wconversion) when types change. -#ifdef ARCH_32 -KHASH_SET_INIT_INT(bufset) -#elif defined(ARCH_64) -KHASH_SET_INIT_INT64(bufset) -#else -# error Not a 64- or 32-bit architecture -#endif -KHASH_MAP_INIT_STR(fnamebufs, buf_T *) -KHASH_SET_INIT_STR(strset) - #define SEARCH_KEY_MAGIC "sm" #define SEARCH_KEY_SMARTCASE "sc" #define SEARCH_KEY_HAS_LINE_OFFSET "sl" @@ -305,8 +292,6 @@ typedef struct hm_llist_entry { struct hm_llist_entry *prev; ///< Pointer to previous entry or NULL. } HMLListEntry; -KHASH_MAP_INIT_STR(hmll_entries, HMLListEntry *) - /// Sized linked list structure for history merger typedef struct { HMLListEntry *entries; ///< Pointer to the start of the allocated array of @@ -318,9 +303,8 @@ typedef struct { HMLListEntry *last_free_entry; ///< Last unused element in entries array. size_t size; ///< Number of allocated entries. size_t num_entries; ///< Number of entries already used. - khash_t(hmll_entries) contained_entries; ///< Hash mapping all history entry - ///< strings to corresponding entry - ///< pointers. + PMap(cstr_t) contained_entries; ///< Map all history entry strings to + ///< corresponding entry pointers. } HMLList; typedef struct { @@ -348,8 +332,6 @@ typedef struct { Timestamp greatest_timestamp; ///< Greatest timestamp among marks. } FileMarks; -KHASH_MAP_INIT_STR(file_marks, FileMarks) - /// State structure used by shada_write /// /// Before actually writing most of the data is read to this structure. @@ -363,8 +345,8 @@ typedef struct { PossiblyFreedShadaEntry search_pattern; ///< Last search pattern. PossiblyFreedShadaEntry sub_search_pattern; ///< Last s/ search pattern. PossiblyFreedShadaEntry replacement; ///< Last s// replacement string. - khash_t(strset) dumped_variables; ///< Names of already dumped variables. - khash_t(file_marks) file_marks; ///< All file marks. + Set(cstr_t) dumped_variables; ///< Names of already dumped variables. + PMap(cstr_t) file_marks; ///< All file marks. } WriteMergerState; struct sd_read_def; @@ -504,7 +486,7 @@ static inline void hmll_init(HMLList *const hmll, const size_t size) .free_entry = NULL, .size = size, .num_entries = 0, - .contained_entries = KHASH_EMPTY_TABLE(hmll_entries), + .contained_entries = MAP_INIT, }; hmll->last_free_entry = hmll->entries; } @@ -535,10 +517,10 @@ static inline void hmll_remove(HMLList *const hmll, HMLListEntry *const hmll_ent assert(hmll->free_entry == NULL); hmll->free_entry = hmll_entry; } - const khiter_t k = kh_get(hmll_entries, &hmll->contained_entries, - hmll_entry->data.data.history_item.string); - assert(k != kh_end(&hmll->contained_entries)); - kh_del(hmll_entries, &hmll->contained_entries, k); + ptr_t val = pmap_del(cstr_t)(&hmll->contained_entries, + hmll_entry->data.data.history_item.string, NULL); + assert(val); + (void)val; if (hmll_entry->next == NULL) { hmll->last = hmll_entry->prev; } else { @@ -586,11 +568,11 @@ static inline void hmll_insert(HMLList *const hmll, HMLListEntry *hmll_entry, co } target_entry->data = data; target_entry->can_free_entry = can_free_entry; - int kh_ret; - const khiter_t k = kh_put(hmll_entries, &hmll->contained_entries, - data.data.history_item.string, &kh_ret); - if (kh_ret > 0) { - kh_val(&hmll->contained_entries, k) = target_entry; + bool new_item = false; + ptr_t *val = pmap_put_ref(cstr_t)(&hmll->contained_entries, data.data.history_item.string, + NULL, &new_item); + if (new_item) { + *val = target_entry; } hmll->num_entries++; target_entry->prev = hmll_entry; @@ -614,7 +596,7 @@ static inline void hmll_insert(HMLList *const hmll, HMLListEntry *hmll_entry, co static inline void hmll_dealloc(HMLList *const hmll) FUNC_ATTR_NONNULL_ALL { - kh_dealloc(hmll_entries, &hmll->contained_entries); + map_destroy(cstr_t, &hmll->contained_entries); xfree(hmll->entries); } @@ -771,30 +753,6 @@ static void close_file(void *cookie) } } -/// Check whether buffer is in the given set -/// -/// @param[in] set Set to check within. -/// @param[in] buf Buffer to find. -/// -/// @return true or false. -static inline bool in_bufset(const khash_t(bufset) *const set, const buf_T *buf) - FUNC_ATTR_PURE -{ - return kh_get(bufset, set, (uintptr_t)buf) != kh_end(set); -} - -/// Check whether string is in the given set -/// -/// @param[in] set Set to check within. -/// @param[in] buf Buffer to find. -/// -/// @return true or false. -static inline bool in_strset(const khash_t(strset) *const set, char *str) - FUNC_ATTR_PURE -{ - return kh_get(strset, set, str) != kh_end(set); -} - /// Msgpack callback for writing to ShaDaWriteDef* static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) { @@ -930,10 +888,11 @@ static void hms_insert(HistoryMergerState *const hms_p, const ShadaEntry entry, } } HMLList *const hmll = &hms_p->hmll; - const khiter_t k = kh_get(hmll_entries, &hms_p->hmll.contained_entries, - entry.data.history_item.string); - if (k != kh_end(&hmll->contained_entries)) { - HMLListEntry *const existing_entry = kh_val(&hmll->contained_entries, k); + cstr_t *key_alloc = NULL; + ptr_t *val = pmap_ref(cstr_t)(&hms_p->hmll.contained_entries, entry.data.history_item.string, + &key_alloc); + if (val) { + HMLListEntry *const existing_entry = *val; if (entry.timestamp > existing_entry->data.timestamp) { hmll_remove(hmll, existing_entry); } else if (!do_iter && entry.timestamp == existing_entry->data.timestamp) { @@ -944,7 +903,7 @@ static void hms_insert(HistoryMergerState *const hms_p, const ShadaEntry entry, existing_entry->data = entry; existing_entry->can_free_entry = can_free_entry; // Previous key was freed above, as part of freeing the ShaDa entry. - kh_key(&hmll->contained_entries, k) = entry.data.history_item.string; + *key_alloc = entry.data.history_item.string; return; } else { return; @@ -1046,24 +1005,27 @@ static inline void hms_dealloc(HistoryMergerState *const hms_p) /// @param[in] fname File name to find. /// /// @return Pointer to the buffer or NULL. -static buf_T *find_buffer(khash_t(fnamebufs) *const fname_bufs, const char *const fname) +static buf_T *find_buffer(PMap(cstr_t) *const fname_bufs, const char *const fname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - int kh_ret; - khint_t k = kh_put(fnamebufs, fname_bufs, fname, &kh_ret); - if (!kh_ret) { - return kh_val(fname_bufs, k); + cstr_t *key_alloc = NULL; + bool new_item = false; + buf_T **ref = (buf_T **)pmap_put_ref(cstr_t)(fname_bufs, fname, &key_alloc, &new_item); + if (new_item) { + *key_alloc = xstrdup(fname); + } else { + return *ref; // item already existed (can be a NULL value) } - kh_key(fname_bufs, k) = xstrdup(fname); + FOR_ALL_BUFFERS(buf) { if (buf->b_ffname != NULL) { if (path_fnamecmp(fname, buf->b_ffname) == 0) { - kh_val(fname_bufs, k) = buf; + *ref = buf; return buf; } } } - kh_val(fname_bufs, k) = NULL; + *ref = NULL; return NULL; } @@ -1163,9 +1125,9 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } } ShadaEntry cur_entry; - khash_t(bufset) cl_bufs = KHASH_EMPTY_TABLE(bufset); - khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); - khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); + Set(ptr_t) cl_bufs = SET_INIT; + PMap(cstr_t) fname_bufs = MAP_INIT; + Set(cstr_t) oldfiles_set = SET_INIT; if (get_old_files && (oldfiles_list == NULL || force)) { oldfiles_list = tv_list_alloc(kListLenUnknown); set_vim_var_list(VV_OLDFILES, oldfiles_list); @@ -1359,8 +1321,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) break; case kSDItemChange: case kSDItemLocalMark: { - if (get_old_files && !in_strset(&oldfiles_set, - cur_entry.data.filemark.fname)) { + if (get_old_files && !set_has(cstr_t, &oldfiles_set, cur_entry.data.filemark.fname)) { char *fname = cur_entry.data.filemark.fname; if (want_marks) { // Do not bother with allocating memory for the string if already @@ -1368,8 +1329,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) // want_marks is set because this way it may be used for a mark. fname = xstrdup(fname); } - int kh_ret; - (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + set_put(cstr_t, &oldfiles_set, fname); tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. @@ -1398,8 +1358,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) break; } } else { - int kh_ret; - (void)kh_put(bufset, &cl_bufs, (uintptr_t)buf, &kh_ret); + set_put(ptr_t, &cl_bufs, buf); #define SDE_TO_FMARK(entry) fm #define AFTERFREE(entry) (entry).data.filemark.fname = NULL #define DUMMY_IDX_ADJ(i) @@ -1440,18 +1399,18 @@ shada_read_main_cycle_end: if (cl_bufs.n_occupied) { FOR_ALL_TAB_WINDOWS(tp, wp) { (void)tp; - if (in_bufset(&cl_bufs, wp->w_buffer)) { + if (set_has(ptr_t, &cl_bufs, wp->w_buffer)) { wp->w_changelistidx = wp->w_buffer->b_changelistlen; } } } - kh_dealloc(bufset, &cl_bufs); + set_destroy(ptr_t, &cl_bufs); const char *key; - kh_foreach_key(&fname_bufs, key, { - xfree((void *)key); + map_foreach_key(&fname_bufs, key, { + xfree((char *)key); }) - kh_dealloc(fnamebufs, &fname_bufs); - kh_dealloc(strset, &oldfiles_set); + map_destroy(cstr_t, &fname_bufs); + set_destroy(cstr_t, &oldfiles_set); } /// Default shada file location: cached path @@ -2154,7 +2113,7 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re break; } case kSDItemVariable: - if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { + if (!set_has(cstr_t, &wms->dumped_variables, entry.data.global_var.name)) { ret = shada_pack_entry(packer, entry, 0); } shada_free_shada_entry(&entry); @@ -2211,13 +2170,12 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re break; } const char *const fname = entry.data.filemark.fname; - khiter_t k; - int kh_ret; - k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); - FileMarks *const filemarks = &kh_val(&wms->file_marks, k); - if (kh_ret > 0) { - CLEAR_POINTER(filemarks); + cstr_t *key = NULL; + ptr_t *val = pmap_put_ref(cstr_t)(&wms->file_marks, fname, &key, NULL); + if (*val == NULL) { + *val = xcalloc(1, sizeof(FileMarks)); } + FileMarks *const filemarks = *val; if (entry.timestamp > filemarks->greatest_timestamp) { filemarks->greatest_timestamp = entry.timestamp; } @@ -2237,9 +2195,8 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re break; } if (wms_entry->can_free_entry) { - if (kh_key(&wms->file_marks, k) - == wms_entry->data.data.filemark.fname) { - kh_key(&wms->file_marks, k) = entry.data.filemark.fname; + if (*key == wms_entry->data.data.filemark.fname) { + *key = entry.data.filemark.fname; } shada_free_shada_entry(&wms_entry->data); } @@ -2281,11 +2238,11 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re /// @param[in] removable_bufs Cache of buffers ignored due to their location. /// /// @return true or false. -static inline bool ignore_buf(const buf_T *const buf, khash_t(bufset) *const removable_bufs) +static inline bool ignore_buf(const buf_T *const buf, Set(ptr_t) *const removable_bufs) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { return (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ - || bt_terminal(buf) || in_bufset(removable_bufs, buf)); + || bt_terminal(buf) || set_has(ptr_t, removable_bufs, (ptr_t)buf)); } /// Get list of buffers to write to the shada file @@ -2293,7 +2250,7 @@ static inline bool ignore_buf(const buf_T *const buf, khash_t(bufset) *const rem /// @param[in] removable_bufs Buffers which are ignored /// /// @return ShadaEntry List of buffers to save, kSDItemBufferList entry. -static inline ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) +static inline ShadaEntry shada_get_buflist(Set(ptr_t) *const removable_bufs) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { int max_bufs = get_shada_parameter('%'); @@ -2461,12 +2418,11 @@ static inline void replace_numbered_mark(WriteMergerState *const wms, const size /// Find buffers ignored due to their location. /// /// @param[out] removable_bufs Cache of buffers ignored due to their location. -static inline void find_removable_bufs(khash_t(bufset) *removable_bufs) +static inline void find_removable_bufs(Set(ptr_t) *removable_bufs) { FOR_ALL_BUFFERS(buf) { if (buf->b_ffname != NULL && shada_removable(buf->b_ffname)) { - int kh_ret; - (void)kh_put(bufset, removable_bufs, (uintptr_t)buf, &kh_ret); + set_put(ptr_t, removable_bufs, (ptr_t)buf); } } } @@ -2518,7 +2474,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef max_reg_lines = get_shada_parameter('"'); } const bool dump_registers = (max_reg_lines != 0); - khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset); + Set(ptr_t) removable_bufs = SET_INIT; const size_t max_kbyte = (size_t)max_kbyte_i; const size_t num_marked_files = (size_t)get_shada_parameter('\''); const bool dump_global_marks = get_shada_parameter('f') != 0; @@ -2573,15 +2529,15 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef .capacity = 5, .items = ((KeyValuePair[]) { { STATIC_CSTR_AS_STRING("generator"), - STRING_OBJ(STATIC_CSTR_AS_STRING("nvim")) }, + STATIC_CSTR_AS_OBJ("nvim") }, { STATIC_CSTR_AS_STRING("version"), - STRING_OBJ(cstr_as_string(longVersion)) }, + CSTR_AS_OBJ(longVersion) }, { STATIC_CSTR_AS_STRING("max_kbyte"), INTEGER_OBJ((Integer)max_kbyte) }, { STATIC_CSTR_AS_STRING("pid"), INTEGER_OBJ((Integer)os_get_pid()) }, { STATIC_CSTR_AS_STRING("encoding"), - STRING_OBJ(cstr_as_string(p_enc)) }, + CSTR_AS_OBJ(p_enc) }, }), } } @@ -2662,8 +2618,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef tv_clear(&vartv); tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessful) { - int kh_ret; - (void)kh_put(strset, &wms->dumped_variables, name, &kh_ret); + set_put(cstr_t, &wms->dumped_variables, name); } } while (var_iter != NULL); } @@ -2723,7 +2678,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef } else { const buf_T *const buf = buflist_findnr(fm.fmark.fnum); if (buf == NULL || buf->b_ffname == NULL - || in_bufset(&removable_bufs, buf)) { + || set_has(ptr_t, &removable_bufs, (ptr_t)buf)) { continue; } fname = buf->b_ffname; @@ -2759,18 +2714,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef // Initialize buffers if (num_marked_files > 0) { FOR_ALL_BUFFERS(buf) { - if (buf->b_ffname == NULL || in_bufset(&removable_bufs, buf)) { + if (buf->b_ffname == NULL || set_has(ptr_t, &removable_bufs, buf)) { continue; } const void *local_marks_iter = NULL; const char *const fname = buf->b_ffname; - khiter_t k; - int kh_ret; - k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); - FileMarks *const filemarks = &kh_val(&wms->file_marks, k); - if (kh_ret > 0) { - CLEAR_POINTER(filemarks); + ptr_t *val = pmap_put_ref(cstr_t)(&wms->file_marks, fname, NULL, NULL); + if (*val == NULL) { + *val = xcalloc(1, sizeof(FileMarks)); } + FileMarks *const filemarks = *val; do { fmark_T fm; char name = NUL; @@ -2887,16 +2840,14 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef PACK_WMS_ENTRY(wms->replacement); #undef PACK_WMS_ENTRY - const size_t file_markss_size = kh_size(&wms->file_marks); + const size_t file_markss_size = map_size(&wms->file_marks); FileMarks **const all_file_markss = xmalloc(file_markss_size * sizeof(*all_file_markss)); FileMarks **cur_file_marks = all_file_markss; - for (khint_t i = kh_begin(&wms->file_marks); i != kh_end(&wms->file_marks); - i++) { - if (kh_exist(&wms->file_marks, i)) { - *cur_file_marks++ = &kh_val(&wms->file_marks, i); - } - } + ptr_t val; + map_foreach_value(ptr_t, &wms->file_marks, val, { + *cur_file_marks++ = val; + }) qsort((void *)all_file_markss, file_markss_size, sizeof(*all_file_markss), &compare_file_marks); const size_t file_markss_to_dump = MIN(num_marked_files, file_markss_size); @@ -2949,10 +2900,13 @@ shada_write_exit: hms_dealloc(&wms->hms[i]); } } - kh_dealloc(file_marks, &wms->file_marks); - kh_dealloc(bufset, &removable_bufs); + map_foreach_value(ptr_t, &wms->file_marks, val, { + xfree(val); + }) + map_destroy(cstr_t, &wms->file_marks); + set_destroy(ptr_t, &removable_bufs); msgpack_packer_free(packer); - kh_dealloc(strset, &wms->dumped_variables); + set_destroy(cstr_t, &wms->dumped_variables); xfree(wms); return ret; } @@ -4035,7 +3989,7 @@ static bool shada_removable(const char *name) /// /// @return number of jumplist entries static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, - khash_t(bufset) *const removable_bufs) + Set(ptr_t) *const removable_bufs) { // Initialize jump list size_t jumps_size = 0; @@ -4056,7 +4010,7 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, ? NULL : buflist_findnr(fm.fmark.fnum)); if (buf != NULL - ? in_bufset(removable_bufs, buf) + ? set_has(ptr_t, removable_bufs, (ptr_t)buf) : fm.fmark.fnum != 0) { continue; } @@ -4111,7 +4065,7 @@ void shada_encode_regs(msgpack_sbuffer *const sbuf) void shada_encode_jumps(msgpack_sbuffer *const sbuf) FUNC_ATTR_NONNULL_ALL { - khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset); + Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; size_t jumps_size = shada_init_jumps(jumps, &removable_bufs); @@ -4130,7 +4084,7 @@ void shada_encode_jumps(msgpack_sbuffer *const sbuf) void shada_encode_buflist(msgpack_sbuffer *const sbuf) FUNC_ATTR_NONNULL_ALL { - khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset); + Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); msgpack_packer packer; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 84875261f1..778266e5ac 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1189,11 +1189,19 @@ bool spell_valid_case(int wordflags, int treeflags) || (wordflags & WF_ONECAP) != 0)); } -// Returns true if spell checking is not enabled. +/// Return true if spell checking is enabled for "wp". +bool spell_check_window(win_T *wp) +{ + return wp->w_p_spell + && *wp->w_s->b_p_spl != NUL + && wp->w_s->b_langp.ga_len > 0 + && *(char **)(wp->w_s->b_langp.ga_data) != NULL; +} + +/// Return true and give an error if spell checking is not enabled. bool no_spell_checking(win_T *wp) { - if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL - || GA_EMPTY(&wp->w_s->b_langp)) { + if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL || GA_EMPTY(&wp->w_s->b_langp)) { emsg(_(e_no_spell)); return true; } @@ -1304,7 +1312,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att } else if (curline && wp == curwin) { // For spellbadword(): check if first word needs a capital. col = (colnr_T)getwhitecols(line); - if (check_need_cap(lnum, col)) { + if (check_need_cap(curwin, lnum, col)) { capcol = col; } @@ -2528,25 +2536,24 @@ int spell_casefold(const win_T *wp, char *str, int len, char *buf, int buflen) } // Check if the word at line "lnum" column "col" is required to start with a -// capital. This uses 'spellcapcheck' of the current buffer. -bool check_need_cap(linenr_T lnum, colnr_T col) +// capital. This uses 'spellcapcheck' of the buffer in window "wp". +bool check_need_cap(win_T *wp, linenr_T lnum, colnr_T col) { - bool need_cap = false; - - if (curwin->w_s->b_cap_prog == NULL) { + if (wp->w_s->b_cap_prog == NULL) { return false; } - char *line = get_cursor_line_ptr(); + bool need_cap = false; + char *line = col ? ml_get_buf(wp->w_buffer, lnum, false) : NULL; char *line_copy = NULL; colnr_T endcol = 0; - if (getwhitecols(line) >= (int)col) { + if (col == 0 || getwhitecols(line) >= col) { // At start of line, check if previous line is empty or sentence // ends there. if (lnum == 1) { need_cap = true; } else { - line = ml_get(lnum - 1); + line = ml_get_buf(wp->w_buffer, lnum - 1, false); if (*skipwhite(line) == NUL) { need_cap = true; } else { @@ -2563,13 +2570,13 @@ bool check_need_cap(linenr_T lnum, colnr_T col) if (endcol > 0) { // Check if sentence ends before the bad word. regmatch_T regmatch = { - .regprog = curwin->w_s->b_cap_prog, + .regprog = wp->w_s->b_cap_prog, .rm_ic = false }; char *p = line + endcol; while (true) { MB_PTR_BACK(line, p); - if (p == line || spell_iswordp_nmw(p, curwin)) { + if (p == line || spell_iswordp_nmw(p, wp)) { break; } if (vim_regexec(®match, p, 0) @@ -2578,7 +2585,7 @@ bool check_need_cap(linenr_T lnum, colnr_T col) break; } } - curwin->w_s->b_cap_prog = regmatch.regprog; + wp->w_s->b_cap_prog = regmatch.regprog; } xfree(line_copy); @@ -3593,7 +3600,7 @@ static bool spell_expand_need_cap; void spell_expand_check_cap(colnr_T col) { - spell_expand_need_cap = check_need_cap(curwin->w_cursor.lnum, col); + spell_expand_need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, col); } // Get list of spelling suggestions. diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 5e4a429cc7..e81cebe18a 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -3131,7 +3131,7 @@ static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile) // Remove CR, LF and white space from the end. White space halfway through // the word is kept to allow multi-word terms like "et al.". l = (int)strlen(line); - while (l > 0 && line[l - 1] <= ' ') { + while (l > 0 && (uint8_t)line[l - 1] <= ' ') { l--; } if (l == 0) { diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 84be88be7b..1c34c2487f 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -509,7 +509,7 @@ void spell_suggest(int count) // Get the word and its length. // Figure out if the word should be capitalised. - int need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col); + int need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, curwin->w_cursor.col); // Make a copy of current line since autocommands may free the line. line = xstrdup(get_cursor_line_ptr()); diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index a6fd1d677b..015c578396 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -336,7 +336,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) grid_adjust(&grid, &row, &col); if (row < 0) { - return; + goto theend; } fillchar = wp->w_p_fcs_chars.wbr; @@ -596,7 +596,7 @@ void win_redr_ruler(win_T *wp) MAXSIZE_TEMP_ARRAY(content, 1); MAXSIZE_TEMP_ARRAY(chunk, 2); ADD_C(chunk, INTEGER_OBJ(attr)); - ADD_C(chunk, STRING_OBJ(cstr_as_string(buffer))); + ADD_C(chunk, CSTR_AS_OBJ(buffer)); ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 5231ec0841..4e521b14f7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1502,7 +1502,8 @@ char *strrep(const char *src, const char *what, const char *rep) return ret; } -static void byteidx(typval_T *argvars, typval_T *rettv, int comp) +/// Implementation of "byteidx()" and "byteidxcomp()" functions +static void byteidx_common(typval_T *argvars, typval_T *rettv, int comp) { rettv->vval.v_number = -1; @@ -1514,7 +1515,11 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) varnumber_T utf16idx = false; if (argvars[2].v_type != VAR_UNKNOWN) { - utf16idx = tv_get_bool(&argvars[2]); + bool error = false; + utf16idx = tv_get_bool_chk(&argvars[2], &error); + if (error) { + return; + } if (utf16idx < 0 || utf16idx > 1) { semsg(_(e_using_number_as_bool_nr), utf16idx); return; @@ -1550,13 +1555,13 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) /// "byteidx()" function void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - byteidx(argvars, rettv, false); + byteidx_common(argvars, rettv, false); } /// "byteidxcomp()" function void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - byteidx(argvars, rettv, true); + byteidx_common(argvars, rettv, true); } /// "charidx()" function @@ -1764,16 +1769,21 @@ void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "strchars()" function void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - int skipcc = false; + varnumber_T skipcc = false; if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = (int)tv_get_bool(&argvars[1]); - } - if (skipcc < 0 || skipcc > 1) { - semsg(_(e_using_number_as_bool_nr), skipcc); - } else { - strchar_common(argvars, rettv, skipcc); + bool error = false; + skipcc = tv_get_bool_chk(&argvars[1], &error); + if (error) { + return; + } + if (skipcc < 0 || skipcc > 1) { + semsg(_(e_using_number_as_bool_nr), skipcc); + return; + } } + + strchar_common(argvars, rettv, skipcc); } /// "strutf16len()" function @@ -1840,7 +1850,10 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (!error) { if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { - skipcc = tv_get_bool(&argvars[3]); + skipcc = tv_get_bool_chk(&argvars[3], &error); + if (error) { + return; + } if (skipcc < 0 || skipcc > 1) { semsg(_(e_using_number_as_bool_nr), skipcc); return; diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 6ad9daf5bf..f4ccf9b30c 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -6,7 +6,7 @@ #include <string.h> #include "klib/kvec.h" -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/types.h" /// Append string to string and return pointer to the next byte diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ff16a10d8e..18331cc95d 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -732,7 +732,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) num_matches, max_num_matches != MAXCOL ? _(" or more") : ""); if (ic) { - STRCAT(IObuff, _(" Using tag with different case!")); + xstrlcat(IObuff, _(" Using tag with different case!"), IOSIZE); } if ((num_matches > prev_num_matches || new_tag) && num_matches > 1) { @@ -2901,21 +2901,10 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, int keep_help) buf_T *const existing_buf = buflist_findname_exp(fname); if (existing_buf != NULL) { - const win_T *wp = NULL; - - if (swb_flags & SWB_USEOPEN) { - wp = buf_jump_open_win(existing_buf); - } - - // If 'switchbuf' contains "usetab": jump to first window in any tab - // page containing "existing_buf" if one exists - if (wp == NULL && (swb_flags & SWB_USETAB)) { - wp = buf_jump_open_tab(existing_buf); - } - - // We've switched to the buffer, the usual loading of the file must - // be skipped. - if (wp != NULL) { + // If 'switchbuf' is set jump to the window containing "buf". + if (swbuf_goto_win_with_buf(existing_buf) != NULL) { + // We've switched to the buffer, the usual loading of the file + // must be skipped. getfile_result = GETFILE_SAME_FILE; } } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 0686305d02..792071963d 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -169,7 +169,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = { .sb_popline = term_sb_pop, }; -static PMap(ptr_t) invalidated_terminals = MAP_INIT; +static Set(ptr_t) invalidated_terminals = SET_INIT; void terminal_init(void) { @@ -183,10 +183,10 @@ void terminal_teardown(void) time_watcher_stop(&refresh_timer); multiqueue_free(refresh_timer.events); time_watcher_close(&refresh_timer, NULL); - pmap_destroy(ptr_t)(&invalidated_terminals); + set_destroy(ptr_t, &invalidated_terminals); // terminal_destroy might be called after terminal_teardown is invoked // make sure it is in an empty, valid state - pmap_init(ptr_t, &invalidated_terminals); + invalidated_terminals = (Set(ptr_t)) SET_INIT; } static void term_output_callback(const char *s, size_t len, void *user_data) @@ -220,6 +220,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) // Set up screen rv->vts = vterm_obtain_screen(rv->vt); vterm_screen_enable_altscreen(rv->vts, true); + vterm_screen_enable_reflow(rv->vts, true); // delete empty lines at the end of the buffer vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); @@ -596,7 +597,7 @@ static int terminal_execute(VimState *state, int key) break; case K_LUA: - map_execute_lua(); + map_execute_lua(false); break; case Ctrl_N: @@ -652,12 +653,12 @@ void terminal_destroy(Terminal **termpp) } if (!term->refcount) { - if (pmap_has(ptr_t)(&invalidated_terminals, term)) { + if (set_has(ptr_t, &invalidated_terminals, term)) { // flush any pending changes to the buffer block_autocmds(); refresh_terminal(term); unblock_autocmds(); - pmap_del(ptr_t)(&invalidated_terminals, term); + set_del(ptr_t, &invalidated_terminals, term); } for (size_t i = 0; i < term->sb_current; i++) { xfree(term->sb_buffer[i]); @@ -1027,7 +1028,7 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) } memcpy(sbrow->cells, cells, sizeof(cells[0]) * c); - pmap_put(ptr_t)(&invalidated_terminals, term, NULL); + set_put(ptr_t, &invalidated_terminals, term); return 1; } @@ -1068,7 +1069,7 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) } xfree(sbrow); - pmap_put(ptr_t)(&invalidated_terminals, term, NULL); + set_put(ptr_t, &invalidated_terminals, term); return 1; } @@ -1524,7 +1525,7 @@ static void invalidate_terminal(Terminal *term, int start_row, int end_row) term->invalid_end = MAX(term->invalid_end, end_row); } - pmap_put(ptr_t)(&invalidated_terminals, term, NULL); + set_put(ptr_t, &invalidated_terminals, term); if (!refresh_pending) { time_watcher_start(&refresh_timer, refresh_timer_cb, REFRESH_DELAY, 0); refresh_pending = true; @@ -1567,10 +1568,10 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data) void *stub; (void)(stub); // don't process autocommands while updating terminal buffers block_autocmds(); - map_foreach(&invalidated_terminals, term, stub, { + set_foreach(&invalidated_terminals, term, { refresh_terminal(term); }); - pmap_clear(ptr_t)(&invalidated_terminals); + set_clear(ptr_t, &invalidated_terminals); unblock_autocmds(); } diff --git a/src/nvim/testing.c b/src/nvim/testing.c index 5483a58525..25ec8e898a 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -418,11 +418,11 @@ static int assert_equalfile(typval_T *argvars) const int c2 = fgetc(fd2); if (c1 == EOF) { if (c2 != EOF) { - STRCPY(IObuff, "first file is shorter"); + xstrlcpy(IObuff, "first file is shorter", IOSIZE); } break; } else if (c2 == EOF) { - STRCPY(IObuff, "second file is shorter"); + xstrlcpy(IObuff, "second file is shorter", IOSIZE); break; } else { line1[lineidx] = (char)c1; diff --git a/src/nvim/testing.h b/src/nvim/testing.h index 69596d725c..69ceeb16b5 100644 --- a/src/nvim/testing.h +++ b/src/nvim/testing.h @@ -1,7 +1,7 @@ #ifndef NVIM_TESTING_H #define NVIM_TESTING_H -#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "testing.h.generated.h" diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index 428b14a68d..5036c10827 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -22,6 +22,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option_defs.h" #include "nvim/pos.h" @@ -414,6 +415,7 @@ int bck_word(long count, bool bigword, bool stop) finished: stop = false; } + adjust_skipcol(); return OK; } @@ -518,6 +520,7 @@ int bckend_word(long count, bool bigword, bool eol) } } } + adjust_skipcol(); return OK; } diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 73ed7b6096..30a1af68ad 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -33,7 +33,7 @@ #define KEY_BUFFER_SIZE 0xfff static const struct kitty_key_map_entry { - KittyKey key; + int key; const char *name; } kitty_key_map_entry[] = { { KITTY_KEY_ESCAPE, "Esc" }, @@ -115,7 +115,7 @@ static const struct kitty_key_map_entry { { KITTY_KEY_KP_BEGIN, "kOrigin" }, }; -static Map(KittyKey, cstr_t) kitty_key_map = MAP_INIT; +static Map(int, cstr_t) kitty_key_map = MAP_INIT; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/input.c.generated.h" @@ -135,8 +135,8 @@ void tinput_init(TermInput *input, Loop *loop) input->key_buffer = rbuffer_new(KEY_BUFFER_SIZE); for (size_t i = 0; i < ARRAY_SIZE(kitty_key_map_entry); i++) { - map_put(KittyKey, cstr_t)(&kitty_key_map, kitty_key_map_entry[i].key, - kitty_key_map_entry[i].name); + map_put(int, cstr_t)(&kitty_key_map, kitty_key_map_entry[i].key, + kitty_key_map_entry[i].name); } input->in_fd = STDIN_FILENO; @@ -162,7 +162,7 @@ void tinput_init(TermInput *input, Loop *loop) void tinput_destroy(TermInput *input) { - map_destroy(KittyKey, cstr_t)(&kitty_key_map); + map_destroy(int, &kitty_key_map); rbuffer_free(input->key_buffer); time_watcher_close(&input->timer_handle, NULL); stream_close(&input->read_stream, NULL, NULL); @@ -231,7 +231,7 @@ static void tinput_enqueue(TermInput *input, char *buf, size_t size) static void handle_kitty_key_protocol(TermInput *input, TermKeyKey *key) { - const char *name = map_get(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint); + const char *name = map_get(int, cstr_t)(&kitty_key_map, (int)key->code.codepoint); if (name) { char buf[64]; size_t len = 0; @@ -257,7 +257,7 @@ static void forward_simple_utf8(TermInput *input, TermKeyKey *key) char *ptr = key->utf8; if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF - && map_has(KittyKey, cstr_t)(&kitty_key_map, (KittyKey)key->code.codepoint)) { + && map_has(int, cstr_t)(&kitty_key_map, (int)key->code.codepoint)) { handle_kitty_key_protocol(input, key); return; } @@ -286,8 +286,7 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) } else { assert(key->modifiers); if (key->code.codepoint >= 0xE000 && key->code.codepoint <= 0xF8FF - && map_has(KittyKey, cstr_t)(&kitty_key_map, - (KittyKey)key->code.codepoint)) { + && map_has(int, cstr_t)(&kitty_key_map, (int)key->code.codepoint)) { handle_kitty_key_protocol(input, key); return; } @@ -548,8 +547,8 @@ static void set_bg(char *bgvalue) { if (ui_client_attached) { MAXSIZE_TEMP_ARRAY(args, 2); - ADD_C(args, STRING_OBJ(cstr_as_string("term_background"))); - ADD_C(args, STRING_OBJ(cstr_as_string(bgvalue))); + ADD_C(args, CSTR_AS_OBJ("term_background")); + ADD_C(args, CSTR_AS_OBJ(bgvalue)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 2de1467511..c9d9b08b79 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -106,6 +106,7 @@ struct TUIData { bool bce; bool mouse_enabled; bool mouse_move_enabled; + bool title_enabled; bool busy, is_invisible, want_invisible; bool cork, overflow; bool set_cursor_color_as_str; @@ -325,8 +326,6 @@ static void terminfo_start(TUIData *tui) // Enter alternate screen, save title, and clear. // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(tui, unibi_enter_ca_mode); - // Save title/icon to the "stack". #4063 - unibi_out_ext(tui, tui->unibi_ext.save_title); unibi_out(tui, unibi_keypad_xmit); unibi_out(tui, unibi_clear_screen); // Ask the terminal to send us the background color. @@ -383,8 +382,7 @@ static void terminfo_stop(TUIData *tui) // Disable extended keys before exiting alternate screen. unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys); unibi_out(tui, unibi_exit_ca_mode); - // Restore title/icon from the "stack". #4063 - unibi_out_ext(tui, tui->unibi_ext.restore_title); + tui_set_title(tui, (String)STRING_INIT); if (tui->cursor_color_changed) { unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); } @@ -1304,16 +1302,16 @@ static void show_verbose_terminfo(TUIData *tui) Array chunks = ARRAY_DICT_INIT; Array title = ARRAY_DICT_INIT; - ADD(title, STRING_OBJ(cstr_to_string("\n\n--- Terminal info --- {{{\n"))); - ADD(title, STRING_OBJ(cstr_to_string("Title"))); + ADD(title, CSTR_TO_OBJ("\n\n--- Terminal info --- {{{\n")); + ADD(title, CSTR_TO_OBJ("Title")); ADD(chunks, ARRAY_OBJ(title)); Array info = ARRAY_DICT_INIT; String str = terminfo_info_msg(ut, tui->term); ADD(info, STRING_OBJ(str)); ADD(chunks, ARRAY_OBJ(info)); Array end_fold = ARRAY_DICT_INIT; - ADD(end_fold, STRING_OBJ(cstr_to_string("}}}\n"))); - ADD(end_fold, STRING_OBJ(cstr_to_string("Title"))); + ADD(end_fold, CSTR_TO_OBJ("}}}\n")); + ADD(end_fold, CSTR_TO_OBJ("Title")); ADD(chunks, ARRAY_OBJ(end_fold)); Array args = ARRAY_DICT_INIT; @@ -1361,13 +1359,24 @@ void tui_suspend(TUIData *tui) void tui_set_title(TUIData *tui, String title) { - if (!(title.data && unibi_get_str(tui->ut, unibi_to_status_line) + if (!(unibi_get_str(tui->ut, unibi_to_status_line) && unibi_get_str(tui->ut, unibi_from_status_line))) { return; } - unibi_out(tui, unibi_to_status_line); - out(tui, title.data, title.size); - unibi_out(tui, unibi_from_status_line); + if (title.size > 0) { + if (!tui->title_enabled) { + // Save title/icon to the "stack". #4063 + unibi_out_ext(tui, tui->unibi_ext.save_title); + tui->title_enabled = true; + } + unibi_out(tui, unibi_to_status_line); + out(tui, title.data, title.size); + unibi_out(tui, unibi_from_status_line); + } else if (tui->title_enabled) { + // Restore title/icon from the "stack". #4063 + unibi_out_ext(tui, tui->unibi_ext.restore_title); + tui->title_enabled = false; + } } void tui_set_icon(TUIData *tui, String icon) @@ -1416,7 +1425,7 @@ void tui_option_set(TUIData *tui, String name, Object value) if (ui_client_channel_id) { MAXSIZE_TEMP_ARRAY(args, 2); - ADD_C(args, STRING_OBJ(cstr_as_string("rgb"))); + ADD_C(args, CSTR_AS_OBJ("rgb")); ADD_C(args, BOOLEAN_OBJ(value.data.boolean)); rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 6d09b9f3a3..8c31032492 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -127,10 +127,10 @@ void ui_free_all_mem(void) kv_destroy(call_buf); UIEventCallback *event_cb; - map_foreach_value(&ui_event_cbs, event_cb, { + pmap_foreach_value(&ui_event_cbs, event_cb, { free_ui_event_callback(event_cb); }) - pmap_destroy(uint32_t)(&ui_event_cbs); + map_destroy(uint32_t, &ui_event_cbs); } #endif @@ -613,8 +613,8 @@ Array ui_array(void) PUT(info, "override", BOOLEAN_OBJ(ui->override)); // TUI fields. (`stdin_fd` is intentionally omitted.) - PUT(info, "term_name", STRING_OBJ(cstr_to_string(ui->term_name))); - PUT(info, "term_background", STRING_OBJ(cstr_to_string(ui->term_background))); + PUT(info, "term_name", CSTR_TO_OBJ(ui->term_name)); + PUT(info, "term_background", CSTR_TO_OBJ(ui->term_background)); PUT(info, "term_colors", INTEGER_OBJ(ui->term_colors)); PUT(info, "stdin_tty", BOOLEAN_OBJ(ui->stdin_tty)); PUT(info, "stdout_tty", BOOLEAN_OBJ(ui->stdout_tty)); @@ -660,7 +660,7 @@ void ui_call_event(char *name, Array args) { UIEventCallback *event_cb; bool handled = false; - map_foreach_value(&ui_event_cbs, event_cb, { + pmap_foreach_value(&ui_event_cbs, event_cb, { Error err = ERROR_INIT; Object res = nlua_call_ref(event_cb->cb, name, args, false, &err); if (res.type == kObjectTypeBoolean && res.data.boolean == true) { @@ -686,7 +686,7 @@ void ui_cb_update_ext(void) for (size_t i = 0; i < kUIGlobalCount; i++) { UIEventCallback *event_cb; - map_foreach_value(&ui_event_cbs, event_cb, { + pmap_foreach_value(&ui_event_cbs, event_cb, { if (event_cb->ext_widgets[i]) { ui_cb_ext[i] = true; break; @@ -710,9 +710,9 @@ void ui_add_cb(uint32_t ns_id, LuaRef cb, bool *ext_widgets) event_cb->ext_widgets[kUICmdline] = true; } - UIEventCallback **item = (UIEventCallback **)pmap_ref(uint32_t)(&ui_event_cbs, ns_id, true); + ptr_t *item = pmap_put_ref(uint32_t)(&ui_event_cbs, ns_id, NULL, NULL); if (*item) { - free_ui_event_callback(*item); + free_ui_event_callback((UIEventCallback *)(*item)); } *item = event_cb; @@ -723,8 +723,8 @@ void ui_add_cb(uint32_t ns_id, LuaRef cb, bool *ext_widgets) void ui_remove_cb(uint32_t ns_id) { if (pmap_has(uint32_t)(&ui_event_cbs, ns_id)) { - free_ui_event_callback(pmap_get(uint32_t)(&ui_event_cbs, ns_id)); - pmap_del(uint32_t)(&ui_event_cbs, ns_id); + UIEventCallback *item = pmap_del(uint32_t)(&ui_event_cbs, ns_id, NULL); + free_ui_event_callback(item); } ui_cb_update_ext(); ui_refresh(); diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index b93b31f7dc..1918b0b800 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -83,11 +83,11 @@ void ui_client_attach(int width, int height, char *term) PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { - PUT_C(opts, "term_name", STRING_OBJ(cstr_as_string(term))); + PUT_C(opts, "term_name", CSTR_AS_OBJ(term)); } if (ui_client_bg_response != kNone) { bool is_dark = (ui_client_bg_response == kTrue); - PUT_C(opts, "term_background", STRING_OBJ(cstr_as_string(is_dark ? "dark" : "light"))); + PUT_C(opts, "term_background", CSTR_AS_OBJ(is_dark ? "dark" : "light")); } PUT_C(opts, "term_colors", INTEGER_OBJ(t_colors)); if (!ui_client_is_remote) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 35f46e512d..1eb73d85d7 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -120,6 +120,7 @@ #include "nvim/path.h" #include "nvim/pos.h" #include "nvim/sha256.h" +#include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/types.h" @@ -2372,6 +2373,12 @@ static void u_undoredo(int undo, bool do_buf_event) } changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event); + // When text has been changed, possibly the start of the next line + // may have SpellCap that should be removed or it needs to be + // displayed. Schedule the next line for redrawing just in case. + if (spell_check_window(curwin) && bot <= curbuf->b_ml.ml_line_count) { + redrawWinline(curwin, bot); + } // Set the '[ mark. if (top + 1 < curbuf->b_op_start.lnum) { @@ -2667,7 +2674,7 @@ void ex_undolist(exarg_T *eap) undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { while (strlen(IObuff) < 33) { - STRCAT(IObuff, " "); + xstrlcat(IObuff, " ", IOSIZE); } vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr); } diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 11f2620aaa..c56b241988 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1754,8 +1754,8 @@ Dictionary commands_array(buf_T *buf) Dictionary d = ARRAY_DICT_INIT; ucmd_T *cmd = USER_CMD_GA(gap, i); - PUT(d, "name", STRING_OBJ(cstr_to_string(cmd->uc_name))); - PUT(d, "definition", STRING_OBJ(cstr_to_string(cmd->uc_rep))); + PUT(d, "name", CSTR_TO_OBJ(cmd->uc_name)); + PUT(d, "definition", CSTR_TO_OBJ(cmd->uc_rep)); PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid)); PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); @@ -1775,21 +1775,21 @@ Dictionary commands_array(buf_T *buf) case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; } - PUT(d, "nargs", STRING_OBJ(cstr_to_string(arg))); + PUT(d, "nargs", CSTR_TO_OBJ(arg)); char *cmd_compl = get_command_complete(cmd->uc_compl); PUT(d, "complete", (cmd_compl == NULL - ? NIL : STRING_OBJ(cstr_to_string(cmd_compl)))); + ? NIL : CSTR_TO_OBJ(cmd_compl))); PUT(d, "complete_arg", cmd->uc_compl_arg == NULL - ? NIL : STRING_OBJ(cstr_to_string(cmd->uc_compl_arg))); + ? NIL : CSTR_TO_OBJ(cmd->uc_compl_arg)); Object obj = NIL; if (cmd->uc_argt & EX_COUNT) { if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = STRING_OBJ(cstr_to_string(str)); // -count=N + obj = CSTR_TO_OBJ(str); // -count=N } else { - obj = STRING_OBJ(cstr_to_string("0")); // -count + obj = CSTR_TO_OBJ("0"); // -count } } PUT(d, "count", obj); @@ -1797,12 +1797,12 @@ Dictionary commands_array(buf_T *buf) obj = NIL; if (cmd->uc_argt & EX_RANGE) { if (cmd->uc_argt & EX_DFLALL) { - obj = STRING_OBJ(cstr_to_string("%")); // -range=% + obj = CSTR_TO_OBJ("%"); // -range=% } else if (cmd->uc_def >= 0) { snprintf(str, sizeof(str), "%" PRId64, cmd->uc_def); - obj = STRING_OBJ(cstr_to_string(str)); // -range=N + obj = CSTR_TO_OBJ(str); // -range=N } else { - obj = STRING_OBJ(cstr_to_string(".")); // -range + obj = CSTR_TO_OBJ("."); // -range } } PUT(d, "range", obj); @@ -1811,7 +1811,7 @@ Dictionary commands_array(buf_T *buf) for (int j = 0; addr_type_complete[j].expand != ADDR_NONE; j++) { if (addr_type_complete[j].expand != ADDR_LINES && addr_type_complete[j].expand == cmd->uc_addr_type) { - obj = STRING_OBJ(cstr_to_string(addr_type_complete[j].name)); + obj = CSTR_TO_OBJ(addr_type_complete[j].name); break; } } diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index d1c6426cba..830d8c5f34 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -2866,7 +2866,7 @@ viml_pexpr_parse_no_paren_closing_error: {} case kENodeOperator: if (prev_token.type == kExprLexSpacing) { // For some reason "function (args)" is a function call, but - // "(funcref) (args)" is not. AFAIR this somehow involves + // "(funcref) (args)" is not. As far as I remember this somehow involves // compatibility and Bram was commenting that this is // intentionally inconsistent and he is not very happy with the // situation himself. diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index 6fe6a784a0..245f4c500c 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -5,7 +5,6 @@ #include <stddef.h> #include <stdint.h> -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/types.h" #include "nvim/viml/parser/parser.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index fd6755a382..90c8ba92f9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -139,6 +139,32 @@ win_T *prevwin_curwin(void) return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin; } +/// If the 'switchbuf' option contains "useopen" or "usetab", then try to jump +/// to a window containing "buf". +/// Returns the pointer to the window that was jumped to or NULL. +win_T *swbuf_goto_win_with_buf(buf_T *buf) +{ + win_T *wp = NULL; + + if (buf == NULL) { + return wp; + } + + // If 'switchbuf' contains "useopen": jump to first window in the current + // tab page containing "buf" if one exists. + if (swb_flags & SWB_USEOPEN) { + wp = buf_jump_open_win(buf); + } + + // If 'switchbuf' contains "usetab": jump to first window in any tab page + // containing "buf" if one exists. + if (wp == NULL && (swb_flags & SWB_USETAB)) { + wp = buf_jump_open_tab(buf); + } + + return wp; +} + /// all CTRL-W window commands are handled here, called from normal_cmd(). /// /// @param xchar extra char from ":wincmd gx" or NUL @@ -514,18 +540,31 @@ wingotofile: tabpage_T *oldtab = curtab; win_T *oldwin = curwin; setpcmark(); - if (win_split(0, 0) == OK) { + + // If 'switchbuf' is set to 'useopen' or 'usetab' and the + // file is already opened in a window, then jump to it. + win_T *wp = NULL; + if ((swb_flags & (SWB_USEOPEN | SWB_USETAB)) + && cmdmod.cmod_tab == 0) { + wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr)); + } + + if (wp == NULL && win_split(0, 0) == OK) { RESET_BINDING(curwin); if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { // Failed to open the file, close the window opened for it. win_close(curwin, false, false); goto_tabpage_win(oldtab, oldwin); - } else if (nchar == 'F' && lnum >= 0) { - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_SOL | BL_FIX); + } else { + wp = curwin; } } + + if (wp != NULL && nchar == 'F' && lnum >= 0) { + curwin->w_cursor.lnum = lnum; + check_cursor_lnum(); + beginline(BL_SOL | BL_FIX); + } xfree(ptr); } break; @@ -2278,6 +2317,9 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } if (hnc) { // add next_curwin size next_curwin_size -= (int)p_wiw - (m - n); + if (next_curwin_size < 0) { + next_curwin_size = 0; + } new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -4035,7 +4077,7 @@ static tabpage_T *alloc_tabpage(void) static int last_tp_handle = 0; tabpage_T *tp = xcalloc(1, sizeof(tabpage_T)); tp->handle = ++last_tp_handle; - pmap_put(handle_T)(&tabpage_handles, tp->handle, tp); + pmap_put(int)(&tabpage_handles, tp->handle, tp); // Init t: variables. tp->tp_vars = tv_dict_alloc(); @@ -4048,7 +4090,7 @@ static tabpage_T *alloc_tabpage(void) void free_tabpage(tabpage_T *tp) { - pmap_del(handle_T)(&tabpage_handles, tp->handle); + pmap_del(int)(&tabpage_handles, tp->handle, NULL); diff_clear(tp); for (int idx = 0; idx < SNAP_COUNT; idx++) { clear_snapshot(tp, idx); @@ -5020,7 +5062,7 @@ static win_T *win_alloc(win_T *after, bool hidden) win_T *new_wp = xcalloc(1, sizeof(win_T)); new_wp->handle = ++last_win_id; - pmap_put(handle_T)(&window_handles, new_wp->handle, new_wp); + pmap_put(int)(&window_handles, new_wp->handle, new_wp); grid_assign_handle(&new_wp->w_grid_alloc); @@ -5082,7 +5124,7 @@ void free_wininfo(wininfo_T *wip, buf_T *bp) /// @param tp tab page "win" is in, NULL for current static void win_free(win_T *wp, tabpage_T *tp) { - pmap_del(handle_T)(&window_handles, wp->handle); + pmap_del(int)(&window_handles, wp->handle, NULL); clearFolding(wp); // reduce the reference count to the argument list. @@ -6660,7 +6702,8 @@ static int win_border_width(win_T *wp) /// Set the width of a window. void win_new_width(win_T *wp, int width) { - wp->w_width = width; + // Should we give an error if width < 0? + wp->w_width = width < 0 ? 0 : width; wp->w_pos_changed = true; win_set_inner_size(wp, true); } diff --git a/src/nvim/window.h b/src/nvim/window.h index 4ab2bea60a..9201800d53 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -4,13 +4,9 @@ #include <stdbool.h> #include <stddef.h> -#include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/macros.h" -#include "nvim/mark.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" -#include "nvim/vim.h" +#include "nvim/option_defs.h" // Values for file_name_in_line() #define FNAME_MESS 1 // give error message diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index 5934c088df..32cf173741 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.76.0_f +# Uncrustify-0.77.1_f # # General options @@ -195,6 +195,11 @@ sp_before_ptr_star = force # ignore/add/remove/force/not_defined # variable name. If set to ignore, sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined +# Add or remove space between a qualifier and a pointer star '*' that isn't +# followed by a variable name, as in '(char const *)'. If set to ignore, +# sp_before_ptr_star is used instead. +sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined + # Add or remove space between pointer stars '*', as in 'int ***a;'. sp_between_ptr_star = ignore # ignore/add/remove/force/not_defined @@ -232,13 +237,24 @@ sp_ptr_star_func_type = ignore # ignore/add/remove/force/not_defined sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined # Add or remove space before a pointer star '*', if followed by a function -# prototype or function definition. +# prototype or function definition. If set to ignore, sp_before_ptr_star is +# used instead. sp_before_ptr_star_func = ignore # ignore/add/remove/force/not_defined +# Add or remove space between a qualifier and a pointer star '*' followed by +# the name of the function in a function prototype or definition, as in +# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead. +sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined + # Add or remove space before a pointer star '*' in the trailing return of a # function prototype or function definition. sp_before_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +# Add or remove space between a qualifier and a pointer star '*' in the +# trailing return of a function prototype or function definition, as in +# 'auto foo() -> char const *'. +sp_qualifier_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined + # Add or remove space before a reference sign '&'. sp_before_byref = ignore # ignore/add/remove/force/not_defined @@ -628,6 +644,16 @@ sp_inside_fparens = remove # ignore/add/remove/force/not_defined # Add or remove space inside function '(' and ')'. sp_inside_fparen = remove # ignore/add/remove/force/not_defined +# Add or remove space inside user functor '(' and ')'. +sp_func_call_user_inside_rparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside empty functor '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_rparens = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside functor '(' and ')'. +sp_inside_rparen = ignore # ignore/add/remove/force/not_defined + # Add or remove space inside the first parentheses in a function type, as in # 'void (*x)(...)'. sp_inside_tparen = remove # ignore/add/remove/force/not_defined @@ -967,6 +993,14 @@ sp_extern_paren = ignore # ignore/add/remove/force/not_defined # Add or remove space after the opening of a C++ comment, as in '// <here> A'. sp_cmt_cpp_start = ignore # ignore/add/remove/force/not_defined +# remove space after the '//' and the pvs command '-V1234', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_pvs = false # true/false + +# remove space after the '//' and the command 'lint', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_lint = false # true/false + # Add or remove space in a C++ region marker comment, as in '// <here> BEGIN'. # A region marker is defined as a comment which is not preceded by other text # (i.e. the comment is the first non-whitespace on the line), and which starts @@ -2833,9 +2867,16 @@ align_single_line_brace_gap = 0 # unsigned number # 0: Don't align (default). align_oc_msg_spec_span = 0 # unsigned number -# Whether to align macros wrapped with a backslash and a newline. This will -# not work right if the macro contains a multi-line comment. -align_nl_cont = false # true/false +# Whether and how to align backslashes that split a macro onto multiple lines. +# This will not work right if the macro contains a multi-line comment. +# +# 0: Do nothing (default) +# 1: Align the backslashes in the column at the end of the longest line +# 2: Align with the backslash that is farthest to the left, or, if that +# backslash is farther left than the end of the longest line, at the end of +# the longest line +# 3: Align with the backslash that is farthest to the right +align_nl_cont = 0 # unsigned number # Whether to align macro functions and variables together. align_pp_define_together = false # true/false @@ -3146,6 +3187,12 @@ mod_remove_extra_semicolon = true # true/false # Whether to remove duplicate include. mod_remove_duplicate_include = true # true/false +# the following options (mod_XX_closebrace_comment) use different comment, +# depending of the setting of the next option. +# false: Use the c comment (default) +# true : Use the cpp comment +mod_add_force_c_closebrace_comment = false # true/false + # If a function body exceeds the specified number of newlines and doesn't have # a comment after the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # unsigned number |