diff options
Diffstat (limited to 'src')
39 files changed, 1113 insertions, 643 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 0d0fba265b..37f3aa1a23 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -82,10 +82,10 @@ set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz) set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive") set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErrors.cmake") -file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) -file(GLOB API_HEADERS api/*.h) +glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt) +glob_wrapper(API_HEADERS api/*.h) list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h) -file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) +glob_wrapper(MSGPACK_RPC_HEADERS msgpack_rpc/*.h) include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) @@ -97,10 +97,10 @@ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}) file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) -file(GLOB NVIM_SOURCES *.c) -file(GLOB NVIM_HEADERS *.h) -file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) -file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) +glob_wrapper(NVIM_SOURCES *.c) +glob_wrapper(NVIM_HEADERS *.h) +glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) +glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) foreach(subdir os @@ -120,13 +120,13 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) - file(GLOB sources ${subdir}/*.c) - file(GLOB headers ${subdir}/*.h) + glob_wrapper(sources ${subdir}/*.c) + glob_wrapper(headers ${subdir}/*.h) list(APPEND NVIM_SOURCES ${sources}) list(APPEND NVIM_HEADERS ${headers}) endforeach() -file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) +glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from # build to build. @@ -805,7 +805,7 @@ add_custom_target(lintc DEPENDS ${LINT_TARGETS}) def_cmd_target(lintuncrustify ${UNCRUSTIFY_PRG} UNCRUSTIFY_PRG false) if(UNCRUSTIFY_PRG) - add_custom_command(OUTPUT lintuncrustify-cmd APPEND + add_custom_command(TARGET lintuncrustify COMMAND ${CMAKE_COMMAND} -D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG} -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 61d4becd32..19ce25f676 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -21,17 +21,73 @@ # include "api/options.c.generated.h" #endif +static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from, + Error *err) +{ + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + *scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + *scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + return FAIL; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + return FAIL; + } + + *opt_type = SREQ_GLOBAL; + + if (opts->win.type == kObjectTypeInteger) { + *opt_type = SREQ_WIN; + *from = find_window_by_handle((int)opts->win.data.integer, err); + if (ERROR_SET(err)) { + return FAIL; + } + } else if (HAS_KEY(opts->win)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: win"); + return FAIL; + } + + if (opts->buf.type == kObjectTypeInteger) { + *scope = OPT_LOCAL; + *opt_type = SREQ_BUF; + *from = find_buffer_by_handle((int)opts->buf.data.integer, err); + if (ERROR_SET(err)) { + return FAIL; + } + } else if (HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: buf"); + return FAIL; + } + + if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together"); + return FAIL; + } + + if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together"); + return FAIL; + } + + return OK; +} + /// Gets the value of an option. The behavior of this function matches that of /// |:set|: the local value of an option is returned if it exists; otherwise, /// the global value is returned. Local values always correspond to the current -/// buffer or window. To get a buffer-local or window-local option for a -/// specific buffer or window, use |nvim_buf_get_option()| or -/// |nvim_win_get_option()|. +/// buffer or window, unless "buf" or "win" is set in {opts}. /// /// @param name Option name /// @param opts Optional parameters -/// - scope: One of 'global' or 'local'. Analogous to +/// - scope: One of "global" or "local". Analogous to /// |:setglobal| and |:setlocal|, respectively. +/// - win: |window-ID|. Used for getting window local options. +/// - buf: Buffer number. Used for getting buffer local options. +/// Implies {scope} is "local". /// @param[out] err Error details, if any /// @return Option value Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) @@ -40,23 +96,21 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) Object rv = OBJECT_INIT; int scope = 0; - if (opts->scope.type == kObjectTypeString) { - if (!strcmp(opts->scope.data.string.data, "local")) { - scope = OPT_LOCAL; - } else if (!strcmp(opts->scope.data.string.data, "global")) { - scope = OPT_GLOBAL; - } else { - api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); - goto end; - } - } else if (HAS_KEY(opts->scope)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); - goto end; + int opt_type = SREQ_GLOBAL; + void *from = NULL; + if (!validate_option_value_args(opts, &scope, &opt_type, &from, err)) { + return rv; } long numval = 0; char *stringval = NULL; - switch (get_option_value(name.data, &numval, &stringval, scope)) { + int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from, + true, err); + if (ERROR_SET(err)) { + return rv; + } + + switch (result) { case 0: rv = STRING_OBJ(cstr_as_string(stringval)); break; @@ -78,10 +132,9 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) break; default: api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data); - goto end; + return rv; } -end: return rv; } @@ -103,47 +156,9 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error FUNC_API_SINCE(9) { int scope = 0; - if (opts->scope.type == kObjectTypeString) { - if (!strcmp(opts->scope.data.string.data, "local")) { - scope = OPT_LOCAL; - } else if (!strcmp(opts->scope.data.string.data, "global")) { - scope = OPT_GLOBAL; - } else { - api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); - return; - } - } else if (HAS_KEY(opts->scope)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); - return; - } - int opt_type = SREQ_GLOBAL; void *to = NULL; - - if (opts->win.type == kObjectTypeInteger) { - opt_type = SREQ_WIN; - to = find_window_by_handle((int)opts->win.data.integer, err); - } else if (HAS_KEY(opts->win)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: win"); - return; - } - - if (opts->buf.type == kObjectTypeInteger) { - scope = OPT_LOCAL; - opt_type = SREQ_BUF; - to = find_buffer_by_handle((int)opts->buf.data.integer, err); - } else if (HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: buf"); - return; - } - - if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together"); - return; - } - - if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together"); + if (!validate_option_value_args(opts, &scope, &opt_type, &to, err)) { return; } @@ -168,7 +183,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error return; } - set_option_value_for(name.data, numval, stringval, scope, opt_type, to, err); + access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err); } /// Gets the option information for all options. @@ -405,7 +420,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object } } - int numval = 0; + long numval = 0; char *stringval = NULL; if (flags & SOPT_BOOL) { @@ -450,16 +465,34 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object ? 0 : (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); + access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err); }); } -void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type, - void *from, Error *err) +static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get, + Error *err) +{ + if (get) { + return get_option_value(key, numval, stringval, opt_flags); + } else { + char *errmsg; + if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) { + if (try_end(err)) { + return 0; + } + + api_set_error(err, kErrorTypeException, "%s", errmsg); + } + return 0; + } +} + +static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, + int opt_type, void *from, bool get, Error *err) { switchwin_T switchwin; aco_save_T aco; + int result = 0; try_start(); switch (opt_type) { @@ -468,42 +501,31 @@ void set_option_value_for(char *key, long numval, char *stringval, int opt_flags == FAIL) { restore_win_noblock(&switchwin, true); if (try_end(err)) { - return; + return result; } api_set_error(err, kErrorTypeException, "Problem while switching windows"); - return; + return result; } - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); restore_win_noblock(&switchwin, true); break; case SREQ_BUF: aucmd_prepbuf(&aco, (buf_T *)from); - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); aucmd_restbuf(&aco); break; case SREQ_GLOBAL: - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); break; } if (ERROR_SET(err)) { - return; + return result; } try_end(err); -} -static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err) -{ - char *errmsg; - - if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { - if (try_end(err)) { - return; - } - - api_set_error(err, kErrorTypeException, "%s", errmsg); - } + return result; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 396fab721d..b1e0dd364c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -6,9 +6,10 @@ #include <string.h> #include "nvim/func_attr.h" +#include "nvim/lib/kvec.h" #include "nvim/types.h" -#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL } +#define ARRAY_DICT_INIT KV_INITIAL_VALUE #define STRING_INIT { .data = NULL, .size = 0 } #define OBJECT_INIT { .type = kObjectTypeNil } #define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL } @@ -84,18 +85,10 @@ REMOTE_TYPE(Window); REMOTE_TYPE(Tabpage); typedef struct object Object; - -typedef struct { - Object *items; - size_t size, capacity; -} Array; +typedef kvec_t(Object) Array; typedef struct key_value_pair KeyValuePair; - -typedef struct { - KeyValuePair *items; - size_t size, capacity; -} Dictionary; +typedef kvec_t(KeyValuePair) Dictionary; typedef enum { kObjectTypeNil = 0, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff6a4c37e8..9cadef0385 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -669,6 +669,29 @@ void api_free_string(String value) xfree(value.data); } +Array arena_array(Arena *arena, size_t max_size) +{ + Array arr = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, arr, max_size); + return arr; +} + +Dictionary arena_dict(Arena *arena, size_t max_size) +{ + Dictionary dict = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, dict, max_size); + return dict; +} + +String arena_string(Arena *arena, String str) +{ + if (str.size) { + return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size); + } else { + return (String)STRING_INIT; + } +} + void api_free_object(Object value) { switch (value.type) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8423db4970..a4348d8b44 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -63,17 +63,31 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_C(dict, k, v) \ + kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) + #define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); #define ADD(array, item) \ kv_push(array, item) +#define ADD_C(array, item) \ + kv_push_c(array, item) + #define FIXED_TEMP_ARRAY(name, fixsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[fixsize]; \ name.size = fixsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_ARRAY(name, maxsize) \ + Array name = ARRAY_DICT_INIT; \ + Object name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + +#define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) + #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) /// Create a new String instance, putting data in allocated memory diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index dc04eedebc..54ce838b9b 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -9,11 +9,13 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" +#include "nvim/channel.h" #include "nvim/cursor_shape.h" #include "nvim/highlight.h" #include "nvim/map.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" @@ -21,14 +23,32 @@ #include "nvim/vim.h" #include "nvim/window.h" -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/ui.c.generated.h" -# include "ui_events_remote.generated.h" -#endif - typedef struct { uint64_t channel_id; - Array buffer; + +#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. + /// guranteed size available for each new event (so packing of simple events + /// and the header of grid_line will never fail) +#define EVENT_BUF_SIZE 256 + char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data + char *buf_wptr; ///< write head of buffer + const char *cur_event; ///< name of current event (might get multiple arglists) + Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) + + // state for write_cb, while packing a single arglist to msgpack. This + // might fail due to buffer overflow. + size_t pack_totlen; + bool buf_overflow; + char *temp_buf; + + // We start packing the two outermost msgpack arrays before knowing the total + // number of elements. Thus track the location where array size will need + // to be written in the msgpack buffer, once the specifc array is finished. + char *nevents_pos; + char *ncalls_pos; + uint32_t nevents; ///< number of distinct events (top-level args to "redraw" + uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!) + bool flushed_events; ///< events where sent to client without "flush" event int hl_id; // Current highlight for legacy put event. Integer cursor_row, cursor_col; // Intended visible cursor position. @@ -38,8 +58,76 @@ typedef struct { bool wildmenu_active; } UIData; +#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/ui.c.generated.h" +# include "ui_events_remote.generated.h" +#endif + static PMap(uint64_t) connected_uis = MAP_INIT; +#define mpack_w(b, byte) *(*b)++ = (char)(byte); +static void mpack_w2(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_w4(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_uint(char **buf, uint32_t val) +{ + if (val > 0xffff) { + mpack_w(buf, 0xce); + mpack_w4(buf, val); + } else if (val > 0xff) { + mpack_w(buf, 0xcd); + mpack_w2(buf, val); + } else if (val > 0x7f) { + mpack_w(buf, 0xcc); + mpack_w(buf, val); + } else { + mpack_w(buf, val); + } +} + +static void mpack_array(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x90 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xdc); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdd); + mpack_w4(buf, len); + } +} + +static char *mpack_array_dyn16(char **buf) +{ + mpack_w(buf, 0xdc); + char *pos = *buf; + mpack_w2(buf, 0xFFEF); + return pos; +} + +static void mpack_str(char **buf, const char *str) +{ + assert(sizeof(schar_T) - 1 < 0x20); + size_t len = STRLEN(str); + mpack_w(buf, 0xa0 | len); + memcpy(*buf, str, len); + *buf += len; +} + void remote_ui_disconnect(uint64_t channel_id) { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); @@ -47,9 +135,9 @@ void remote_ui_disconnect(uint64_t channel_id) return; } UIData *data = ui->data; - api_free_array(data->buffer); // Destroy pending screen updates. + kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id); - xfree(ui->data); + xfree(data); ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui, channel_id); xfree(ui); @@ -159,10 +247,19 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona UIData *data = xmalloc(sizeof(UIData)); data->channel_id = channel_id; - data->buffer = (Array)ARRAY_DICT_INIT; + data->cur_event = NULL; data->hl_id = 0; data->client_col = -1; + data->nevents_pos = NULL; + data->nevents = 0; + data->flushed_events = false; + data->ncalls_pos = NULL; + data->ncalls = 0; + data->buf_wptr = data->buf; + data->temp_buf = NULL; data->wildmenu_active = false; + data->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(data->call_buf, 16); ui->data = data; pmap_put(uint64_t)(&connected_uis, channel_id, ui); @@ -433,34 +530,128 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa ui->pum_pos = true; } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +static void flush_event(UIData *data) +{ + if (data->cur_event) { + mpack_w2(&data->ncalls_pos, data->ncalls); + data->cur_event = NULL; + } + if (!data->nevents_pos) { + assert(BUF_POS(data) == 0); + char **buf = &data->buf_wptr; + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + data->nevents_pos = mpack_array_dyn16(buf); + } +} + +static inline int write_cb(void *vdata, const char *buf, size_t len) +{ + UIData *data = (UIData *)vdata; + if (!buf) { + return 0; + } + + data->pack_totlen += len; + if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { + data->buf_overflow = true; + return 0; + } + + memcpy(data->buf_wptr, buf, len); + data->buf_wptr += len; + + return 0; +} + +static bool prepare_call(UI *ui, const char *name) { - Array call = ARRAY_DICT_INIT; UIData *data = ui->data; - // To optimize data transfer(especially for "put"), we bundle adjacent + if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + remote_ui_flush_buf(ui); + } + + // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (kv_size(data->buffer)) { - call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array; - } - if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) { - call = (Array)ARRAY_DICT_INIT; - ADD(data->buffer, ARRAY_OBJ(call)); - ADD(call, STRING_OBJ(cstr_to_string(name))); + if (!data->cur_event || !strequal(data->cur_event, name)) { + flush_event(data); + data->cur_event = name; + char **buf = &data->buf_wptr; + data->ncalls_pos = mpack_array_dyn16(buf); + mpack_str(buf, name); + data->nevents++; + data->ncalls = 1; + return true; } - ADD(call, ARRAY_OBJ(args)); - kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; + return false; +} + +/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). +static void push_call(UI *ui, const char *name, Array args) +{ + UIData *data = ui->data; + bool pending = data->nevents_pos; + char *buf_pos_save = data->buf_wptr; + + bool new_event = prepare_call(ui, name); + + msgpack_packer pac; + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_packer_init(&pac, data, write_cb); + msgpack_rpc_from_array(args, &pac); + if (data->buf_overflow) { + data->buf_wptr = buf_pos_save; + if (new_event) { + data->cur_event = NULL; + data->nevents--; + } + if (pending) { + remote_ui_flush_buf(ui); + } + + if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { + // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) + data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); + data->buf_wptr = data->temp_buf; + char **buf = &data->buf_wptr; + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + mpack_array(buf, 1); + mpack_array(buf, 2); + mpack_str(buf, name); + } else { + prepare_call(ui, name); + } + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_rpc_from_array(args, &pac); + + if (data->temp_buf) { + size_t size = (size_t)(data->buf_wptr - data->temp_buf); + WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->temp_buf = NULL; + data->buf_wptr = data->buf; + data->nevents_pos = NULL; + } + } + data->ncalls++; } static void remote_ui_grid_clear(UI *ui, Integer grid) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear"; push_call(ui, name, args); @@ -468,12 +659,13 @@ static void remote_ui_grid_clear(UI *ui, Integer grid) static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); + ADD_C(args, INTEGER_OBJ(width)); + ADD_C(args, INTEGER_OBJ(height)); const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize"; push_call(ui, name, args); } @@ -481,35 +673,36 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { + UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - ADD(args, INTEGER_OBJ(rows)); - ADD(args, INTEGER_OBJ(cols)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right)); + ADD_C(args, INTEGER_OBJ(rows)); + ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot - 1)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right - 1)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot - 1)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rows)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->height - 1)); - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->width - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->height - 1)); + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->width - 1)); push_call(ui, "set_scroll_region", args); } } @@ -520,26 +713,27 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rgb_fg)); - ADD(args, INTEGER_OBJ(rgb_bg)); - ADD(args, INTEGER_OBJ(rgb_sp)); - ADD(args, INTEGER_OBJ(cterm_fg)); - ADD(args, INTEGER_OBJ(cterm_bg)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rgb_fg)); + ADD_C(args, INTEGER_OBJ(rgb_bg)); + ADD_C(args, INTEGER_OBJ(rgb_sp)); + ADD_C(args, INTEGER_OBJ(cterm_fg)); + ADD_C(args, INTEGER_OBJ(cterm_bg)); push_call(ui, "default_colors_set", args); // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } @@ -550,25 +744,29 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt if (!ui->ui_ext[kUILinegrid]) { return; } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(id)); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(id)); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { - ADD(args, ARRAY_OBJ(copy_array(info))); + ADD_C(args, ARRAY_OBJ(info)); } else { - ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); + ADD_C(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); } push_call(ui, "hl_attr_define", args); + // TODO(bfredl): could be elided + api_free_dictionary(kv_A(args, 1).data.dictionary); + api_free_dictionary(kv_A(args, 2).data.dictionary); } static void remote_ui_highlight_set(UI *ui, int id) { - Array args = ARRAY_DICT_INIT; UIData *data = ui->data; + Array args = data->call_buf; if (data->hl_id == id) { return; @@ -576,18 +774,20 @@ static void remote_ui_highlight_set(UI *ui, int id) data->hl_id = id; Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - ADD(args, DICTIONARY_OBJ(hl)); + ADD_C(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); + api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { UIData *data = ui->data; @@ -606,9 +806,9 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) } data->client_row = row; data->client_col = col; - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } @@ -616,8 +816,8 @@ static void remote_ui_put(UI *ui, const char *cell) { UIData *data = ui->data; data->client_col++; - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(cell))); + Array args = data->call_buf; + ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell))); push_call(ui, "put", args); } @@ -627,41 +827,64 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc { UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(startcol)); - Array cells = ARRAY_DICT_INIT; - int repeat = 0; + prepare_call(ui, "grid_line"); + data->ncalls++; + + char **buf = &data->buf_wptr; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol); + char *lenpos = mpack_array_dyn16(buf); + + uint32_t repeat = 0; size_t ncells = (size_t)(endcol - startcol); int last_hl = -1; + uint32_t nelem = 0; for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || STRCMP(chunk[i], chunk[i + 1])) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i]))); - if (attrs[i] != last_hl || repeat > 1) { - ADD(cell, INTEGER_OBJ(attrs[i])); - last_hl = attrs[i]; + if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) { + // close to overflowing the redraw buffer. finish this event, + // flush, and start a new "grid_line" event at the current position. + // For simplicity leave place for the final "clear" element + // as well, hence the factor of 2 in the check. + mpack_w2(&lenpos, nelem); + remote_ui_flush_buf(ui); + + prepare_call(ui, "grid_line"); + data->ncalls++; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol + (uint32_t)i - repeat + 1); + lenpos = mpack_array_dyn16(buf); + nelem = 0; + last_hl = -1; } - if (repeat > 1) { - ADD(cell, INTEGER_OBJ(repeat)); + uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); + nelem++; + mpack_array(buf, csize); + mpack_str(buf, (const char *)chunk[i]); + if (csize >= 2) { + mpack_uint(buf, (uint32_t)attrs[i]); + if (csize >= 3) { + mpack_uint(buf, repeat); + } } - ADD(cells, ARRAY_OBJ(cell)); + last_hl = attrs[i]; repeat = 0; } } if (endcol < clearcol) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string(" "))); - ADD(cell, INTEGER_OBJ(clearattr)); - ADD(cell, INTEGER_OBJ(clearcol - endcol)); - ADD(cells, ARRAY_OBJ(cell)); + nelem++; + mpack_array(buf, 3); + mpack_str(buf, " "); + mpack_uint(buf, (uint32_t)clearattr); + mpack_uint(buf, (uint32_t)(clearcol - endcol)); } - ADD(args, ARRAY_OBJ(cells)); - - push_call(ui, "grid_line", args); + mpack_w2(&lenpos, nelem); } else { for (int i = 0; i < endcol - startcol; i++) { remote_ui_cursor_goto(ui, row, startcol + i); @@ -688,16 +911,47 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc } } +/// Flush the internal packing buffer to the client. +/// +/// This might happen multiple times before the actual ui_flush, if the +/// total redraw size is large! +static void remote_ui_flush_buf(UI *ui) +{ + UIData *data = ui->data; + if (!data->nevents_pos) { + return; + } + if (data->cur_event) { + flush_event(data); + } + mpack_w2(&data->nevents_pos, data->nevents); + data->nevents = 0; + data->nevents_pos = NULL; + + // TODO(bfredl): elide copy by a length one free-list like the arena + size_t size = BUF_POS(data); + WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->buf_wptr = data->buf; + // we have sent events to the client, but possibly not yet the final "flush" + // event. + data->flushed_events = true; +} + +/// An intentional flush (vsync) when Nvim is finished redrawing the screen +/// +/// Clients can know this happened by a final "flush" event at the end of the +/// "redraw" batch. static void remote_ui_flush(UI *ui) { UIData *data = ui->data; - if (data->buffer.size > 0) { + if (data->nevents > 0 || data->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - rpc_send_event(data->channel_id, "redraw", data->buffer); - data->buffer = (Array)ARRAY_DICT_INIT; + remote_ui_flush_buf(ui); + data->flushed_events = false; } } @@ -732,7 +986,7 @@ static Array translate_firstarg(UI *ui, Array args) return new_args; } -static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) +static void remote_ui_event(UI *ui, char *name, Array args) { UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { @@ -741,21 +995,24 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) if (strequal(name, "cmdline_show")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array block = args.items[0].data.array; Array new_block = ARRAY_DICT_INIT; for (size_t i = 0; i < block.size; i++) { ADD(new_block, ARRAY_OBJ(translate_contents(ui, block.items[i].data.array))); } - ADD(new_args, ARRAY_OBJ(new_block)); + ADD_C(new_args, ARRAY_OBJ(new_block)); push_call(ui, name, new_args); + api_free_array(new_block); return; } else if (strequal(name, "cmdline_block_append")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } } @@ -766,18 +1023,19 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) data->wildmenu_active = (args.items[4].data.integer == -1) || !ui->ui_ext[kUIPopupmenu]; if (data->wildmenu_active) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array items = args.items[0].data.array; Array new_items = ARRAY_DICT_INIT; for (size_t i = 0; i < items.size; i++) { ADD(new_items, copy_object(items.items[i].data.array.items[0])); } - ADD(new_args, ARRAY_OBJ(new_items)); + ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); + api_free_array(new_items); if (args.items[1].data.integer != -1) { - Array new_args2 = ARRAY_DICT_INIT; - ADD(new_args2, args.items[1]); - push_call(ui, "wildmenu_select", new_args); + Array new_args2 = data->call_buf; + ADD_C(new_args2, args.items[1]); + push_call(ui, "wildmenu_select", new_args2); } return; } @@ -792,18 +1050,7 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) } } - Array my_args = ARRAY_DICT_INIT; - // Objects are currently single-reference - // make a copy, but only if necessary - if (*args_consumed) { - for (size_t i = 0; i < args.size; i++) { - ADD(my_args, copy_object(args.items[i])); - } - } else { - my_args = args; - *args_consumed = true; - } - push_call(ui, name, my_args); + push_call(ui, name, args); } static void remote_ui_inspect(UI *ui, Dictionary *info) diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 0030f9edf7..8b7e01e1c3 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -99,7 +99,7 @@ void raw_line(Integer grid, Integer row, Integer startcol, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL; -void event(char *name, Array args, bool *args_consumed) +void event(char *name, Array args) FUNC_API_NOEXPORT; void win_pos(Integer grid, Window win, Integer startrow, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3a24f2b405..b7df1398f5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1293,7 +1293,8 @@ void nvim_unsubscribe(uint64_t channel_id, String event) Integer nvim_get_color_by_name(String name) FUNC_API_SINCE(1) { - return name_to_color(name.data); + int dummy; + return name_to_color(name.data, &dummy); } /// Returns a map of color names and RGB values. diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 0248c25dfa..c7c7e6ae58 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -43,43 +43,45 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into an Array of Dictionaries +/// @param arena initialized arena where memory will be alocated +/// /// @return Array of the form {[ "cursor_shape": ... ], ...} -Array mode_style_array(void) +Array mode_style_array(Arena *arena) { - Array all = ARRAY_DICT_INIT; + Array all = arena_array(arena, SHAPE_IDX_COUNT); for (int i = 0; i < SHAPE_IDX_COUNT; i++) { - Dictionary dic = ARRAY_DICT_INIT; 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))); if (cur->used_for & SHAPE_MOUSE) { - PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); + PUT_C(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); } if (cur->used_for & SHAPE_CURSOR) { String shape_str; switch (cur->shape) { case SHAPE_BLOCK: - shape_str = cstr_to_string("block"); break; + shape_str = cstr_as_string("block"); break; case SHAPE_VER: - shape_str = cstr_to_string("vertical"); break; + shape_str = cstr_as_string("vertical"); break; case SHAPE_HOR: - shape_str = cstr_to_string("horizontal"); break; + shape_str = cstr_as_string("horizontal"); break; default: - shape_str = cstr_to_string("unknown"); + shape_str = cstr_as_string("unknown"); } - PUT(dic, "cursor_shape", STRING_OBJ(shape_str)); - PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); - PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); - PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); - PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); - PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); - PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); - PUT(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0)); - PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0)); + PUT_C(dic, "cursor_shape", STRING_OBJ(shape_str)); + PUT_C(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); + PUT_C(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); + PUT_C(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); + PUT_C(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); + PUT_C(dic, "hl_id", INTEGER_OBJ(cur->id)); + PUT_C(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); + PUT_C(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0)); + PUT_C(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0)); } - PUT(dic, "name", STRING_OBJ(cstr_to_string(cur->full_name))); - PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); - ADD(all, DICTIONARY_OBJ(dic)); + ADD_C(all, DICTIONARY_OBJ(dic)); } return all; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index b1a22b3294..e7c76fe38e 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -69,7 +69,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { if (row2 >= row1) { - if (!decor || decor->hl_id || decor_has_sign(decor)) { + if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal) { redraw_buf_range_later(buf, row1 + 1, row2 + 1); } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 28b1a7580d..49db5c3716 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2826,10 +2826,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) curwin->w_cursor.lnum = eap->line2; // Don't leave the cursor on an illegal line or column, but do - // accept zero as address, so 0;/PATTERN/ works correctly. + // accept zero as address, so 0;/PATTERN/ works correctly + // (where zero usually means to use the first line). // Check the cursor position before returning. if (eap->line2 > 0) { check_cursor(); + } else { + check_cursor_col(); } need_check_cursor = true; } @@ -7272,7 +7275,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3065dc462b..211eeff45e 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3309,45 +3309,53 @@ draw_cmdline_no_arabicshape: static void ui_ext_cmdline_show(CmdlineInfo *line) { + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); Array content = ARRAY_DICT_INIT; if (cmdline_star) { + content = arena_array(&arena, 1); size_t len = 0; for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } - char *buf = xmallocz(len); + char *buf = arena_alloc(&arena, len, false); memset(buf, '*', len); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len))); + ADD_C(content, ARRAY_OBJ(item)); } else if (kv_size(line->last_colors.colors)) { + content = arena_array(&arena, kv_size(line->last_colors.colors)); for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(chunk.attr)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(chunk.attr)); assert(chunk.end >= chunk.start); - ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, - (size_t)(chunk.end - chunk.start)))); - ADD(content, ARRAY_OBJ(item)); + ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start, + (size_t)(chunk.end - chunk.start)))); + ADD_C(content, ARRAY_OBJ(item)); } } else { - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff)))); + content = arena_array(&arena, 1); + ADD_C(content, ARRAY_OBJ(item)); } + char charbuf[2] = { (char)line->cmdfirstc, 0 }; ui_call_cmdline_show(content, line->cmdpos, - cchar_to_string((char)line->cmdfirstc), - cstr_to_string((char *)(line->cmdprompt)), + cstr_as_string(charbuf), + cstr_as_string((char *)(line->cmdprompt)), line->cmdindent, line->level); if (line->special_char) { - ui_call_cmdline_special_char(cchar_to_string(line->special_char), + charbuf[0] = line->special_char; + ui_call_cmdline_special_char(cstr_as_string(charbuf), line->special_shift, line->level); } + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } void ui_ext_cmdline_block_append(size_t indent, const char *line) @@ -3363,9 +3371,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); if (cmdline_block.size > 1) { - ui_call_cmdline_block_append(copy_array(content)); + ui_call_cmdline_block_append(content); } else { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } } @@ -3385,7 +3393,7 @@ void cmdline_screen_cleared(void) } if (cmdline_block.size) { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } int prev_level = ccline.level - 1; @@ -3442,7 +3450,8 @@ void putcmdline(char c, int shift) } msg_no_more = false; } else if (ccline.redraw_state != kCmdRedrawAll) { - ui_call_cmdline_special_char(cchar_to_string(c), shift, + char charbuf[2] = { c, 0 }; + ui_call_cmdline_special_char(cstr_as_string(charbuf), shift, ccline.level); } cursorcmd(); diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index b9e0d18f20..1639f72990 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -68,6 +68,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col if (decor) { if (kv_size(decor->virt_text) || kv_size(decor->virt_lines) + || decor->conceal || decor_has_sign(decor) || decor->ui_watched) { decor_full = true; diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 99dfac05e8..93bbaab74c 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -36,20 +36,12 @@ local function write_signature(output, ev, prefix, notype) output:write(')') end -local function write_arglist(output, ev, need_copy) - output:write(' Array args = ARRAY_DICT_INIT;\n') +local function write_arglist(output, ev) for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = string.upper(param[1]) - local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT") - output:write(' ADD(args, ') - if do_copy then - output:write('copy_object(') - end + output:write(' ADD_C(args, ') output:write(kind..'_OBJ('..param[2]..')') - if do_copy then - output:write(')') - end output:write(');\n') end end @@ -119,7 +111,9 @@ for i = 1, #events do remote_output:write('static void remote_ui_'..ev.name) write_signature(remote_output, ev, 'UI *ui') remote_output:write('\n{\n') - write_arglist(remote_output, ev, true) + remote_output:write(' UIData *data = ui->data;\n') + remote_output:write(' Array args = data->call_buf;\n') + write_arglist(remote_output, ev) remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') remote_output:write('}\n\n') end @@ -186,9 +180,10 @@ for i = 1, #events do write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then - write_arglist(call_output, ev, false) + call_output:write(' Array args = call_buf;\n') + write_arglist(call_output, ev) call_output:write(' UI_LOG('..ev.name..');\n') - call_output:write(' ui_event("'..ev.name..'", args);\n') + call_output:write(' ui_call_event("'..ev.name..'", args);\n') elseif ev.compositor_impl then call_output:write(' UI_CALL') write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7f783fd72f..57d25e5c45 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2122,6 +2122,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) const bool save_may_garbage_collect = may_garbage_collect; const int save_cursor_row = ui_current_row(); const int save_cursor_col = ui_current_col(); + const handle_T save_cursor_grid = ui_cursor_grid(); const int prev_did_emsg = did_emsg; vgetc_busy = 0; @@ -2135,7 +2136,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // The mapping may do anything, but we expect it to take care of // redrawing. Do put the cursor back where it was. - ui_cursor_goto(save_cursor_row, save_cursor_col); + ui_grid_cursor_goto(save_cursor_grid, save_cursor_row, save_cursor_col); ui_flush(); // If an error was displayed and the expression returns an empty diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 00a3c3cdab..aeccac060a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -392,7 +392,7 @@ static uint32_t prt_get_color(int hl_id, int modec) const char *color = highlight_color(hl_id, "fg#", 'g'); if (color != NULL) { - RgbValue rgb = name_to_color(color); + RgbValue rgb = name_to_color(color, &colorindex); if (rgb != -1) { return (uint32_t)rgb; } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index ee79958034..229da03cb4 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -962,7 +962,8 @@ int object_to_color(Object val, char *key, bool rgb, Error *err) } int color; if (rgb) { - color = name_to_color(str.data); + int dummy; + color = name_to_color(str.data, &dummy); } else { color = name_to_ctermcolor(str.data); } diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 9d61141e98..93d6e65040 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -30,6 +30,9 @@ // builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; +// arena for object with same lifetime as highlight_ga (aka hl_table) +Arena highlight_arena = ARENA_EMPTY; + Map(cstr_t, int) highlight_unames = MAP_INIT; /// The "term", "cterm" and "gui" arguments can be any combination of the @@ -66,21 +69,25 @@ typedef struct { RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color - char *sg_rgb_fg_name; ///< RGB foreground color name - char *sg_rgb_bg_name; ///< RGB background color name - char *sg_rgb_sp_name; ///< RGB special color name + int sg_rgb_fg_idx; ///< RGB foreground color index + int sg_rgb_bg_idx; ///< RGB background color index + int sg_rgb_sp_idx; ///< RGB special color index int sg_blend; ///< blend level (0-100 inclusive), -1 if unset } HlGroup; +enum { + kColorIdxNone = -1, + kColorIdxHex = -2, + kColorIdxFg = -3, + kColorIdxBg = -4, +}; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight_group.c.generated.h" #endif -static inline HlGroup *HL_TABLE(void) -{ - return ((HlGroup *)((highlight_ga.ga_data))); -} +#define hl_table ((HlGroup *)((highlight_ga.ga_data))) // The default highlight groups. These are compiled-in for fast startup and // they still work when the runtime files can't be found. @@ -464,13 +471,13 @@ int highlight_num_groups(void) /// Returns the name of a highlight group. char_u *highlight_group_name(int id) { - return HL_TABLE()[id].sg_name; + return hl_table[id].sg_name; } /// Returns the ID of the link to a highlight group. int highlight_link_id(int id) { - return HL_TABLE()[id].sg_link; + return hl_table[id].sg_link; } /// Create default links for Nvim* highlight groups used for cmdline coloring @@ -673,7 +680,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) return; } - HlGroup *g = &HL_TABLE()[idx]; + HlGroup *g = &hl_table[idx]; if (link_id > 0) { g->sg_cleared = false; @@ -698,34 +705,21 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_rgb_sp = attrs.rgb_sp_color; struct { - char **dest; RgbValue val; Object name; + int *dest; RgbValue val; Object name; } cattrs[] = { - { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, - { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, - { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { &g->sg_rgb_fg_idx, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_idx, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, { NULL, -1, NIL }, }; - char hex_name[8]; - char *name; - for (int j = 0; cattrs[j].dest; j++) { if (cattrs[j].val < 0) { - XFREE_CLEAR(*cattrs[j].dest); - continue; - } - - if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { - name = cattrs[j].name.data.string.data; + *cattrs[j].dest = kColorIdxNone; + } else if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + name_to_color(cattrs[j].name.data.string.data, cattrs[j].dest); } else { - snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); - name = hex_name; - } - - if (!*cattrs[j].dest - || STRCMP(*cattrs[j].dest, name) != 0) { - xfree(*cattrs[j].dest); - *cattrs[j].dest = xstrdup(name); + *cattrs[j].dest = kColorIdxHex; } } @@ -860,7 +854,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } if (from_id > 0) { - hlgroup = &HL_TABLE()[from_id - 1]; + hlgroup = &hl_table[from_id - 1]; if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { hlgroup->sg_deflink = to_id; hlgroup->sg_deflink_sctx = current_sctx; @@ -931,19 +925,19 @@ void do_highlight(const char *line, const bool forceit, const bool init) } // Make a copy so we can check if any attribute actually changed - item_before = HL_TABLE()[idx]; - is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); + item_before = hl_table[idx]; + is_normal_group = (STRCMP(hl_table[idx].sg_name_u, "NORMAL") == 0); // Clear the highlighting for ":hi clear {group}" and ":hi clear". if (doclear || (forceit && init)) { highlight_clear(idx); if (!doclear) { - HL_TABLE()[idx].sg_set = 0; + hl_table[idx].sg_set = 0; } } - char *key = NULL; - char *arg = NULL; + char key[64]; + char arg[512]; if (!doclear) { while (!ends_excmd((uint8_t)(*linep))) { key_start = linep; @@ -958,15 +952,21 @@ void do_highlight(const char *line, const bool forceit, const bool init) while (*linep && !ascii_iswhite(*linep) && *linep != '=') { linep++; } - xfree(key); - key = (char *)vim_strnsave_up((const char_u *)key_start, - (size_t)(linep - key_start)); + size_t key_len = (size_t)(linep - key_start); + if (key_len > sizeof key - 1) { + semsg(_("E423: Illegal argument")); + error = true; + break; + } + memcpy(key, key_start, key_len); + key[key_len] = NUL; + vim_strup((char_u *)key); linep = (const char *)skipwhite(linep); if (strcmp(key, "NONE") == 0) { - if (!init || HL_TABLE()[idx].sg_set == 0) { + if (!init || hl_table[idx].sg_set == 0) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM + SG_GUI; + hl_table[idx].sg_set |= SG_CTERM + SG_GUI; } highlight_clear(idx); } @@ -1000,8 +1000,14 @@ void do_highlight(const char *line, const bool forceit, const bool init) error = true; break; } - xfree(arg); - arg = xstrndup(arg_start, (size_t)(linep - arg_start)); + size_t arg_len = (size_t)(linep - arg_start); + if (arg_len > sizeof arg - 1) { + semsg(_("E423: Illegal argument")); + error = true; + break; + } + memcpy(arg, arg_start, arg_len); + arg[arg_len] = NUL; if (*linep == '\'') { linep++; @@ -1036,34 +1042,34 @@ void do_highlight(const char *line, const bool forceit, const bool init) break; } if (*key == 'C') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init || !(hl_table[idx].sg_set & SG_CTERM)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; + hl_table[idx].sg_set |= SG_CTERM; } - HL_TABLE()[idx].sg_cterm = attr; - HL_TABLE()[idx].sg_cterm_bold = false; + hl_table[idx].sg_cterm = attr; + hl_table[idx].sg_cterm_bold = false; } } else if (*key == 'G') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - HL_TABLE()[idx].sg_gui = attr; + hl_table[idx].sg_gui = attr; } } } else if (STRCMP(key, "FONT") == 0) { // in non-GUI fonts are simply ignored } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init || !(hl_table[idx].sg_set & SG_CTERM)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; + hl_table[idx].sg_set |= SG_CTERM; } // When setting the foreground color, and previously the "bold" // flag was set for a light color, reset it now - if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = false; + if (key[5] == 'F' && hl_table[idx].sg_cterm_bold) { + hl_table[idx].sg_cterm &= ~HL_BOLD; + hl_table[idx].sg_cterm_bold = false; } if (ascii_isdigit(*arg)) { @@ -1107,21 +1113,21 @@ void do_highlight(const char *line, const bool forceit, const bool init) // set/reset bold attribute to get light foreground // colors (on some terminals, e.g. "linux") if (bold == kTrue) { - HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = true; + hl_table[idx].sg_cterm |= HL_BOLD; + hl_table[idx].sg_cterm_bold = true; } else if (bold == kFalse) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + hl_table[idx].sg_cterm &= ~HL_BOLD; } } // Add one to the argument, to avoid zero. Zero is used for // "NONE", then "color" is -1. if (key[5] == 'F') { - HL_TABLE()[idx].sg_cterm_fg = color + 1; + hl_table[idx].sg_cterm_fg = color + 1; if (is_normal_group) { cterm_normal_fg_color = color + 1; } } else { - HL_TABLE()[idx].sg_cterm_bg = color + 1; + hl_table[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; if (!ui_rgb_attached()) { @@ -1148,95 +1154,94 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } } else if (strcmp(key, "GUIFG") == 0) { - char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; + int *indexp = &hl_table[idx].sg_rgb_fg_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_fg = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_fg; + int old_idx = hl_table[idx].sg_rgb_fg_idx; + + if (strcmp(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_fg = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_fg = -1; + hl_table[idx].sg_rgb_fg_idx = kColorIdxNone; } + + did_change = hl_table[idx].sg_rgb_fg != old_color || hl_table[idx].sg_rgb_fg != old_idx; } if (is_normal_group) { - normal_fg = HL_TABLE()[idx].sg_rgb_fg; + normal_fg = hl_table[idx].sg_rgb_fg; } } else if (STRCMP(key, "GUIBG") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + int *indexp = &hl_table[idx].sg_rgb_bg_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (STRCMP(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_bg = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_bg; + int old_idx = hl_table[idx].sg_rgb_bg_idx; + + if (STRCMP(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_bg = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_bg = -1; + hl_table[idx].sg_rgb_bg_idx = kColorIdxNone; } + + did_change = hl_table[idx].sg_rgb_bg != old_color || hl_table[idx].sg_rgb_bg != old_idx; } if (is_normal_group) { - normal_bg = HL_TABLE()[idx].sg_rgb_bg; + normal_bg = hl_table[idx].sg_rgb_bg; } } else if (strcmp(key, "GUISP") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + int *indexp = &hl_table[idx].sg_rgb_sp_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_sp = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_sp; + int old_idx = hl_table[idx].sg_rgb_sp_idx; + + if (strcmp(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_sp = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_sp = -1; } + + did_change = hl_table[idx].sg_rgb_sp != old_color || hl_table[idx].sg_rgb_sp != old_idx; } if (is_normal_group) { - normal_sp = HL_TABLE()[idx].sg_rgb_sp; + normal_sp = hl_table[idx].sg_rgb_sp; } } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { // Ignored for now } else if (strcmp(key, "BLEND") == 0) { if (strcmp(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_blend = (int)strtol(arg, NULL, 10); + hl_table[idx].sg_blend = (int)strtol(arg, NULL, 10); } else { - HL_TABLE()[idx].sg_blend = -1; + hl_table[idx].sg_blend = -1; } } else { semsg(_("E423: Illegal argument: %s"), key_start); error = true; break; } - HL_TABLE()[idx].sg_cleared = false; + hl_table[idx].sg_cleared = false; // When highlighting has been given for a group, don't link it. - if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) { - HL_TABLE()[idx].sg_link = 0; + if (!init || !(hl_table[idx].sg_set & SG_LINK)) { + hl_table[idx].sg_link = 0; } // Continue with next argument. @@ -1244,40 +1249,33 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } - // If there is an error, and it's a new entry, remove it from the table. - if (error && idx == highlight_ga.ga_len) { - syn_unadd_group(); - } else { - if (!error && is_normal_group) { - // Need to update all groups, because they might be using "bg" and/or - // "fg", which have been changed now. - highlight_attr_set_all(); - - if (!ui_has(kUILinegrid) && starting == 0) { - // Older UIs assume that we clear the screen after normal group is - // changed - ui_refresh(); - } else { - // TUI and newer UIs will repaint the screen themselves. NOT_VALID - // redraw below will still handle usages of guibg=fg etc. - ui_default_colors_set(); - } - did_highlight_changed = true; - redraw_all_later(NOT_VALID); + if (!error && is_normal_group) { + // Need to update all groups, because they might be using "bg" and/or + // "fg", which have been changed now. + highlight_attr_set_all(); + + if (!ui_has(kUILinegrid) && starting == 0) { + // Older UIs assume that we clear the screen after normal group is + // changed + ui_refresh(); } else { - set_hl_attr(idx); + // TUI and newer UIs will repaint the screen themselves. NOT_VALID + // redraw below will still handle usages of guibg=fg etc. + ui_default_colors_set(); } - HL_TABLE()[idx].sg_script_ctx = current_sctx; - HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); + did_highlight_changed = true; + redraw_all_later(NOT_VALID); + } else { + set_hl_attr(idx); } - xfree(key); - xfree(arg); + hl_table[idx].sg_script_ctx = current_sctx; + hl_table[idx].sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hl_table[idx].sg_script_ctx); // Only call highlight_changed() once, after a sequence of highlight // commands, and only if an attribute actually changed if ((did_change - || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) + || memcmp(&hl_table[idx], &item_before, sizeof(item_before)) != 0) && !did_highlight_changed) { // Do not trigger a redraw when highlighting is changed while // redrawing. This may happen when evaluating 'statusline' changes the @@ -1292,13 +1290,9 @@ void do_highlight(const char *line, const bool forceit, const bool init) #if defined(EXITFREE) void free_highlight(void) { - for (int i = 0; i < highlight_ga.ga_len; i++) { - highlight_clear(i); - xfree(HL_TABLE()[i].sg_name); - xfree(HL_TABLE()[i].sg_name_u); - } ga_clear(&highlight_ga); map_destroy(cstr_t, int)(&highlight_unames); + arena_mem_free(arena_finish(&highlight_arena), NULL); } #endif @@ -1319,39 +1313,39 @@ void restore_cterm_colors(void) /// @return TRUE if highlight group "idx" has any settings. static int hl_has_settings(int idx, bool check_link) { - return HL_TABLE()[idx].sg_cleared == 0 - && (HL_TABLE()[idx].sg_attr != 0 - || HL_TABLE()[idx].sg_cterm_fg != 0 - || HL_TABLE()[idx].sg_cterm_bg != 0 - || HL_TABLE()[idx].sg_rgb_fg_name != NULL - || HL_TABLE()[idx].sg_rgb_bg_name != NULL - || HL_TABLE()[idx].sg_rgb_sp_name != NULL - || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); + return hl_table[idx].sg_cleared == 0 + && (hl_table[idx].sg_attr != 0 + || hl_table[idx].sg_cterm_fg != 0 + || hl_table[idx].sg_cterm_bg != 0 + || hl_table[idx].sg_rgb_fg_idx != kColorIdxNone + || hl_table[idx].sg_rgb_bg_idx != kColorIdxNone + || hl_table[idx].sg_rgb_sp_idx != kColorIdxNone + || (check_link && (hl_table[idx].sg_set & SG_LINK))); } /// Clear highlighting for one group. static void highlight_clear(int idx) { - HL_TABLE()[idx].sg_cleared = true; - - HL_TABLE()[idx].sg_attr = 0; - HL_TABLE()[idx].sg_cterm = 0; - HL_TABLE()[idx].sg_cterm_bold = false; - HL_TABLE()[idx].sg_cterm_fg = 0; - HL_TABLE()[idx].sg_cterm_bg = 0; - HL_TABLE()[idx].sg_gui = 0; - HL_TABLE()[idx].sg_rgb_fg = -1; - HL_TABLE()[idx].sg_rgb_bg = -1; - HL_TABLE()[idx].sg_rgb_sp = -1; - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); - HL_TABLE()[idx].sg_blend = -1; + hl_table[idx].sg_cleared = true; + + hl_table[idx].sg_attr = 0; + hl_table[idx].sg_cterm = 0; + hl_table[idx].sg_cterm_bold = false; + hl_table[idx].sg_cterm_fg = 0; + hl_table[idx].sg_cterm_bg = 0; + hl_table[idx].sg_gui = 0; + hl_table[idx].sg_rgb_fg = -1; + hl_table[idx].sg_rgb_bg = -1; + hl_table[idx].sg_rgb_sp = -1; + hl_table[idx].sg_rgb_fg_idx = kColorIdxNone; + hl_table[idx].sg_rgb_bg_idx = kColorIdxNone; + hl_table[idx].sg_rgb_sp_idx = kColorIdxNone; + hl_table[idx].sg_blend = -1; // Restore default link and context if they exist. Otherwise clears. - HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; + hl_table[idx].sg_link = hl_table[idx].sg_deflink; // Since we set the default link, set the location to where the default // link was set. - HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; + hl_table[idx].sg_script_ctx = hl_table[idx].sg_deflink_sctx; } /// \addtogroup LIST_XXX @@ -1363,7 +1357,7 @@ static void highlight_clear(int idx) static void highlight_list_one(const int id) { - const HlGroup *sgp = &HL_TABLE()[id - 1]; // index is ID minus one + const HlGroup *sgp = &hl_table[id - 1]; // index is ID minus one bool didh = false; if (message_filtered(sgp->sg_name)) { @@ -1379,12 +1373,13 @@ static void highlight_list_one(const int id) didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_gui, NULL, "gui"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_fg_name, "guifg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_bg_name, "guibg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_sp_name, "guisp"); + char hexbuf[8]; + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_fg_idx, sgp->sg_rgb_fg, hexbuf), "guifg"); + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_bg_idx, sgp->sg_rgb_bg, hexbuf), "guibg"); + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_sp_idx, sgp->sg_rgb_sp, hexbuf), "guisp"); didh = highlight_list_arg(id, didh, LIST_INT, sgp->sg_blend + 1, NULL, "blend"); @@ -1394,7 +1389,7 @@ static void highlight_list_one(const int id) didh = true; msg_puts_attr("links to", HL_ATTR(HLF_D)); msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name); } if (!didh) { @@ -1410,11 +1405,11 @@ Dictionary get_global_hl_defs(void) Dictionary rv = ARRAY_DICT_INIT; for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { Dictionary attrs = ARRAY_DICT_INIT; - HlGroup *h = &HL_TABLE()[i - 1]; + HlGroup *h = &hl_table[i - 1]; if (h->sg_attr > 0) { attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); } else if (h->sg_link > 0) { - const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + const char *link = (const char *)hl_table[h->sg_link - 1].sg_name; PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); } PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); @@ -1428,7 +1423,7 @@ Dictionary get_global_hl_defs(void) /// @param type one of \ref LIST_XXX /// @param iarg integer argument used if \p type == LIST_INT /// @param sarg string used if \p type == LIST_STRING -static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, +static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, const char *sarg, const char *const name) { char buf[100]; @@ -1437,7 +1432,7 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg return false; } if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char *ts = buf; + const char *ts = buf; if (type == LIST_INT) { snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); } else if (type == LIST_STRING) { @@ -1485,9 +1480,9 @@ const char *highlight_has_attr(const int id, const int flag, const int modec) } if (modec == 'g') { - attr = HL_TABLE()[id - 1].sg_gui; + attr = hl_table[id - 1].sg_gui; } else { - attr = HL_TABLE()[id - 1].sg_cterm; + attr = hl_table[id - 1].sg_cterm; } return (attr & flag) ? "1" : NULL; @@ -1528,11 +1523,11 @@ const char *highlight_color(const int id, const char *const what, const int mode if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { - n = HL_TABLE()[id - 1].sg_rgb_fg; + n = hl_table[id - 1].sg_rgb_fg; } else if (sp) { - n = HL_TABLE()[id - 1].sg_rgb_sp; + n = hl_table[id - 1].sg_rgb_sp; } else { - n = HL_TABLE()[id - 1].sg_rgb_bg; + n = hl_table[id - 1].sg_rgb_bg; } if (n < 0 || n > 0xffffff) { return NULL; @@ -1541,21 +1536,21 @@ const char *highlight_color(const int id, const char *const what, const int mode return name; } if (fg) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; - } - if (sp) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; + return coloridx_to_name(hl_table[id - 1].sg_rgb_fg_idx, hl_table[id - 1].sg_rgb_fg, name); + } else if (sp) { + return coloridx_to_name(hl_table[id - 1].sg_rgb_sp_idx, hl_table[id - 1].sg_rgb_sp, name); + } else { + return coloridx_to_name(hl_table[id - 1].sg_rgb_bg_idx, hl_table[id - 1].sg_rgb_bg, name); } - return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } if (font || sp) { return NULL; } if (modec == 'c') { if (fg) { - n = HL_TABLE()[id - 1].sg_cterm_fg - 1; + n = hl_table[id - 1].sg_cterm_fg - 1; } else { - n = HL_TABLE()[id - 1].sg_cterm_bg - 1; + n = hl_table[id - 1].sg_cterm_bg - 1; } if (n < 0) { return NULL; @@ -1586,7 +1581,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool if (got_int) { return true; } - msg_outtrans(HL_TABLE()[id - 1].sg_name); + msg_outtrans(hl_table[id - 1].sg_name); name_col = msg_col; endcol = 15; } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { @@ -1630,7 +1625,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool static void set_hl_attr(int idx) { HlAttrs at_en = HLATTRS_INIT; - HlGroup *sgp = HL_TABLE() + idx; + HlGroup *sgp = hl_table + idx; at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm; at_en.cterm_fg_color = sgp->sg_cterm_fg; @@ -1639,9 +1634,9 @@ static void set_hl_attr(int idx) // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name // before setting attr_entry->{f,g}g_color to a other than -1 - at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; - at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; + at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1; + at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1; + at_en.rgb_sp_color = sgp->sg_rgb_sp_idx != kColorIdxNone ? sgp->sg_rgb_sp : -1; at_en.hl_blend = sgp->sg_blend; sgp->sg_attr = hl_get_syn_attr(0, idx + 1, at_en); @@ -1708,7 +1703,7 @@ char_u *syn_id2name(int id) if (id <= 0 || id > highlight_ga.ga_len) { return (char_u *)""; } - return HL_TABLE()[id - 1].sg_name; + return hl_table[id - 1].sg_name; } /// Find highlight group name in the table and return its ID. @@ -1726,7 +1721,7 @@ int syn_check_group(const char *name, size_t len) } int id = syn_name2id_len((char_u *)name, len); if (id == 0) { // doesn't exist yet - return syn_add_group(vim_strnsave((char_u *)name, len)); + return syn_add_group(name, len); } return id; } @@ -1735,18 +1730,16 @@ int syn_check_group(const char *name, size_t len) /// /// @param name must be an allocated string, it will be consumed. /// @return 0 for failure, else the allocated group id -/// @see syn_check_group syn_unadd_group -static int syn_add_group(char_u *name) +/// @see syn_check_group +static int syn_add_group(const char *name, size_t len) { - char_u *p; - // Check that the name is ASCII letters, digits and underscore. - for (p = name; *p != NUL; p++) { - if (!vim_isprintc(*p)) { + for (size_t i = 0; i < len; i++) { + int c = (int8_t)name[i]; + if (!vim_isprintc(c)) { emsg(_("E669: Unprintable character in group name")); - xfree(name); return 0; - } else if (!ASCII_ISALNUM(*p) && *p != '_') { + } else if (!ASCII_ISALNUM(c) && c != '_') { // This is an error, but since there previously was no check only give a warning. msg_source(HL_ATTR(HLF_W)); msg(_("W18: Invalid character in group name")); @@ -1762,46 +1755,36 @@ static int syn_add_group(char_u *name) if (highlight_ga.ga_len >= MAX_HL_ID) { emsg(_("E849: Too many highlight and syntax groups")); - xfree(name); return 0; } - char *const name_up = (char *)vim_strsave_up(name); - // Append another syntax_highlight entry. HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga); memset(hlgp, 0, sizeof(*hlgp)); - hlgp->sg_name = name; + hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len); hlgp->sg_rgb_bg = -1; hlgp->sg_rgb_fg = -1; hlgp->sg_rgb_sp = -1; + hlgp->sg_rgb_bg_idx = kColorIdxNone; + hlgp->sg_rgb_fg_idx = kColorIdxNone; + hlgp->sg_rgb_sp_idx = kColorIdxNone; hlgp->sg_blend = -1; - hlgp->sg_name_u = name_up; + hlgp->sg_name_u = arena_memdupz(&highlight_arena, name, len); + vim_strup((char_u *)hlgp->sg_name_u); int id = highlight_ga.ga_len; // ID is index plus one - map_put(cstr_t, int)(&highlight_unames, name_up, id); + map_put(cstr_t, int)(&highlight_unames, hlgp->sg_name_u, id); return id; } -/// When, just after calling syn_add_group(), an error is discovered, this -/// function deletes the new name. -static void syn_unadd_group(void) -{ - highlight_ga.ga_len--; - HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; - map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); - xfree(item->sg_name); - xfree(item->sg_name_u); -} - /// Translate a group ID to highlight attributes. /// @see syn_attr2entry int syn_id2attr(int hl_id) { hl_id = syn_get_final_id(hl_id); - HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); if (attr >= 0) { @@ -1822,7 +1805,7 @@ int syn_get_final_id(int hl_id) // Follow links until there is no more. // Look out for loops! Break after 100 links. for (count = 100; --count >= 0;) { - HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one // ACHTUNG: when using "tmp" attribute (no link) the function might be // called twice. it needs be smart enough to remember attr only to @@ -1848,15 +1831,21 @@ int syn_get_final_id(int hl_id) void highlight_attr_set_all(void) { for (int idx = 0; idx < highlight_ga.ga_len; idx++) { - HlGroup *sgp = &HL_TABLE()[idx]; - if (sgp->sg_rgb_bg_name != NULL) { - sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); + HlGroup *sgp = &hl_table[idx]; + if (sgp->sg_rgb_bg_idx == kColorIdxFg) { + sgp->sg_rgb_bg = normal_fg; + } else if (sgp->sg_rgb_bg_idx == kColorIdxBg) { + sgp->sg_rgb_bg = normal_bg; } - if (sgp->sg_rgb_fg_name != NULL) { - sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); + if (sgp->sg_rgb_fg_idx == kColorIdxFg) { + sgp->sg_rgb_fg = normal_fg; + } else if (sgp->sg_rgb_fg_idx == kColorIdxBg) { + sgp->sg_rgb_fg = normal_bg; } - if (sgp->sg_rgb_sp_name != NULL) { - sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); + if (sgp->sg_rgb_sp_idx == kColorIdxFg) { + sgp->sg_rgb_sp = normal_fg; + } else if (sgp->sg_rgb_sp_idx == kColorIdxBg) { + sgp->sg_rgb_sp = normal_bg; } set_hl_attr(idx); } @@ -1866,7 +1855,7 @@ void highlight_attr_set_all(void) static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) FUNC_ATTR_NONNULL_ALL { - HlGroup *const hlt = HL_TABLE(); + HlGroup *const hlt = hl_table; if (id_alt == 0) { memset(&hlt[hlcnt + i], 0, sizeof(HlGroup)); @@ -1951,7 +1940,7 @@ void highlight_changed(void) hlcnt = highlight_ga.ga_len; if (id_S == -1) { // Make sure id_S is always valid to simplify code below. Use the last entry - memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(HlGroup)); + memset(&hl_table[hlcnt + 9], 0, sizeof(HlGroup)); id_S = hlcnt + 10; } for (int i = 0; i < 9; i++) { @@ -2048,7 +2037,7 @@ const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) } // Items are never removed from the table, skip the ones that were cleared. - if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { + if (skip_cleared && idx < highlight_ga.ga_len && hl_table[idx].sg_cleared) { return ""; } @@ -2066,7 +2055,7 @@ const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) } else if (idx >= highlight_ga.ga_len) { return NULL; } - return (const char *)HL_TABLE()[idx].sg_name; + return (const char *)hl_table[idx].sg_name; } color_name_table_T color_name_table[] = { @@ -2758,29 +2747,55 @@ color_name_table_T color_name_table[] = { /// hex value /// /// @param[in] name string value to convert to RGB +/// @param[out] idx index in color table or special value /// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const char *name) +RgbValue name_to_color(const char *name, int *idx) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) && isxdigit(name[6]) && name[7] == NUL) { // rgb hex string + *idx = kColorIdxHex; return (RgbValue)strtol((char *)(name + 1), NULL, 16); } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { + *idx = kColorIdxBg; return normal_bg; } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { + *idx = kColorIdxFg; return normal_fg; } for (int i = 0; color_name_table[i].name != NULL; i++) { if (!STRICMP(name, color_name_table[i].name)) { + *idx = i; return color_name_table[i].color; } } + *idx = kColorIdxNone; return -1; } +const char *coloridx_to_name(int idx, int val, char hexbuf[8]) +{ + if (idx >= 0) { + return color_name_table[idx].name; + } + switch (idx) { + case kColorIdxNone: + return NULL; + case kColorIdxFg: + return "fg"; + case kColorIdxBg: + return "bg"; + case kColorIdxHex: + snprintf(hexbuf, 7 + 1, "#%06x", val); + return hexbuf; + default: + abort(); + } +} + int name_to_ctermcolor(const char *name) { int i; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 010d2fe869..d71b3adb5c 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -697,8 +697,10 @@ int get_lisp_indent(void) && lisp_match(that + 1)) { amount += 2; } else { - that++; - amount++; + if (*that != NUL) { + that++; + amount++; + } firsttry = amount; while (ascii_iswhite(*that)) { diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 4238088b1c..b5b3adf7d2 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -121,6 +121,9 @@ #define kv_push(v, x) \ (*kv_pushp(v) = (x)) +#define kv_pushp_c(v) ((v).items + ((v).size++)) +#define kv_push_c(v, x) (*kv_pushp_c(v) = (x)) + #define kv_a(v, i) \ (*(((v).capacity <= (size_t)(i) \ ? ((v).capacity = (v).size = (i) + 1, \ diff --git a/src/nvim/memory.c b/src/nvim/memory.c index dd06419391..a615802b36 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -605,6 +605,14 @@ void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk) } } +char *arena_memdupz(Arena *arena, const char *buf, size_t size) +{ + char *mem = arena_alloc(arena, size + 1, false); + memcpy(mem, buf, size); + mem[size] = NUL; + return mem; +} + #if defined(EXITFREE) # include "nvim/buffer.h" @@ -786,6 +794,7 @@ void free_all_mem(void) decor_free_all_mem(); nlua_free_all_mem(); + ui_free_all_mem(); } #endif diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 0c048e1b5b..63d607c2ce 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -51,6 +51,10 @@ typedef struct { // inits an empty arena. use arena_start() to actually allocate space! #define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 } +#define kv_fixsize_arena(a, v, s) \ + ((v).capacity = (s), \ + (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif diff --git a/src/nvim/message.c b/src/nvim/message.c index acc3122e93..e192af6aad 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1068,6 +1068,7 @@ void ex_messages(void *const eap_p) } } ui_call_msg_history_show(entries); + api_free_array(entries); msg_ext_history_visible = true; wait_return(false); } else { @@ -1262,11 +1263,11 @@ void wait_return(int redraw) msg_ext_keep_after_cmdline = true; } - // If the window size changed set_shellsize() will redraw the screen. + // If the screen size changed screen_resize() will redraw the screen. // Otherwise the screen is only redrawn if 'redraw' is set and no ':' // typed. tmpState = State; - State = oldState; // restore State before set_shellsize + State = oldState; // restore State before screen_resize() setmouse(); msg_check(); need_wait_return = false; @@ -3136,12 +3137,13 @@ void msg_ext_ui_flush(void) msg_ext_emit_chunk(); if (msg_ext_chunks.size > 0) { - ui_call_msg_show(cstr_to_string(msg_ext_kind), + ui_call_msg_show(cstr_as_string((char *)msg_ext_kind), msg_ext_chunks, msg_ext_overwrite); if (!msg_ext_overwrite) { msg_ext_visible++; } msg_ext_kind = NULL; + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; msg_ext_overwrite = false; @@ -3155,6 +3157,7 @@ void msg_ext_flush_showmode(void) if (ui_has(kUIMessages)) { msg_ext_emit_chunk(); ui_call_msg_showmode(msg_ext_chunks); + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 9d0099a2f8..cc811fca18 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1518,12 +1518,10 @@ void set_empty_rows(win_T *wp, int used) } } -/* - * Recompute topline to put the cursor at the bottom of the window. - * Scroll at least "min_scroll" lines. - * If "set_topbot" is true, set topline and botline first (for "zb"). - * This is messy stuff!!! - */ +/// Recompute topline to put the cursor at the bottom of the window. +/// When scrolling scroll at least "min_scroll" lines. +/// If "set_topbot" is true, set topline and botline first (for "zb"). +/// This is messy stuff!!! void scroll_cursor_bot(int min_scroll, int set_topbot) { int used; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index b4f39c4c61..388fa2584c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -360,6 +360,17 @@ free_ret: api_clear_error(&error); } +bool rpc_write_raw(uint64_t id, WBuffer *buffer) +{ + Channel *channel = find_rpc_channel(id); + if (!channel) { + wstream_release_wbuffer(buffer); + return false; + } + + return channel_write(channel, buffer); +} + static bool channel_write(Channel *channel, WBuffer *buffer) { bool success; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 419349e74d..26c1843026 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -11,10 +11,6 @@ # include "msgpack_rpc/unpacker.c.generated.h" #endif -#define kv_fixsize_arena(a, v, s) \ - ((v).capacity = (s), \ - (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) - Object unpack(const char *data, size_t size, Error *err) { Unpacker unpacker; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 35a7ee3319..8e4f78818b 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2755,13 +2755,13 @@ static void display_showcmd(void) showcmd_is_clear = (len == 0); if (ui_has(kUIMessages)) { - Array content = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); if (len > 0) { - Array chunk = ARRAY_DICT_INIT; // placeholder for future highlight support - ADD(chunk, INTEGER_OBJ(0)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf))); - ADD(content, ARRAY_OBJ(chunk)); + ADD_C(chunk, INTEGER_OBJ(0)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf))); + ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); return; diff --git a/src/nvim/option.c b/src/nvim/option.c index 075dd3d4bc..c97c027740 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4528,7 +4528,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, last_status(false); // (re)set last window status line. } else if (pp == &p_stal) { // (re)set tab page line - shell_new_rows(); // recompute window positions and heights + win_new_screen_rows(); // recompute window positions and heights } else if (pp == &curwin->w_p_fdl) { newFoldLevel(); } else if (pp == &curwin->w_p_fml) { @@ -4617,7 +4617,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, p_columns = MIN_COLUMNS; } - // True max size is defined by check_shellsize() + // True max size is defined by check_screensize() p_lines = MIN(p_lines, INT_MAX); p_columns = MIN(p_columns, INT_MAX); @@ -4635,7 +4635,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // messages. Rows = (int)p_lines; Columns = (int)p_columns; - check_shellsize(); + check_screensize(); if (cmdline_row > Rows - p_ch && Rows > p_ch) { assert(p_ch >= 0 && Rows - p_ch <= INT_MAX); cmdline_row = (int)(Rows - p_ch); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 7233a3bb72..db1054eb1f 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -151,17 +151,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_external) { if (array_changed) { - Array arr = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + Array arr = arena_array(&arena, (size_t)size); for (int i = 0; i < size; i++) { - Array item = ARRAY_DICT_INIT; - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); - ADD(arr, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 4); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_text))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_kind))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_extra))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_info))); + ADD_C(arr, ARRAY_OBJ(item)); } ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, pum_anchor_grid); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } else { ui_call_popupmenu_select(selected); return; @@ -438,7 +441,7 @@ void pum_redraw(void) if (ui_has(kUIMultigrid)) { const char *anchor = pum_above ? "SW" : "NW"; int row_off = pum_above ? -pum_height : 0; - ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), + ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor), pum_anchor_grid, pum_row - row_off, pum_col - col_off, false, pum_grid.zindex); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2ee7cd44f7..98f8e30391 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5578,9 +5578,9 @@ void check_for_delay(bool check_msg_scroll) /// /// There may be some time between setting Rows and Columns and (re)allocating /// default_grid arrays. This happens when starting up and when -/// (manually) changing the shell size. Always use default_grid.rows and +/// (manually) changing the screen size. Always use default_grid.rows and /// default_grid.Columns to access items in default_grid.chars[]. Use Rows -/// and Columns for positioning text etc. where the final size of the shell is +/// and Columns for positioning text etc. where the final size of the screen is /// needed. void screenalloc(void) { @@ -5615,14 +5615,14 @@ retry: */ ++RedrawingDisabled; - // win_new_shellsize will recompute floats position, but tell the + // win_new_screensize will recompute floats position, but tell the // compositor to not redraw them yet ui_comp_set_screen_valid(false); if (msg_grid.chars) { msg_grid_invalid = true; } - win_new_shellsize(); // fit the windows in the new sized shell + win_new_screensize(); // fit the windows in the new sized screen comp_col(); // recompute columns for shown command and ruler @@ -6341,35 +6341,49 @@ void draw_tabline(void) void ui_ext_tabline_update(void) { - Array tabs = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + + size_t n_tabs = 0; FOR_ALL_TABS(tp) { - Dictionary tab_info = ARRAY_DICT_INIT; - PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle)); + n_tabs++; + } + + Array tabs = arena_array(&arena, n_tabs); + FOR_ALL_TABS(tp) { + Dictionary tab_info = arena_dict(&arena, 2); + PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); + + ADD_C(tabs, DICTIONARY_OBJ(tab_info)); + } - ADD(tabs, DICTIONARY_OBJ(tab_info)); + size_t n_buffers = 0; + FOR_ALL_BUFFERS(buf) { + n_buffers += buf->b_p_bl ? 1 : 0; } - Array buffers = ARRAY_DICT_INIT; + Array buffers = arena_array(&arena, n_buffers); FOR_ALL_BUFFERS(buf) { // Do not include unlisted buffers if (!buf->b_p_bl) { continue; } - Dictionary buffer_info = ARRAY_DICT_INIT; - PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + Dictionary buffer_info = arena_dict(&arena, 2); + PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - ADD(buffers, DICTIONARY_OBJ(buffer_info)); + ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } /* @@ -6618,11 +6632,11 @@ static void win_redr_ruler(win_T *wp, bool always) } if (ui_has(kUIMessages) && !part_of_status) { - Array content = ARRAY_DICT_INIT; - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, INTEGER_OBJ(attr)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer))); - ADD(content, ARRAY_OBJ(chunk)); + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); + ADD_C(chunk, INTEGER_OBJ(attr)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer))); + ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; } else { @@ -6744,7 +6758,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) prev_col_off = cur_col_off; } -/// Set dimensions of the Nvim application "shell". +/// Set dimensions of the Nvim application "screen". void screen_resize(int width, int height) { // Avoid recursiveness, can happen when setting the window size causes @@ -6775,7 +6789,7 @@ void screen_resize(int width, int height) Rows = height; Columns = width; - check_shellsize(); + check_screensize(); int max_p_ch = Rows - min_rows() + 1; if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) { p_ch = max_p_ch ? max_p_ch : 1; @@ -6852,49 +6866,23 @@ void screen_resize(int width, int height) resizing_screen = false; } -/// Check if the new Nvim application "shell" dimensions are valid. +/// Check if the new Nvim application "screen" dimensions are valid. /// Correct it if it's too small or way too big. -void check_shellsize(void) +void check_screensize(void) { + // Limit Rows and Columns to avoid an overflow in Rows * Columns. if (Rows < min_rows()) { // need room for one window and command line Rows = min_rows(); + } else if (Rows > 1000) { + Rows = 1000; } - limit_screen_size(); -} -// Limit Rows and Columns to avoid an overflow in Rows * Columns. -void limit_screen_size(void) -{ if (Columns < MIN_COLUMNS) { Columns = MIN_COLUMNS; } else if (Columns > 10000) { Columns = 10000; } - - if (Rows > 1000) { - Rows = 1000; - } -} - -void win_new_shellsize(void) -{ - static long old_Rows = 0; - static long old_Columns = 0; - - if (old_Rows != Rows) { - // If 'window' uses the whole screen, keep it using that. - // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { - p_window = Rows - 1; - } - old_Rows = Rows; - shell_new_rows(); // update window sizes - } - if (old_Columns != Columns) { - old_Columns = Columns; - shell_new_columns(); // update window sizes - } } win_T *get_win_by_grid_handle(handle_T handle) diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 47305b6709..0d43458b5b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -2381,8 +2381,6 @@ static void update_si_attr(int idx) } else { sip->si_attr = CUR_STATE(idx - 1).si_attr; sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id; - sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos; - sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos; if (sip->si_cont_list == NULL) { sip->si_flags |= HL_TRANS_CONT; sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index b9fdddf235..85517a71a4 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -257,7 +257,8 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { - color_val = name_to_color(name); + int dummy; + color_val = name_to_color(name, &dummy); xfree(name); if (color_val != -1) { diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index b2c752376f..d26c80077d 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -636,6 +636,14 @@ func Test_illegal_address2() call delete('Xtest.vim') endfunc +func Test_mark_from_line_zero() + " this was reading past the end of the first (empty) line + new + norm oxxxx + call assert_fails("0;'(", 'E20:') + bwipe! +endfunc + func Test_cmdline_complete_wildoptions() help call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx') diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim index ff710b2716..4144fb0521 100644 --- a/src/nvim/testdir/test_lispwords.vim +++ b/src/nvim/testdir/test_lispwords.vim @@ -1,4 +1,5 @@ -" Tests for 'lispwords' settings being global-local +" Tests for 'lispwords' settings being global-local. +" And other lisp indent stuff. set nocompatible viminfo+=nviminfo @@ -85,4 +86,13 @@ func Test_lisp_indent() set nolisp endfunc +func Test_lisp_indent_works() + " This was reading beyond the end of the line + new + exe "norm a\tü(\<CR>=" + set lisp + norm == + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index c55ba391a5..cf90e416c4 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -363,21 +363,29 @@ func Test_mkview_open_folds() call append(0, ['a', 'b', 'c']) 1,3fold + write! Xtestfile + + call assert_notequal(-1, foldclosed(1)) + call assert_notequal(-1, foldclosed(2)) + call assert_notequal(-1, foldclosed(3)) + + " Save the view with folds closed + mkview! Xtestview + " zR affects 'foldlevel', make sure the option is applied after the folds " have been recreated. + " Open folds to ensure they get closed when restoring the view normal zR - write! Xtestfile call assert_equal(-1, foldclosed(1)) call assert_equal(-1, foldclosed(2)) call assert_equal(-1, foldclosed(3)) - mkview! Xtestview source Xtestview - call assert_equal(-1, foldclosed(1)) - call assert_equal(-1, foldclosed(2)) - call assert_equal(-1, foldclosed(3)) + call assert_notequal(-1, foldclosed(1)) + call assert_notequal(-1, foldclosed(2)) + call assert_notequal(-1, foldclosed(3)) call delete('Xtestview') call delete('Xtestfile') diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 6bef61ae8f..c46bba04e3 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -778,6 +778,82 @@ func Test_search_syntax_skip() bwipe! endfunc +func Test_syn_contained_transparent() + " Comments starting with "Regression:" show the result when the highlighting + " span of the containing item is assigned to the contained region. + syntax on + + let l:case = "Transparent region contained in region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ contains=X + + call setline(1, "==(--[~~]--)==") + let l:expected = " YYYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region extends region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ end=/e/ contains=X + + call setline(1, "==(--[~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYY " + " Regression: " YYYYYYY YYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Nested transparent regions extend region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ end=/e/ contains=X + + call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYYYYYYYYYYYYYY " + " Regression: " YYYYYYY YYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region contained in match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-})/ contains=X + + call setline(1, "==(--[~~]--)==") + let l:expected = " YYYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region extends match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-}[e)]/ contains=X + + call setline(1, "==(--[~~e~~]--)==") + let l:expected = " YYYYYYYYYY " + " Regression: " YYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Nested transparent regions extend match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-}[e)]/ contains=X + + call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYYYYYYYYYYY " + " Regression: " YYYYYYY YYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! +endfunc + func Test_syn_include_contains_TOP() let l:case = "TOP in included syntax means its group list name" new diff --git a/src/nvim/ui.c b/src/nvim/ui.c index bdcb7af7e9..a49e9df9ee 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -64,6 +64,8 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; static bool has_mouse = false; static int pending_has_mouse = -1; +static Array call_buf = ARRAY_DICT_INIT; + #if MIN_LOG_LEVEL > LOGLVL_DBG # define UI_LOG(funname) #else @@ -123,6 +125,12 @@ void ui_init(void) default_grid.handle = 1; msg_grid_adj.target = &default_grid; ui_comp_init(); + kv_ensure_space(call_buf, 16); +} + +void ui_free_all_mem(void) +{ + kv_destroy(call_buf); } void ui_builtin_start(void) @@ -173,15 +181,6 @@ bool ui_active(void) return ui_count > 1; } -void ui_event(char *name, Array args) -{ - bool args_consumed = false; - ui_call_event(name, args, &args_consumed); - if (!args_consumed) { - api_free_array(args); - } -} - void ui_refresh(void) { if (!ui_active()) { @@ -496,6 +495,11 @@ int ui_current_col(void) return cursor_col; } +handle_T ui_cursor_grid(void) +{ + return cursor_grid_handle; +} + void ui_flush(void) { cmdline_ui_flush(); @@ -508,13 +512,15 @@ void ui_flush(void) pending_cursor_update = false; } if (pending_mode_info_update) { - Array style = mode_style_array(); + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + Array style = mode_style_array(&arena); bool enabled = (*p_guicursor != NUL); ui_call_mode_info_set(enabled, style); - api_free_array(style); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); pending_mode_info_update = false; } - if (pending_mode_update) { + if (pending_mode_update && !starting) { char *full_name = shape_table[ui_mode_idx].full_name; ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx); pending_mode_update = false; diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 1d278010e8..7dd2f5bce3 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -8,6 +8,7 @@ #include "nvim/api/private/defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" +#include "nvim/memory.h" typedef enum { kUICmdline = 0, @@ -46,6 +47,8 @@ enum { typedef int LineFlags; +EXTERN ArenaMem ui_ext_fixblk INIT(= NULL); + struct ui_t { bool rgb; bool override; ///< Force highest-requested UI capabilities. diff --git a/src/nvim/window.c b/src/nvim/window.c index 1487759d60..1a1f62f2c0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -893,7 +893,7 @@ void ui_ext_win_position(win_T *wp) wp->w_grid_alloc.zindex = wp->w_float_config.zindex; if (ui_has(kUIMultigrid)) { - String anchor = cstr_to_string(float_anchor_str[c.anchor]); + String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]); ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, grid->handle, row, col, c.focusable, wp->w_grid_alloc.zindex); @@ -967,8 +967,8 @@ static int check_split_disallowed(void) * "flags": * WSP_ROOM: require enough room for new window * WSP_VERT: vertical split. - * WSP_TOP: open window at the top-left of the shell (help window). - * WSP_BOT: open window at the bottom-right of the shell (quickfix window). + * WSP_TOP: open window at the top-left of the screen (help window). + * WSP_BOT: open window at the bottom-right of the screen (quickfix window). * WSP_HELP: creating the help window, keep layout snapshot * * return FAIL for failure, OK otherwise @@ -2499,7 +2499,7 @@ void close_windows(buf_T *buf, bool keep_curwin) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } @@ -2606,7 +2606,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev win_close_othertab(win, free_buf, prev_curtab); if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } entering_window(curwin); @@ -4350,10 +4350,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a } if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { - shell_new_rows(); + win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { - shell_new_columns(); // update window widths + win_new_screen_cols(); // update window widths } lastused_tabpage = old_curtab; @@ -5277,11 +5277,29 @@ static void frame_remove(frame_T *frp) } } -/* - * Called from win_new_shellsize() after Rows changed. - * This only does the current tab page, others must be done when made active. - */ -void shell_new_rows(void) +void win_new_screensize(void) +{ + static long old_Rows = 0; + static long old_Columns = 0; + + if (old_Rows != Rows) { + // If 'window' uses the whole screen, keep it using that. + // Don't change it when set with "-w size" on the command line. + if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { + p_window = Rows - 1; + } + old_Rows = Rows; + win_new_screen_rows(); // update window sizes + } + if (old_Columns != Columns) { + old_Columns = Columns; + win_new_screen_cols(); // update window sizes + } +} +/// Called from win_new_screensize() after Rows changed. +/// +/// This only does the current tab page, others must be done when made active. +void win_new_screen_rows(void) { int h = (int)ROWS_AVAIL; @@ -5305,10 +5323,8 @@ void shell_new_rows(void) curtab->tp_ch_used = p_ch; } -/* - * Called from win_new_shellsize() after Columns changed. - */ -void shell_new_columns(void) +/// Called from win_new_screensize() after Columns changed. +void win_new_screen_cols(void) { if (firstwin == NULL) { // not initialized yet return; |