diff options
Diffstat (limited to 'src')
64 files changed, 1711 insertions, 1335 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index a268e04559..8355bfe868 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -51,10 +51,10 @@ Integer buffer_get_length(Buffer buffer, Error *err) String buffer_get_line(Buffer buffer, Integer index, Error *err) { String rv = {.size = 0}; - StringArray slice = buffer_get_slice(buffer, index, index, true, true, err); + Array slice = buffer_get_slice(buffer, index, index, true, true, err); if (!err->set && slice.size) { - rv = slice.items[0]; + rv = slice.items[0].data.string; } free(slice.items); @@ -70,7 +70,8 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// @param[out] err Details of an error that may have occurred void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { - StringArray array = {.items = &line, .size = 1}; + Object l = STRING_OBJ(line); + Array array = {.items = &l, .size = 1}; buffer_set_slice(buffer, index, index, true, true, array, err); } @@ -81,7 +82,7 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) { - StringArray array = ARRAY_DICT_INIT; + Array array = ARRAY_DICT_INIT; buffer_set_slice(buffer, index, index, true, true, array, err); } @@ -94,14 +95,14 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err) /// @param include_end True if the slice includes the `end` parameter /// @param[out] err Details of an error that may have occurred /// @return An array of lines -StringArray buffer_get_slice(Buffer buffer, - Integer start, - Integer end, - Boolean include_start, - Boolean include_end, - Error *err) +ArrayOf(String) buffer_get_slice(Buffer buffer, + Integer start, + Integer end, + Boolean include_start, + Boolean include_end, + Error *err) { - StringArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -118,7 +119,7 @@ StringArray buffer_get_slice(Buffer buffer, } rv.size = (size_t)(end - start); - rv.items = xcalloc(sizeof(String), rv.size); + rv.items = xcalloc(sizeof(Object), rv.size); for (size_t i = 0; i < rv.size; i++) { int64_t lnum = start + (int64_t)i; @@ -129,13 +130,13 @@ StringArray buffer_get_slice(Buffer buffer, } const char *bufstr = (char *) ml_get_buf(buf, (linenr_T) lnum, false); - rv.items[i] = cstr_to_string(bufstr); + rv.items[i] = STRING_OBJ(cstr_to_string(bufstr)); } end: if (err->set) { for (size_t i = 0; i < rv.size; i++) { - free(rv.items[i].data); + free(rv.items[i].data.string.data); } free(rv.items); @@ -152,15 +153,15 @@ end: /// @param end The last line index /// @param include_start True if the slice includes the `start` parameter /// @param include_end True if the slice includes the `end` parameter -/// @param lines An array of lines to use as replacement(A 0-length array -/// will simply delete the line range) +/// @param replacement An array of lines to use as replacement(A 0-length +// array will simply delete the line range) /// @param[out] err Details of an error that may have occurred void buffer_set_slice(Buffer buffer, Integer start, Integer end, Boolean include_start, Boolean include_end, - StringArray replacement, + ArrayOf(String) replacement, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -184,10 +185,15 @@ void buffer_set_slice(Buffer buffer, size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); ssize_t extra = 0; // lines added to text, can be negative - char **lines = (new_len != 0) ? xmalloc(new_len * sizeof(char *)) : NULL; + char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { - String l = replacement.items[i]; + if (replacement.items[i].type != kObjectTypeString) { + set_api_error("all items in the replacement array must be strings", err); + goto end; + } + + String l = replacement.items[i].data.string; lines[i] = xmemdupz(l.data, l.size); } @@ -430,7 +436,10 @@ Boolean buffer_is_valid(Buffer buffer) /// to the end of the buffer. /// @param lines An array of lines /// @param[out] err Details of an error that may have occurred -void buffer_insert(Buffer buffer, Integer lnum, StringArray lines, Error *err) +void buffer_insert(Buffer buffer, + Integer lnum, + ArrayOf(String) lines, + Error *err) { buffer_set_slice(buffer, lnum, lnum, false, true, lines, err); } @@ -441,9 +450,9 @@ void buffer_insert(Buffer buffer, Integer lnum, StringArray lines, Error *err) /// @param name The mark's name /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple -Position buffer_get_mark(Buffer buffer, String name, Error *err) +ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -473,8 +482,9 @@ Position buffer_get_mark(Buffer buffer, String name, Error *err) return rv; } - rv.row = posp->lnum; - rv.col = posp->col; + ADD(rv, INTEGER_OBJ(posp->lnum)); + ADD(rv, INTEGER_OBJ(posp->col)); + return rv; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b049412014..2dd229ec5f 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -5,17 +5,15 @@ #include <stdbool.h> #include <string.h> -#define ARRAY_DICT_INIT {.size = 0, .items = NULL} +#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define STRING_INIT {.data = NULL, .size = 0} #define OBJECT_INIT { .type = kObjectTypeNil } -#define POSITION_INIT { .row = 0, .col = 0 } #define REMOTE_TYPE(type) typedef uint64_t type -#define TYPED_ARRAY_OF(type) \ - typedef struct { \ - type *items; \ - size_t size; \ - } type##Array +#ifdef INCLUDE_GENERATED_DECLARATIONS + #define ArrayOf(...) Array + #define DictionaryOf(...) Dictionary +#endif // Basic types typedef struct { @@ -38,15 +36,6 @@ REMOTE_TYPE(Tabpage); typedef struct object Object; -TYPED_ARRAY_OF(Buffer); -TYPED_ARRAY_OF(Window); -TYPED_ARRAY_OF(Tabpage); -TYPED_ARRAY_OF(String); - -typedef struct { - Integer row, col; -} Position; - typedef struct { Object *items; size_t size, capacity; @@ -60,40 +49,30 @@ typedef struct { } Dictionary; typedef enum { + kObjectTypeBuffer, + kObjectTypeWindow, + kObjectTypeTabpage, kObjectTypeNil, kObjectTypeBoolean, kObjectTypeInteger, kObjectTypeFloat, kObjectTypeString, - kObjectTypeBuffer, - kObjectTypeWindow, - kObjectTypeTabpage, kObjectTypeArray, kObjectTypeDictionary, - kObjectTypePosition, - kObjectTypeStringArray, - kObjectTypeBufferArray, - kObjectTypeWindowArray, - kObjectTypeTabpageArray, } ObjectType; struct object { ObjectType type; union { + Buffer buffer; + Window window; + Tabpage tabpage; Boolean boolean; Integer integer; Float floating; String string; - Buffer buffer; - Window window; - Tabpage tabpage; Array array; Dictionary dictionary; - Position position; - StringArray stringarray; - BufferArray bufferarray; - WindowArray windowarray; - TabpageArray tabpagearray; } data; }; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f6fb46e1d1..14a820aa1b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -6,6 +6,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/handle.h" +#include "nvim/os/provider.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -449,6 +450,130 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) return true; } +void api_free_string(String value) +{ + if (!value.data) { + return; + } + + free(value.data); +} + +void api_free_object(Object value) +{ + switch (value.type) { + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + case kObjectTypeBuffer: + case kObjectTypeWindow: + case kObjectTypeTabpage: + break; + + case kObjectTypeString: + api_free_string(value.data.string); + break; + + case kObjectTypeArray: + api_free_array(value.data.array); + break; + + case kObjectTypeDictionary: + api_free_dictionary(value.data.dictionary); + break; + + default: + abort(); + } +} + +void api_free_array(Array value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_object(value.items[i]); + } + + free(value.items); +} + +void api_free_dictionary(Dictionary value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_string(value.items[i].key); + api_free_object(value.items[i].value); + } + + free(value.items); +} + +Dictionary api_metadata(void) +{ + static Dictionary metadata = ARRAY_DICT_INIT; + + if (!metadata.size) { + msgpack_rpc_init_function_metadata(&metadata); + init_type_metadata(&metadata); + provider_init_feature_metadata(&metadata); + } + + return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; +} + +static void init_type_metadata(Dictionary *metadata) +{ + Dictionary types = ARRAY_DICT_INIT; + + Dictionary buffer_metadata = ARRAY_DICT_INIT; + PUT(buffer_metadata, "id", INTEGER_OBJ(kObjectTypeBuffer)); + + Dictionary window_metadata = ARRAY_DICT_INIT; + PUT(window_metadata, "id", INTEGER_OBJ(kObjectTypeWindow)); + + Dictionary tabpage_metadata = ARRAY_DICT_INIT; + PUT(tabpage_metadata, "id", INTEGER_OBJ(kObjectTypeTabpage)); + + PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata)); + PUT(types, "Window", DICTIONARY_OBJ(window_metadata)); + PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata)); + + PUT(*metadata, "types", DICTIONARY_OBJ(types)); +} + +/// Creates a deep clone of an object +static Object copy_object(Object obj) +{ + switch (obj.type) { + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + return obj; + + case kObjectTypeString: + return STRING_OBJ(cstr_to_string(obj.data.string.data)); + + case kObjectTypeArray: { + Array rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.array.size; i++) { + ADD(rv, copy_object(obj.data.array.items[i])); + } + return ARRAY_OBJ(rv); + } + + case kObjectTypeDictionary: { + Dictionary rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.dictionary.size; i++) { + KeyValuePair item = obj.data.dictionary.items[i]; + PUT(rv, item.key.data, copy_object(item.value)); + } + return DICTIONARY_OBJ(rv); + } + default: + abort(); + } +} + /// Recursion helper for the `vim_to_object`. This uses a pointer table /// to avoid infinite recursion due to cyclic references /// diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index f1b9dc3bc8..f3ecdaacc4 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -51,36 +51,11 @@ .data.array = a \ }) -#define STRINGARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeStringArray, \ - .data.stringarray = a \ - }) - -#define BUFFERARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeBufferArray, \ - .data.bufferarray = a \ - }) - -#define WINDOWARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeWindowArray, \ - .data.windowarray = a \ - }) - -#define TABPAGEARRAY_OBJ(a) ((Object) { \ - .type = kObjectTypeTabpageArray, \ - .data.tabpagearray = a \ - }) - #define DICTIONARY_OBJ(d) ((Object) { \ .type = kObjectTypeDictionary, \ .data.dictionary = d \ }) -#define POSITION_OBJ(p) ((Object) { \ - .type = kObjectTypePosition, \ - .data.position = p \ - }) - #define NIL ((Object) {.type = kObjectTypeNil}) #define PUT(dict, k, v) \ @@ -91,6 +66,25 @@ #define ADD(array, item) \ kv_push(Object, array, item) +// Helpers used by the generated msgpack-rpc api wrappers +#define api_init_boolean +#define api_init_integer +#define api_init_float +#define api_init_string = STRING_INIT +#define api_init_buffer +#define api_init_window +#define api_init_tabpage +#define api_init_object = NIL +#define api_init_array = ARRAY_DICT_INIT +#define api_init_dictionary = ARRAY_DICT_INIT + +#define api_free_boolean(value) +#define api_free_integer(value) +#define api_free_float(value) +#define api_free_buffer(value) +#define api_free_window(value) +#define api_free_tabpage(value) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" #endif diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 535722c087..91020d6850 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -13,9 +13,9 @@ /// @param tabpage The tabpage /// @param[out] err Details of an error that may have occurred /// @return The number of windows in `tabpage` -WindowArray tabpage_get_windows(Tabpage tabpage, Error *err) +ArrayOf(Window) tabpage_get_windows(Tabpage tabpage, Error *err) { - WindowArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tab = find_tab_by_handle(tabpage, err); if (!tab) { @@ -32,14 +32,14 @@ WindowArray tabpage_get_windows(Tabpage tabpage, Error *err) rv.size++; } - rv.items = xmalloc(sizeof(Window) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { if (tp != tab) { break; } - rv.items[i++] = wp->handle; + rv.items[i++] = WINDOW_OBJ(wp->handle); } return rv; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a2c50b4c81..43f2aafdc8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -149,9 +149,9 @@ Integer vim_strwidth(String str, Error *err) /// Returns a list of paths contained in 'runtimepath' /// /// @return The list of paths -StringArray vim_list_runtime_paths(void) +ArrayOf(String) vim_list_runtime_paths(void) { - StringArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; uint8_t *rtp = p_rtp; if (*rtp == NUL) { @@ -168,19 +168,20 @@ StringArray vim_list_runtime_paths(void) } // Allocate memory for the copies - rv.items = xmalloc(sizeof(String) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); // reset the position rtp = p_rtp; // Start copying for (size_t i = 0; i < rv.size && *rtp != NUL; i++) { - rv.items[i].data = xmalloc(MAXPATHL); + rv.items[i].type = kObjectTypeString; + rv.items[i].data.string.data = xmalloc(MAXPATHL); // Copy the path from 'runtimepath' to rv.items[i] int length = copy_option_part(&rtp, - (char_u *)rv.items[i].data, + (char_u *)rv.items[i].data.string.data, MAXPATHL, ","); assert(length >= 0); - rv.items[i].size = (size_t)length; + rv.items[i].data.string.size = (size_t)length; } return rv; @@ -307,12 +308,22 @@ void vim_err_write(String str) write_msg(str, true); } +/// Higher level error reporting function that ensures all str contents +/// are written by sending a trailing linefeed to `vim_wrr_write` +/// +/// @param str The message +void vim_report_error(String str) +{ + vim_err_write(str); + vim_err_write((String) {.data = "\n", .size = 1}); +} + /// Gets the current list of buffer handles /// /// @return The number of buffers -BufferArray vim_get_buffers(void) +ArrayOf(Buffer) vim_get_buffers(void) { - BufferArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; buf_T *b = firstbuf; while (b) { @@ -320,12 +331,12 @@ BufferArray vim_get_buffers(void) b = b->b_next; } - rv.items = xmalloc(sizeof(Buffer) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; b = firstbuf; while (b) { - rv.items[i++] = b->handle; + rv.items[i++] = BUFFER_OBJ(b->handle); b = b->b_next; } @@ -370,9 +381,9 @@ void vim_set_current_buffer(Buffer buffer, Error *err) /// Gets the current list of window handles /// /// @return The number of windows -WindowArray vim_get_windows(void) +ArrayOf(Window) vim_get_windows(void) { - WindowArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tp; win_T *wp; @@ -380,11 +391,11 @@ WindowArray vim_get_windows(void) rv.size++; } - rv.items = xmalloc(sizeof(Window) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; FOR_ALL_TAB_WINDOWS(tp, wp) { - rv.items[i++] = wp->handle; + rv.items[i++] = WINDOW_OBJ(wp->handle); } return rv; @@ -426,9 +437,9 @@ void vim_set_current_window(Window window, Error *err) /// Gets the current list of tabpage handles /// /// @return The number of tab pages -TabpageArray vim_get_tabpages(void) +ArrayOf(Tabpage) vim_get_tabpages(void) { - TabpageArray rv = ARRAY_DICT_INIT; + Array rv = ARRAY_DICT_INIT; tabpage_T *tp = first_tabpage; while (tp) { @@ -436,12 +447,12 @@ TabpageArray vim_get_tabpages(void) tp = tp->tp_next; } - rv.items = xmalloc(sizeof(Tabpage) * rv.size); + rv.items = xmalloc(sizeof(Object) * rv.size); size_t i = 0; tp = first_tabpage; while (tp) { - rv.items[i++] = tp->handle; + rv.items[i++] = TABPAGE_OBJ(tp->handle); tp = tp->tp_next; } @@ -501,22 +512,33 @@ void vim_unsubscribe(uint64_t channel_id, String event) channel_unsubscribe(channel_id, e); } -/// Registers the channel as the provider for `method`. This fails if -/// a provider for `method` is already registered. +/// Registers the channel as the provider for `feature`. This fails if +/// a provider for `feature` is already provided by another channel. /// /// @param channel_id The channel id -/// @param method The method name +/// @param feature The feature name /// @param[out] err Details of an error that may have occurred -void vim_register_provider(uint64_t channel_id, String method, Error *err) +void vim_register_provider(uint64_t channel_id, String feature, Error *err) { char buf[METHOD_MAXLEN]; - xstrlcpy(buf, method.data, sizeof(buf)); + xstrlcpy(buf, feature.data, sizeof(buf)); if (!provider_register(buf, channel_id)) { - set_api_error("Provider already registered", err); + set_api_error("Feature doesn't exist", err); } } +Array vim_get_api_info(uint64_t channel_id) +{ + Array rv = ARRAY_DICT_INIT; + + assert(channel_id <= INT64_MAX); + ADD(rv, INTEGER_OBJ((int64_t)channel_id)); + ADD(rv, DICTIONARY_OBJ(api_metadata())); + + return rv; +} + /// Writes a message to vim output or error buffer. The string is split /// and flushed after each newline. Incomplete lines are kept for writing /// later. diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 1ab441bed3..dd256f2b6d 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -33,14 +33,14 @@ Buffer window_get_buffer(Window window, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return the (row, col) tuple -Position window_get_cursor(Window window, Error *err) +ArrayOf(Integer, 2) window_get_cursor(Window window, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - rv.row = win->w_cursor.lnum; - rv.col = win->w_cursor.col; + ADD(rv, INTEGER_OBJ(win->w_cursor.lnum)); + ADD(rv, INTEGER_OBJ(win->w_cursor.col)); } return rv; @@ -51,31 +51,35 @@ Position window_get_cursor(Window window, Error *err) /// @param window The window handle /// @param pos the (row, col) tuple representing the new position /// @param[out] err Details of an error that may have occurred -void window_set_cursor(Window window, Position pos, Error *err) +void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) { win_T *win = find_window_by_handle(window, err); - if (!win) { + if (pos.size != 2 || pos.items[0].type != kObjectTypeInteger || + pos.items[1].type != kObjectTypeInteger) { + set_api_error("\"pos\" argument must be a [row, col] array", err); return; } - if (pos.row <= 0 || pos.row > win->w_buffer->b_ml.ml_line_count) { - set_api_error("cursor position outside buffer", err); + if (!win) { return; } - if (pos.row > LONG_MAX || pos.row < LONG_MIN) { - set_api_error("Row value outside range", err); + int64_t row = pos.items[0].data.integer; + int64_t col = pos.items[1].data.integer; + + if (row <= 0 || row > win->w_buffer->b_ml.ml_line_count) { + set_api_error("cursor position outside buffer", err); return; } - if (pos.col > INT_MAX || pos.col < INT_MIN) { + if (col > MAXCOL || col < 0) { set_api_error("Column value outside range", err); return; } - win->w_cursor.lnum = (linenr_T)pos.row; - win->w_cursor.col = (colnr_T)pos.col; + win->w_cursor.lnum = (linenr_T)row; + win->w_cursor.col = (colnr_T)col; win->w_cursor.coladd = 0; // When column is out of range silently correct it. check_cursor_col_win(win); @@ -243,14 +247,14 @@ void window_set_option(Window window, String name, Object value, Error *err) /// @param window The window handle /// @param[out] err Details of an error that may have occurred /// @return The (row, col) tuple with the window position -Position window_get_position(Window window, Error *err) +ArrayOf(Integer, 2) window_get_position(Window window, Error *err) { - Position rv = POSITION_INIT; + Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - rv.col = win->w_wincol; - rv.row = win->w_winrow; + ADD(rv, INTEGER_OBJ(win->w_winrow)); + ADD(rv, INTEGER_OBJ(win->w_wincol)); } return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 77bed67d5f..11171617ef 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1314,7 +1314,7 @@ buflist_new ( * for hard links. */ FileID file_id; bool file_id_valid = (sfname != NULL && - os_get_file_id((char *)sfname, &file_id)); + os_fileid((char *)sfname, &file_id)); if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) { @@ -1671,7 +1671,7 @@ buf_T *buflist_findname_exp(char_u *fname) buf_T *buflist_findname(char_u *ffname) { FileID file_id; - bool file_id_valid = os_get_file_id((char *)ffname, &file_id); + bool file_id_valid = os_fileid((char *)ffname, &file_id); return buflist_findname_file_id(ffname, &file_id, file_id_valid); } @@ -1763,13 +1763,16 @@ buflist_findpat ( if (curtab_only) { /* Ignore the match if the buffer is not open in * the current tab. */ - win_T *wp; - - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) + bool found_window = false; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == buf) { + found_window = true; break; - if (wp == NULL) + } + } + if (!found_window) { continue; + } } if (match >= 0) { /* already found a match */ match = -2; @@ -2020,22 +2023,22 @@ static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, /* - * Return TRUE when "wip" has 'diff' set and the diff is only for another tab + * Return true when "wip" has 'diff' set and the diff is only for another tab * page. That's because a diff is local to a tab page. */ -static int wininfo_other_tab_diff(wininfo_T *wip) +static bool wininfo_other_tab_diff(wininfo_T *wip) { - win_T *wp; - if (wip->wi_opt.wo_diff) { - for (wp = firstwin; wp != NULL; wp = wp->w_next) - /* return FALSE when it's a window in the current tab page, thus + FOR_ALL_WINDOWS(wp) { + /* return false when it's a window in the current tab page, thus * the buffer was in diff mode here */ - if (wip->wi_win == wp) - return FALSE; - return TRUE; + if (wip->wi_win == wp) { + return false; + } + } + return true; } - return FALSE; + return false; } /* @@ -2221,7 +2224,7 @@ setfname ( * - if the buffer is loaded, fail * - if the buffer is not loaded, delete it from the list */ - file_id_valid = os_get_file_id((char *)ffname, &file_id); + file_id_valid = os_fileid((char *)ffname, &file_id); if (!(buf->b_flags & BF_DUMMY)) { obuf = buflist_findname_file_id(ffname, &file_id, file_id_valid); } @@ -2399,7 +2402,7 @@ static int otherfile_buf(buf_T *buf, char_u *ffname, /* If no struct stat given, get it now */ if (file_id_p == NULL) { file_id_p = &file_id; - file_id_valid = os_get_file_id((char *)ffname, file_id_p); + file_id_valid = os_fileid((char *)ffname, file_id_p); } if (!file_id_valid) { // file_id not valid, assume files are different. @@ -2429,7 +2432,7 @@ void buf_set_file_id(buf_T *buf) { FileID file_id; if (buf->b_fname != NULL - && os_get_file_id((char *)buf->b_fname, &file_id)) { + && os_fileid((char *)buf->b_fname, &file_id)) { buf->file_id_valid = true; buf->file_id = file_id; } else { @@ -2441,7 +2444,7 @@ void buf_set_file_id(buf_T *buf) static bool buf_same_file_id(buf_T *buf, FileID *file_id) { return buf->file_id_valid - && os_file_id_equal(&(buf->file_id), file_id); + && os_fileid_equal(&(buf->file_id), file_id); } /* @@ -3601,7 +3604,6 @@ do_arg_all ( ) { int i; - win_T *wp, *wpnext; char_u *opened; /* Array of weight for which args are open: * 0: not opened * 1: opened in other tab @@ -3651,8 +3653,9 @@ do_arg_all ( if (had_tab > 0) goto_tabpage_tp(first_tabpage, TRUE, TRUE); for (;; ) { + win_T *wpnext = NULL; tpnext = curtab->tp_next; - for (wp = firstwin; wp != NULL; wp = wpnext) { + for (win_T *wp = firstwin; wp != NULL; wp = wpnext) { wpnext = wp->w_next; buf = wp->w_buffer; if (buf->b_ffname == NULL @@ -3762,13 +3765,14 @@ do_arg_all ( if (opened[i] > 0) { /* Move the already present window to below the current window */ if (curwin->w_arg_idx != i) { - for (wpnext = firstwin; wpnext != NULL; wpnext = wpnext->w_next) { - if (wpnext->w_arg_idx == i) { + FOR_ALL_WINDOWS(wp) { + if (wp->w_arg_idx == i) { if (keep_tabs) { - new_curwin = wpnext; + new_curwin = wp; new_curtab = curtab; - } else - win_move_after(wpnext, curwin); + } else { + win_move_after(wp, curwin); + } break; } } @@ -4421,8 +4425,8 @@ linenr_T buf_delsign( } /* When deleted the last sign needs to redraw the windows to remove the - * sign column. */ - if (buf->b_signlist == NULL) { + * sign column. Not when curwin is NULL (this means we're exiting). */ + if (buf->b_signlist != NULL && curwin != NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 4162df63ab..84d55fb730 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -255,6 +255,7 @@ struct wininfo_S { typedef struct arglist { garray_T al_ga; /* growarray with the array of file names */ int al_refcount; /* number of windows using this arglist */ + int id; ///< id of this arglist } alist_T; /* @@ -512,7 +513,7 @@ struct file_buffer { long b_mtime; /* last change time of original file */ long b_mtime_read; /* last change time when reading */ - off_t b_orig_size; /* size of original file in bytes */ + uint64_t b_orig_size; /* size of original file in bytes */ int b_orig_mode; /* mode of original file */ pos_T b_namedm[NMARKS]; /* current named marks (mark.c) */ @@ -880,6 +881,28 @@ typedef struct { proftime_T tm; /* for a time limit */ } match_T; +/// number of positions supported by matchaddpos() +#define MAXPOSMATCH 8 + +/// Same as lpos_T, but with additional field len. +typedef struct +{ + linenr_T lnum; ///< line number + colnr_T col; ///< column number + int len; ///< length: 0 - to the end of line +} llpos_T; + +/// posmatch_T provides an array for storing match items for matchaddpos() +/// function. +typedef struct posmatch posmatch_T; +struct posmatch +{ + llpos_T pos[MAXPOSMATCH]; ///< array of positions + int cur; ///< internal position counter + linenr_T toplnum; ///< top buffer line + linenr_T botlnum; ///< bottom buffer line +}; + /* * matchitem_T provides a linked list for storing match items for ":match" and * the match functions. @@ -892,6 +915,7 @@ struct matchitem { char_u *pattern; /* pattern to highlight */ int hlg_id; /* highlight group ID */ regmmatch_T match; /* regexp program for pattern */ + posmatch_T pos; // position matches match_T hl; /* struct for doing the actual highlighting */ }; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index ab0c80112f..8f540fbe09 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -87,14 +87,14 @@ void diff_buf_adjust(win_T *win) if (!win->w_p_diff) { // When there is no window showing a diff for this buffer, remove // it from the diffs. - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + bool found_win = false; + FOR_ALL_WINDOWS(wp) { if ((wp->w_buffer == win->w_buffer) && wp->w_p_diff) { - break; + found_win = true; } } - if (wp == NULL) { + if (!found_win) { int i = diff_buf_idx(win->w_buffer); if (i != DB_COUNT) { curtab->tp_diffbuf[i] = NULL; @@ -581,8 +581,7 @@ static int diff_check_sanity(tabpage_T *tp, diff_T *dp) /// @param dofold Also recompute the folds static void diff_redraw(int dofold) { - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { if (wp->w_p_diff) { redraw_win_later(wp, SOME_VALID); if (dofold && foldmethodIsDiff(wp)) { @@ -947,9 +946,10 @@ void ex_diffpatch(exarg_T *eap) os_remove((char *)buf); // Only continue if the output file was created. - off_t file_size; - bool file_size_success = os_get_file_size((char *)tmp_new, &file_size); - if (!file_size_success || file_size == 0) { + FileInfo file_info; + bool info_ok = os_fileinfo((char *)tmp_new, &file_info); + uint64_t filesize = os_fileinfo_size(&file_info); + if (!info_ok || filesize == 0) { EMSG(_("E816: Cannot read patch output")); } else { if (curbuf->b_fname != NULL) { @@ -1110,8 +1110,7 @@ void ex_diffoff(exarg_T *eap) win_T *old_curwin = curwin; int diffwin = FALSE; - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { // Set 'diff', 'scrollbind' off and 'wrap' on. If option values // were saved in diff_win_options() restore them. @@ -2363,10 +2362,8 @@ void ex_diffgetput(exarg_T *eap) /// @param skip_idx static void diff_fold_update(diff_T *dp, int skip_idx) { - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - int i; - for (i = 0; i < DB_COUNT; ++i) { + FOR_ALL_WINDOWS(wp) { + for (int i = 0; i < DB_COUNT; ++i) { if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) { foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 6158176e56..b3f4e4d449 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3798,6 +3798,8 @@ static void ins_compl_delete(void) */ i = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); backspace_until_column(i); + // TODO: is this sufficient for redrawing? Redrawing everything causes + // flicker, thus we can't do that. changed_cline_bef_curs(); } @@ -6917,8 +6919,9 @@ ins_esc ( State &= ~REPLACE_FLAG; (void)start_redo_ins(); - if (cmdchar == 'r' || cmdchar == 'v') - stuffReadbuff(ESC_STR); /* no ESC in redo buffer */ + if (cmdchar == 'r' || cmdchar == 'v') { + stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer + } ++RedrawingDisabled; disabled_redraw = TRUE; return FALSE; /* repeat the insert */ diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 34af143446..7793f5040c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -84,7 +84,6 @@ #include "nvim/os/channel.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/os/dl.h" #include "nvim/os/provider.h" @@ -4758,7 +4757,7 @@ void listitem_free(listitem_T *item) */ void listitem_remove(list_T *l, listitem_T *item) { - list_remove(l, item, item); + vim_list_remove(l, item, item); listitem_free(item); } @@ -5231,30 +5230,29 @@ static list_T *list_copy(list_T *orig, int deep, int copyID) return copy; } -/* - * Remove items "item" to "item2" from list "l". - * Does not free the listitem or the value! - */ -void list_remove(list_T *l, listitem_T *item, listitem_T *item2) +/// Remove items "item" to "item2" from list "l". +/// @warning Does not free the listitem or the value! +void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) { - listitem_T *ip; - - /* notify watchers */ - for (ip = item; ip != NULL; ip = ip->li_next) { + // notify watchers + for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { --l->lv_len; list_fix_watch(l, ip); - if (ip == item2) + if (ip == item2) { break; + } } - if (item2->li_next == NULL) + if (item2->li_next == NULL) { l->lv_last = item->li_prev; - else + } else { item2->li_next->li_prev = item->li_prev; - if (item->li_prev == NULL) + } + if (item->li_prev == NULL) { l->lv_first = item2->li_next; - else + } else { item->li_prev->li_next = item2->li_next; + } l->lv_idx_item = NULL; } @@ -6311,6 +6309,7 @@ static struct fst { {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, {"argidx", 0, 0, f_argidx}, + {"arglistid", 0, 2, f_arglistid}, {"argv", 0, 1, f_argv}, {"asin", 1, 1, f_asin}, /* WJMc */ {"atan", 1, 1, f_atan}, @@ -6436,9 +6435,9 @@ static struct fst { {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, {"items", 1, 1, f_items}, - {"jobstart", 2, 3, f_job_start}, - {"jobstop", 1, 1, f_job_stop}, - {"jobwrite", 2, 2, f_job_write}, + {"jobsend", 2, 2, f_jobsend}, + {"jobstart", 2, 3, f_jobstart}, + {"jobstop", 1, 1, f_jobstop}, {"join", 1, 2, f_join}, {"keys", 1, 1, f_keys}, {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */ @@ -6456,6 +6455,7 @@ static struct fst { {"mapcheck", 1, 3, f_mapcheck}, {"match", 2, 4, f_match}, {"matchadd", 2, 4, f_matchadd}, + {"matchaddpos", 2, 4, f_matchaddpos}, {"matcharg", 1, 1, f_matcharg}, {"matchdelete", 1, 1, f_matchdelete}, {"matchend", 2, 4, f_matchend}, @@ -6484,6 +6484,10 @@ static struct fst { {"resolve", 1, 1, f_resolve}, {"reverse", 1, 1, f_reverse}, {"round", 1, 1, f_round}, + {"rpcnotify", 2, 64, f_rpcnotify}, + {"rpcrequest", 2, 64, f_rpcrequest}, + {"rpcstart", 1, 2, f_rpcstart}, + {"rpcstop", 1, 1, f_rpcstop}, {"screenattr", 2, 2, f_screenattr}, {"screenchar", 2, 2, f_screenchar}, {"screencol", 0, 0, f_screencol}, @@ -6493,8 +6497,6 @@ static struct fst { {"searchpair", 3, 7, f_searchpair}, {"searchpairpos", 3, 7, f_searchpairpos}, {"searchpos", 1, 4, f_searchpos}, - {"send_call", 2, 64, f_send_call}, - {"send_event", 2, 64, f_send_event}, {"setbufvar", 3, 3, f_setbufvar}, {"setcmdpos", 1, 1, f_setcmdpos}, {"setline", 2, 2, f_setline}, @@ -7125,6 +7127,32 @@ static void f_argidx(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = curwin->w_arg_idx; } +/// "arglistid" function +static void f_arglistid(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_UNKNOWN) { + tabpage_T *tp = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) { + long n = get_tv_number(&argvars[1]); + if (n >= 0) { + tp = find_tabpage(n); + } + } else { + tp = curtab; + } + + if (tp != NULL) { + win_T *wp = find_win_by_nr(&argvars[0], tp); + if (wp != NULL) { + rettv->vval.v_number = wp->w_alist->id; + } + } + } else { + rettv->vval.v_number = curwin->w_alist->id; + } +} + /* * "argv(nr)" function */ @@ -7360,19 +7388,20 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv) */ static void f_bufwinnr(typval_T *argvars, typval_T *rettv) { - win_T *wp; - int winnr = 0; - buf_T *buf; - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ ++emsg_off; - buf = get_buf_tv(&argvars[0], TRUE); - for (wp = firstwin; wp; wp = wp->w_next) { + + buf_T *buf = get_buf_tv(&argvars[0], TRUE); + int winnr = 0; + bool found_buf = false; + FOR_ALL_WINDOWS(wp) { ++winnr; - if (wp->w_buffer == buf) + if (wp->w_buffer == buf) { + found_buf = true; break; + } } - rettv->vval.v_number = (wp != NULL ? winnr : -1); + rettv->vval.v_number = (found_buf ? winnr : -1); --emsg_off; } @@ -9165,15 +9194,16 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_NUMBER; - off_t file_size; - if (os_get_file_size(fname, &file_size)) { + FileInfo file_info; + if (os_fileinfo(fname, &file_info)) { + uint64_t filesize = os_fileinfo_size(&file_info); if (os_isdir((char_u *)fname)) rettv->vval.v_number = 0; else { - rettv->vval.v_number = (varnumber_T)file_size; + rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ - if ((off_t)rettv->vval.v_number != file_size) { + if ((uint64_t)rettv->vval.v_number != filesize) { rettv->vval.v_number = -2; } } @@ -9190,7 +9220,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv) char *fname = (char *)get_tv_string(&argvars[0]); FileInfo file_info; - if (os_get_file_info(fname, &file_info)) { + if (os_fileinfo(fname, &file_info)) { rettv->vval.v_number = (varnumber_T)file_info.stat.st_mtim.tv_sec; } else { rettv->vval.v_number = -1; @@ -9210,7 +9240,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_get_file_info_link((char *)fname, &file_info)) { + if (os_fileinfo_link((char *)fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -9300,12 +9330,34 @@ static void f_getline(typval_T *argvars, typval_T *rettv) static void f_getmatches(typval_T *argvars, typval_T *rettv) { matchitem_T *cur = curwin->w_match_head; + int i; rettv_list_alloc(rettv); while (cur != NULL) { dict_T *dict = dict_alloc(); + if (cur->match.regprog == NULL) { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; ++i) { + llpos_T *llpos; + char buf[6]; + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) { + break; + } + list_T *l = list_alloc(); + list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) { + list_append_number(l, (varnumber_T)llpos->col); + list_append_number(l, (varnumber_T)llpos->len); + } + sprintf(buf, "pos%d", i + 1); + dict_add_list(dict, buf, l); + } + } else { + dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + } dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); dict_add_nr_str(dict, "id", (long)cur->id, NULL); list_append_dict(rettv->vval.v_list, dict); @@ -9790,7 +9842,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "windows", "winaltkeys", "writebackup", - "neovim", + "nvim", NULL }; @@ -10456,8 +10508,40 @@ static void f_items(typval_T *argvars, typval_T *rettv) dict_list(argvars, rettv, 2); } +// "jobsend()" function +static void f_jobsend(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { + // First argument is the job id and second is the string to write to + // the job's stdin + EMSG(_(e_invarg)); + return; + } + + Job *job = job_find(argvars[0].vval.v_number); + + if (!job) { + // Invalid job id + EMSG(_(e_invjob)); + return; + } + + WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), + strlen((char *)argvars[1].vval.v_string), + 1, + free); + rettv->vval.v_number = job_write(job, buf); +} + // "jobstart()" function -static void f_job_start(typval_T *argvars, typval_T *rettv) +static void f_jobstart(typval_T *argvars, typval_T *rettv) { list_T *args = NULL; listitem_T *arg; @@ -10535,7 +10619,7 @@ static void f_job_start(typval_T *argvars, typval_T *rettv) } // "jobstop()" function -static void f_job_stop(typval_T *argvars, typval_T *rettv) +static void f_jobstop(typval_T *argvars, typval_T *rettv) { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; @@ -10562,38 +10646,6 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; } -// "jobwrite()" function -static void f_job_write(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { - // First argument is the job id and second is the string to write to - // the job's stdin - EMSG(_(e_invarg)); - return; - } - - Job *job = job_find(argvars[0].vval.v_number); - - if (!job) { - // Invalid job id - EMSG(_(e_invjob)); - return; - } - - WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string), - strlen((char *)argvars[1].vval.v_string), - 1, - free); - rettv->vval.v_number = job_write(job, buf); -} - /* * "join()" function */ @@ -11104,7 +11156,52 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL); +} + +static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL +{ + rettv->vval.v_number = -1; + + char_u buf[NUMBUFLEN]; + char_u *group; + group = get_tv_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } + + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } + + int error = false; + int prio = 10; + int id = -1; + + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = get_tv_number_chk(&argvars[3], &error); + } + } + if (error == true) { + return; + } + + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + EMSGN("E798: ID is reserved for \"match\": %" PRId64, id); + return; + } + + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l); } /* @@ -11832,8 +11929,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv) EMSGN(_(e_listidx), idx); else { if (argvars[2].v_type == VAR_UNKNOWN) { - /* Remove one item, return its value. */ - list_remove(l, item, item); + // Remove one item, return its value. + vim_list_remove(l, item, item); *rettv = item->li_tv; free(item); } else { @@ -11854,7 +11951,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv) if (li == NULL) /* didn't find "item2" after "item" */ EMSG(_(e_invrange)); else { - list_remove(l, item, item2); + vim_list_remove(l, item, item2); rettv_list_alloc(rettv); l = rettv->vval.v_list; l->lv_first = item; @@ -12273,6 +12370,169 @@ static void f_round(typval_T *argvars, typval_T *rettv) rettv->vval.v_float = 0.0; } +// "rpcnotify()" function +static void f_rpcnotify(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Event type must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + if (!channel_send_event((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + rettv->vval.v_number = 1; +} + +// "rpcrequest()" function +static void f_rpcrequest(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { + EMSG2(_(e_invarg2), "Channel id must be a positive integer"); + return; + } + + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "Method name must be a string"); + return; + } + + Array args = ARRAY_DICT_INIT; + + for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { + ADD(args, vim_to_object(tv)); + } + + bool errored; + Object result; + if (!channel_send_call((uint64_t)argvars[0].vval.v_number, + (char *)argvars[1].vval.v_string, + args, + &result, + &errored)) { + EMSG2(_(e_invarg2), "Channel doesn't exist"); + return; + } + + if (errored) { + vim_report_error(result.data.string); + goto end; + } + + Error conversion_error = {.set = false}; + if (!object_to_vim(result, rettv, &conversion_error)) { + EMSG(_("Error converting the call result")); + } + +end: + api_free_object(result); +} + +// "rpcstart()" function +static void f_rpcstart(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_STRING + || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + list_T *args = NULL; + int argsl = 0; + if (argvars[1].v_type == VAR_LIST) { + args = argvars[1].vval.v_list; + argsl = args->lv_len; + // Assert that all list items are strings + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + if (arg->li_tv.v_type != VAR_STRING) { + EMSG(_(e_invarg)); + return; + } + } + } + + // Allocate extra memory for the argument vector and the NULL pointer + int argvl = argsl + 2; + char **argv = xmalloc(sizeof(char_u *) * argvl); + + // Copy program name + argv[0] = xstrdup((char *)argvars[0].vval.v_string); + + int i = 1; + // Copy arguments to the vector + if (argsl > 0) { + for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { + argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); + } + } + + // The last item of argv must be NULL + argv[i] = NULL; + uint64_t channel_id = channel_from_job(argv); + + if (!channel_id) { + EMSG(_(e_api_spawn_failed)); + } + + rettv->vval.v_number = (varnumber_T)channel_id; +} + +// "rpcstop()" function +static void f_rpcstop(typval_T *argvars, typval_T *rettv) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER) { + // Wrong argument types + EMSG(_(e_invarg)); + return; + } + + rettv->vval.v_number = channel_close(argvars[0].vval.v_number); +} + /* * "screenattr()" function */ @@ -12612,89 +12872,6 @@ do_searchpair ( return retval; } -// "send_call()" function -static void f_send_call(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number <= 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Method name must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - bool errored; - Object result; - if (!channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args, - &result, - &errored)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - Error conversion_error = {.set = false}; - if (errored || !object_to_vim(result, rettv, &conversion_error)) { - EMSG(errored ? - result.data.string.data : - _("Error converting the call result")); - } - - msgpack_rpc_free_object(result); -} - -// "send_event()" function -static void f_send_event(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[0].vval.v_number < 0) { - EMSG2(_(e_invarg2), "Channel id must be a positive integer"); - return; - } - - if (argvars[1].v_type != VAR_STRING) { - EMSG2(_(e_invarg2), "Event type must be a string"); - return; - } - - Array args = ARRAY_DICT_INIT; - - for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); - } - - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)argvars[1].vval.v_string, - args)) { - EMSG2(_(e_invarg2), "Channel doesn't exist"); - return; - } - - rettv->vval.v_number = 1; -} - /* * "searchpos()" function */ @@ -12926,7 +13103,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE), get_dict_string(d, (char_u *)"pattern", FALSE), (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id")); + (int)get_dict_number(d, (char_u *)"id"), NULL); li = li->li_next; } rettv->vval.v_number = 0; @@ -13209,11 +13386,18 @@ static void f_sinh(typval_T *argvars, typval_T *rettv) rettv->vval.v_float = 0.0; } +/// struct used in the array that's given to qsort() +typedef struct { + listitem_T *item; + int idx; +} sortItem_T; static int item_compare_ic; +static bool item_compare_numeric; static char_u *item_compare_func; static dict_T *item_compare_selfdict; static int item_compare_func_err; +static bool item_compare_keep_zero; #define ITEM_COMPARE_FAIL 999 /* @@ -13221,22 +13405,40 @@ static int item_compare_func_err; */ static int item_compare(const void *s1, const void *s2) { + sortItem_T *si1, *si2; char_u *p1, *p2; char_u *tofree1, *tofree2; int res; char_u numbuf1[NUMBUFLEN]; char_u numbuf2[NUMBUFLEN]; - p1 = tv2string(&(*(listitem_T **)s1)->li_tv, &tofree1, numbuf1, 0); - p2 = tv2string(&(*(listitem_T **)s2)->li_tv, &tofree2, numbuf2, 0); + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + p1 = tv2string(&si1->item->li_tv, &tofree1, numbuf1, 0); + p2 = tv2string(&si2->item->li_tv, &tofree2, numbuf2, 0); if (p1 == NULL) p1 = (char_u *)""; if (p2 == NULL) p2 = (char_u *)""; - if (item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); + if (!item_compare_numeric) { + if (item_compare_ic) { + res = STRICMP(p1, p2); + } else { + res = STRCMP(p1, p2); + } + } else { + double n1, n2; + n1 = strtod((char *)p1, (char **)&p1); + n2 = strtod((char *)p2, (char **)&p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + // When the result would be zero, compare the pointers themselves. Makes + // the sort stable. + if (res == 0 && !item_compare_keep_zero) { + res = si1->idx > si2->idx ? 1 : -1; + } + free(tofree1); free(tofree2); return res; @@ -13244,6 +13446,7 @@ static int item_compare(const void *s1, const void *s2) static int item_compare2(const void *s1, const void *s2) { + sortItem_T *si1, *si2; int res; typval_T rettv; typval_T argv[3]; @@ -13253,10 +13456,13 @@ static int item_compare2(const void *s1, const void *s2) if (item_compare_func_err) return 0; - /* copy the values. This is needed to be able to set v_lock to VAR_FIXED - * in the copy without changing the original list items. */ - copy_tv(&(*(listitem_T **)s1)->li_tv, &argv[0]); - copy_tv(&(*(listitem_T **)s2)->li_tv, &argv[1]); + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + + // Copy the values. This is needed to be able to set v_lock to VAR_FIXED + // in the copy without changing the original list items. + copy_tv(&si1->item->li_tv, &argv[0]); + copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ res = call_func(item_compare_func, (int)STRLEN(item_compare_func), @@ -13272,6 +13478,13 @@ static int item_compare2(const void *s1, const void *s2) if (item_compare_func_err) res = ITEM_COMPARE_FAIL; /* return value has wrong type */ clear_tv(&rettv); + + // When the result would be zero, compare the pointers themselves. Makes + // the sort stable. + if (res == 0 && !item_compare_keep_zero) { + res = si1->idx > si2->idx ? 1 : -1; + } + return res; } @@ -13282,7 +13495,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { list_T *l; listitem_T *li; - listitem_T **ptrs; + sortItem_T *ptrs; long len; long i; @@ -13303,6 +13516,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) return; /* short list sorts pretty quickly */ item_compare_ic = FALSE; + item_compare_numeric = false; item_compare_func = NULL; item_compare_selfdict = NULL; @@ -13320,6 +13534,15 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_ic = TRUE; else item_compare_func = get_tv_string(&argvars[1]); + if (item_compare_func != NULL) { + if (STRCMP(item_compare_func, "n") == 0) { + item_compare_func = NULL; + item_compare_numeric = true; + } else if (STRCMP(item_compare_func, "i") == 0) { + item_compare_func = NULL; + item_compare_ic = TRUE; + } + } } if (argvars[2].v_type != VAR_UNKNOWN) { @@ -13333,23 +13556,26 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (listitem_T *))); + ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); i = 0; if (sort) { // sort(): ptrs will be the list to sort. for (li = l->lv_first; li != NULL; li = li->li_next) { - ptrs[i++] = li; + ptrs[i].item = li; + ptrs[i].idx = i; + i++; } item_compare_func_err = FALSE; + item_compare_keep_zero = false; // Test the compare function. if (item_compare_func != NULL && item_compare2(&ptrs[0], &ptrs[1]) == ITEM_COMPARE_FAIL) { EMSG(_("E702: Sort compare function failed")); } else { // Sort the array with item pointers. - qsort(ptrs, (size_t)len, sizeof (listitem_T *), + qsort(ptrs, (size_t)len, sizeof (sortItem_T), item_compare_func == NULL ? item_compare : item_compare2); if (!item_compare_func_err) { @@ -13360,7 +13586,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i]); + list_append(l, ptrs[i].item); } } } @@ -13369,11 +13595,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) // f_uniq(): ptrs will be a stack of items to remove. item_compare_func_err = FALSE; + item_compare_keep_zero = true; item_compare_func_ptr = item_compare_func ? item_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { if (item_compare_func_ptr(&li, &li->li_next) == 0) { - ptrs[i++] = li; + ptrs[i++].item = li; } if (item_compare_func_err) { EMSG(_("E882: Uniq compare function failed")); @@ -13383,12 +13610,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (!item_compare_func_err) { while (--i >= 0) { - li = ptrs[i]->li_next; - ptrs[i]->li_next = li->li_next; + li = ptrs[i].item->li_next; + ptrs[i].item->li_next = li->li_next; if (li->li_next != NULL) { - li->li_next->li_prev = ptrs[i]; + li->li_next->li_prev = ptrs[i].item; } else { - l->lv_last = ptrs[i]; + l->lv_last = ptrs[i].item; } list_fix_watch(l, li); listitem_free(li); @@ -14681,13 +14908,12 @@ static void f_winnr(typval_T *argvars, typval_T *rettv) */ static void f_winrestcmd(typval_T *argvars, typval_T *rettv) { - win_T *wp; int winnr = 1; garray_T ga; char_u buf[50]; ga_init(&ga, (int)sizeof(char), 70); - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height); ga_concat(&ga, buf); sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); @@ -19171,7 +19397,7 @@ static void script_host_eval(char *method, typval_T *argvars, typval_T *rettv) Error err = {.set = false}; object_to_vim(result, rettv, &err); - msgpack_rpc_free_object(result); + api_free_object(result); if (err.set) { EMSG("Error converting value back to vim"); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5b6604fc93..b72d1941ec 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1533,7 +1533,7 @@ void write_viminfo(char_u *file, int forceit) */ FileInfo old_info; // FileInfo of existing viminfo file - if (os_get_file_info((char *)fname, &old_info) + if (os_fileinfo((char *)fname, &old_info) && getuid() != ROOT_UID && !(old_info.stat.st_uid == getuid() ? (old_info.stat.st_mode & 0200) @@ -4610,19 +4610,19 @@ prepare_tagpreview ( bool undo_sync /* sync undo when leaving the window */ ) { - win_T *wp; - - /* * If there is already a preview window open, use that one. */ if (!curwin->w_p_pvw) { - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_p_pvw) + bool found_win = false; + FOR_ALL_WINDOWS(wp) { + if (wp->w_p_pvw) { + win_enter(wp, undo_sync); + found_win = true; break; - if (wp != NULL) - win_enter(wp, undo_sync); - else { + } + } + if (!found_win) { /* * There is no preview window open yet. Create one. */ diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3809858875..c3d34e9991 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -55,7 +55,6 @@ #include "nvim/os/shell.h" #include "nvim/os/fs_defs.h" #include "nvim/os/provider.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" @@ -1206,7 +1205,6 @@ check_changed_any ( int bufcount = 0; int *bufnrs; tabpage_T *tp; - win_T *wp; FOR_ALL_BUFFERS(buf) { ++bufcount; @@ -1220,15 +1218,21 @@ check_changed_any ( /* curbuf */ bufnrs[bufnum++] = curbuf->b_fnum; /* buf in curtab */ - FOR_ALL_WINDOWS(wp) - if (wp->w_buffer != curbuf) - add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer != curbuf) { + add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); + } + } /* buf in other tab */ - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - if (tp != curtab) - for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) + for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { + if (tp != curtab) { + for (win_T *wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) { add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); + } + } + } + /* any other buf */ FOR_ALL_BUFFERS(buf) { add_bufnum(bufnrs, &bufnum, buf->b_fnum); @@ -1277,16 +1281,19 @@ check_changed_any ( } /* Try to find a window that contains the buffer. */ - if (buf != curbuf) - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf) { - goto_tabpage_win(tp, wp); - /* Paranoia: did autocms wipe out the buffer with changes? */ - if (!buf_valid(buf)) { - goto theend; + if (buf != curbuf) { + win_T *wp; + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + goto_tabpage_win(tp, wp); + /* Paranoia: did autocms wipe out the buffer with changes? */ + if (!buf_valid(buf)) { + goto theend; + } + goto buf_found; } - goto buf_found; } + } buf_found: /* Open the changed buffer in the current window. */ @@ -1907,7 +1914,7 @@ void ex_listdo(exarg_T *eap) break; } } - if (buf_still_exists) { + if (!buf_still_exists) { break; } @@ -2417,13 +2424,13 @@ do_source ( */ save_current_SID = current_SID; FileID file_id; - bool file_id_ok = os_get_file_id((char *)fname_exp, &file_id); + bool file_id_ok = os_fileid((char *)fname_exp, &file_id); for (current_SID = script_items.ga_len; current_SID > 0; --current_SID) { si = &SCRIPT_ITEM(current_SID); // Compare dev/ino when possible, it catches symbolic links. // Also compare file names, the inode may change when the file was edited. bool file_id_equal = file_id_ok && si->file_id_valid - && os_file_id_equal(&(si->file_id), &file_id); + && os_fileid_equal(&(si->file_id), &file_id); if (si->sn_name != NULL && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) { break; @@ -3256,7 +3263,7 @@ static void script_host_execute(char *method, exarg_T *eap) Object result = provider_call(method, args); // We don't care about the result, so free it just in case a bad provider // returned something - msgpack_rpc_free_object(result); + api_free_object(result); } free(script); @@ -3270,7 +3277,7 @@ static void script_host_execute_file(char *method, exarg_T *eap) Array args = ARRAY_DICT_INIT; ADD(args, STRING_OBJ(cstr_to_string(buffer))); Object result = provider_call(method, args); - msgpack_rpc_free_object(result); + api_free_object(result); } static void script_host_do_range(char *method, exarg_T *eap) @@ -3280,6 +3287,6 @@ static void script_host_do_range(char *method, exarg_T *eap) ADD(args, INTEGER_OBJ(eap->line2)); ADD(args, STRING_OBJ(cstr_to_string((char *)eap->arg))); Object result = provider_call(method, args); - msgpack_rpc_free_object(result); + api_free_object(result); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ab7add1c5b..1117b6fbcf 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5188,13 +5188,12 @@ static void ex_close(exarg_T *eap) */ static void ex_pclose(exarg_T *eap) { - win_T *win; - - for (win = firstwin; win != NULL; win = win->w_next) + FOR_ALL_WINDOWS(win) { if (win->w_p_pvw) { ex_win_close(eap->forceit, win, NULL); break; } + } } /* @@ -5505,6 +5504,7 @@ void alist_new(void) { curwin->w_alist = xmalloc(sizeof(*curwin->w_alist)); curwin->w_alist->al_refcount = 1; + curwin->w_alist->id = ++max_alist_id; alist_init(curwin->w_alist); } @@ -6119,7 +6119,6 @@ static void ex_swapname(exarg_T *eap) */ static void ex_syncbind(exarg_T *eap) { - win_T *wp; win_T *save_curwin = curwin; buf_T *save_curbuf = curbuf; long topline; @@ -6133,15 +6132,17 @@ static void ex_syncbind(exarg_T *eap) */ if (curwin->w_p_scb) { topline = curwin->w_topline; - for (wp = firstwin; wp; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { if (wp->w_p_scb && wp->w_buffer) { y = wp->w_buffer->b_ml.ml_line_count - p_so; - if (topline > y) + if (topline > y) { topline = y; + } } } - if (topline < 1) + if (topline < 1) { topline = 1; + } } else { topline = 1; } @@ -8859,7 +8860,7 @@ static void ex_match(exarg_T *eap) c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id); + match_add(curwin, g, p + 1, 10, id, NULL); free(g); *end = c; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 69fb1d344a..810df627c1 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -287,6 +287,14 @@ getcmdline ( do_digraph(-1); /* init digraph typeahead */ + // If something above caused an error, reset the flags, we do want to type + // and execute commands. Display may be messed up a bit. + if (did_emsg) { + redrawcmd(); + } + did_emsg = FALSE; + got_int = FALSE; + /* * Collect the command string, handling editing keys. */ diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index c7e1f5cbbc..955b0b0a68 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1097,7 +1097,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * url = true; } else { ff_expand_buffer[0] = NUL; - if (!os_get_file_id((char *)fname, &file_id)) { + if (!os_fileid((char *)fname, &file_id)) { return FAIL; } } @@ -1106,7 +1106,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) || (!url && vp->file_id_valid - && os_file_id_equal(&(vp->file_id), &file_id))) { + && os_fileid_equal(&(vp->file_id), &file_id))) { /* are the wildcard parts equal */ if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) /* already visited */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 2e932e9695..97daa035f8 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -472,7 +472,7 @@ readfile ( if (newfile && !read_stdin && !read_buffer) { /* Remember time of file. */ FileInfo file_info; - if (os_get_file_info((char *)fname, &file_info)) { + if (os_fileinfo((char *)fname, &file_info)) { buf_store_file_info(curbuf, &file_info); curbuf->b_mtime_read = curbuf->b_mtime; #ifdef UNIX @@ -2583,7 +2583,7 @@ buf_write ( #if defined(UNIX) perm = -1; FileInfo file_info_old; - if (!os_get_file_info((char *)fname, &file_info_old)) { + if (!os_fileinfo((char *)fname, &file_info_old)) { newfile = TRUE; } else { perm = file_info_old.stat.st_mode; @@ -2629,7 +2629,7 @@ buf_write ( goto fail; } if (overwriting) { - os_get_file_info((char *)fname, &file_info_old); + os_fileinfo((char *)fname, &file_info_old); } } @@ -2712,9 +2712,9 @@ buf_write ( * - it's a symbolic link * - we don't have write permission in the directory */ - if (file_info_old.stat.st_nlink > 1 - || !os_get_file_info_link((char *)fname, &file_info) - || !os_file_info_id_equal(&file_info, &file_info_old)) { + if (os_fileinfo_hardlinks(&file_info_old) > 1 + || !os_fileinfo_link((char *)fname, &file_info) + || !os_fileinfo_id_equal(&file_info, &file_info_old)) { backup_copy = TRUE; } else # endif @@ -2728,7 +2728,7 @@ buf_write ( STRCPY(IObuff, fname); for (i = 4913;; i += 123) { sprintf((char *)path_tail(IObuff), "%d", i); - if (!os_get_file_info_link((char *)IObuff, &file_info)) { + if (!os_fileinfo_link((char *)IObuff, &file_info)) { break; } } @@ -2739,7 +2739,7 @@ buf_write ( else { # ifdef UNIX os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (!os_get_file_info((char *)IObuff, &file_info) + if (!os_fileinfo((char *)IObuff, &file_info) || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid || (long)file_info.stat.st_mode != perm) { @@ -2759,20 +2759,20 @@ buf_write ( */ if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK)) { # ifdef UNIX - bool file_info_link_ok = os_get_file_info_link((char *)fname, &file_info); + bool file_info_link_ok = os_fileinfo_link((char *)fname, &file_info); /* Symlinks. */ if ((bkc_flags & BKC_BREAKSYMLINK) && file_info_link_ok - && !os_file_info_id_equal(&file_info, &file_info_old)) { + && !os_fileinfo_id_equal(&file_info, &file_info_old)) { backup_copy = FALSE; } /* Hardlinks. */ if ((bkc_flags & BKC_BREAKHARDLINK) - && file_info_old.stat.st_nlink > 1 + && os_fileinfo_hardlinks(&file_info_old) > 1 && (!file_info_link_ok - || os_file_info_id_equal(&file_info, &file_info_old))) { + || os_fileinfo_id_equal(&file_info, &file_info_old))) { backup_copy = FALSE; } # endif @@ -2837,14 +2837,14 @@ buf_write ( /* * Check if backup file already exists. */ - if (os_get_file_info((char *)backup, &file_info_new)) { + if (os_fileinfo((char *)backup, &file_info_new)) { /* * Check if backup file is same as original file. * May happen when modname() gave the same file back (e.g. silly * link). If we don't check here, we either ruin the file when * copying or erase it after writing. */ - if (os_file_info_id_equal(&file_info_new, &file_info_old)) { + if (os_fileinfo_id_equal(&file_info_new, &file_info_old)) { free(backup); backup = NULL; /* no backup file to delete */ } @@ -2861,7 +2861,7 @@ buf_write ( wp = backup; *wp = 'z'; while (*wp > 'a' - && os_get_file_info((char *)backup, &file_info_new)) { + && os_fileinfo((char *)backup, &file_info_new)) { --*wp; } /* They all exist??? Must be something wrong. */ @@ -3201,9 +3201,9 @@ nobackup: FileInfo file_info; /* Don't delete the file when it's a hard or symbolic link. */ - if ((!newfile && file_info_old.stat.st_nlink > 1) - || (os_get_file_info_link((char *)fname, &file_info) - && !os_file_info_id_equal(&file_info, &file_info_old))) { + if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) + || (os_fileinfo_link((char *)fname, &file_info) + && !os_fileinfo_id_equal(&file_info, &file_info_old))) { errmsg = (char_u *)_("E166: Can't open linked file for writing"); } else #endif @@ -3416,7 +3416,7 @@ restore_backup: /* don't change the owner when it's already OK, some systems remove * permission or ACL stuff */ FileInfo file_info; - if (!os_get_file_info((char *)wfname, &file_info) + if (!os_fileinfo((char *)wfname, &file_info) || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); @@ -3713,7 +3713,7 @@ nofail: /* Update the timestamp to avoid an "overwrite changed file" * prompt when writing again. */ - if (os_get_file_info((char *)fname, &file_info_old)) { + if (os_fileinfo((char *)fname, &file_info_old)) { buf_store_file_info(buf, &file_info_old); buf->b_mtime_read = buf->b_mtime; } @@ -4536,7 +4536,7 @@ int vim_rename(char_u *from, char_u *to) // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. FileInfo from_info; - if (!os_get_file_info((char *)from, &from_info)) { + if (!os_fileinfo((char *)from, &from_info)) { return -1; } @@ -4544,8 +4544,8 @@ int vim_rename(char_u *from, char_u *to) // This happens when "from" and "to" differ in case and are on a FAT32 // filesystem. In that case go through a temp file name. FileInfo to_info; - if (os_get_file_info((char *)to, &to_info) - && os_file_info_id_equal(&from_info, &to_info)) { + if (os_fileinfo((char *)to, &to_info) + && os_fileinfo_id_equal(&from_info, &to_info)) { use_tmp_file = true; } @@ -4790,7 +4790,7 @@ buf_check_timestamp ( int helpmesg = FALSE; int reload = FALSE; int can_reload = FALSE; - off_t orig_size = buf->b_orig_size; + uint64_t orig_size = buf->b_orig_size; int orig_mode = buf->b_orig_mode; static int busy = FALSE; int n; @@ -4812,7 +4812,7 @@ buf_check_timestamp ( bool file_info_ok; if (!(buf->b_flags & BF_NOTEDITED) && buf->b_mtime != 0 - && (!(file_info_ok = os_get_file_info((char *)buf->b_ffname, &file_info)) + && (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info)) || time_differs((long)file_info.stat.st_mtim.tv_sec, buf->b_mtime) || (int)file_info.stat.st_mode != buf->b_orig_mode )) { @@ -5127,9 +5127,10 @@ void buf_reload(buf_T *buf, int orig_mode) } void buf_store_file_info(buf_T *buf, FileInfo *file_info) + FUNC_ATTR_NONNULL_ALL { buf->b_mtime = (long)file_info->stat.st_mtim.tv_sec; - buf->b_orig_size = file_info->stat.st_size; + buf->b_orig_size = os_fileinfo_size(file_info); buf->b_orig_mode = (int)file_info->stat.st_mode; } @@ -6176,12 +6177,17 @@ aucmd_prepbuf ( int save_acd; /* Find a window that is for the new buffer */ - if (buf == curbuf) /* be quick when buf is curbuf */ + if (buf == curbuf) { /* be quick when buf is curbuf */ win = curwin; - else - for (win = firstwin; win != NULL; win = win->w_next) - if (win->w_buffer == buf) + } else { + win = NULL; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == buf) { + win = wp; break; + } + } + } /* Allocate "aucmd_win" when needed. If this fails (out of memory) fall * back to using the current window. */ diff --git a/src/nvim/fold.c b/src/nvim/fold.c index f65bbc0875..186e75981e 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -460,13 +460,10 @@ void newFoldLevel(void) newFoldLevelWin(curwin); if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { - win_T *wp; - /* * Set the same foldlevel in other windows in diff mode. */ - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { wp->w_p_fdl = curwin->w_p_fdl; newFoldLevelWin(wp); @@ -1140,19 +1137,18 @@ setManualFold ( ) { if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { - win_T *wp; linenr_T dlnum; /* * Do the same operation in other windows in diff mode. Calculate the * line number from the diffs. */ - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp); - if (dlnum != 0) + if (dlnum != 0) { (void)setManualFoldWin(wp, dlnum, opening, recurse, NULL); + } } } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 6c772a8a66..0d61172d69 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -601,6 +601,13 @@ void stuffReadbuff(char_u *s) add_buff(&readbuf1, s, -1L); } +/// Append string "s" to the redo stuff buffer. +/// @remark CSI and K_SPECIAL must already have been escaped. +void stuffRedoReadbuff(char_u *s) +{ + add_buff(&readbuf2, s, -1L); +} + void stuffReadbuffLen(char_u *s, long len) { add_buff(&readbuf1, s, len); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 1ff8887598..674786ff08 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -519,7 +519,7 @@ EXTERN win_T *firstwin; /* first window */ EXTERN win_T *lastwin; /* last window */ EXTERN win_T *prevwin INIT(= NULL); /* previous window */ # define W_NEXT(wp) ((wp)->w_next) -# define FOR_ALL_WINDOWS(wp) for (wp = firstwin; wp != NULL; wp = wp->w_next) +# define FOR_ALL_WINDOWS(wp) for (win_T *wp = firstwin; wp != NULL; wp = wp->w_next) /* * When using this macro "break" only breaks out of the inner loop. Use "goto" * to break out of the tabpage loop. @@ -568,6 +568,7 @@ EXTERN int mf_dont_release INIT(= FALSE); /* don't release blocks */ * to this when the window is using the global argument list. */ EXTERN alist_T global_alist; /* global argument list */ +EXTERN int max_alist_id INIT(= 0); ///< the previous argument list id EXTERN int arg_had_last INIT(= FALSE); /* accessed last file in global_alist */ @@ -1098,6 +1099,7 @@ EXTERN garray_T error_ga * Excluded are errors that are only used once and debugging messages. */ EXTERN char_u e_abort[] INIT(= N_("E470: Command aborted")); +EXTERN char_u e_api_spawn_failed[] INIT(= N_("E903: Could not spawn API job")); EXTERN char_u e_argreq[] INIT(= N_("E471: Argument required")); EXTERN char_u e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &")); EXTERN char_u e_cmdwin[] INIT(= N_( diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 9e07a60ee1..667e6512f3 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -473,7 +473,7 @@ cs_add_common ( fname = (char *)vim_strnsave((char_u *)fname, len); free(fbuf); FileInfo file_info; - bool file_info_ok = os_get_file_info(fname, &file_info); + bool file_info_ok = os_fileinfo(fname, &file_info); if (!file_info_ok) { staterr: if (p_csverbose) @@ -504,7 +504,7 @@ staterr: else (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE); - file_info_ok = os_get_file_info(fname2, &file_info); + file_info_ok = os_fileinfo(fname2, &file_info); if (!file_info_ok) { if (p_csverbose) cs_stat_emsg(fname2); @@ -1181,7 +1181,7 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, i = -1; /* can be set to the index of an empty item in csinfo */ for (j = 0; j < csinfo_size; j++) { if (csinfo[j].fname != NULL - && os_file_id_equal_file_info(&(csinfo[j].file_id), file_info)) { + && os_fileid_equal_fileinfo(&(csinfo[j].file_id), file_info)) { if (p_csverbose) (void)EMSG(_("E568: duplicate cscope database not added")); return -1; @@ -1224,7 +1224,7 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, } else csinfo[i].flags = NULL; - os_file_info_get_id(file_info, &(csinfo[i].file_id)); + os_fileinfo_id(file_info, &(csinfo[i].file_id)); return i; } /* cs_insert_filelist */ diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 9258ee93b6..7090e007bf 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -451,6 +451,7 @@ int get_breakindent_win(win_T *wp, char_u *line) { static int prev_indent = 0; /* cached indent value */ static int prev_ts = 0L; /* cached tabstop value */ static char_u *prev_line = NULL; /* cached pointer to line */ + static int prev_tick = 0; // changedtick of cached value int bri = 0; /* window width minus window margin space, i.e. what rests for text */ const int eff_wwidth = wp->w_width @@ -459,10 +460,11 @@ int get_breakindent_win(win_T *wp, char_u *line) { ? number_width(wp) + 1 : 0); /* used cached indent, unless pointer or 'tabstop' changed */ - if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts) - { + if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts + || prev_tick != wp->w_buffer->b_changedtick) { prev_line = line; prev_ts = wp->w_buffer->b_p_ts; + prev_tick = wp->w_buffer->b_changedtick; prev_indent = get_indent_str(line, (int)wp->w_buffer->b_p_ts, wp->w_p_list); } diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 6e12194b63..509f94dbf2 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -2009,12 +2009,14 @@ int get_c_indent(void) * ldfd) { * } */ - if (curbuf->b_ind_js || (curbuf->b_ind_keep_case_label - && cin_iscase(skipwhite(get_cursor_line_ptr()), - FALSE))) + if ((curbuf->b_ind_js || curbuf->b_ind_keep_case_label) + && cin_iscase(skipwhite(get_cursor_line_ptr()), FALSE)) { amount = get_indent(); - else + } else if (curbuf->b_ind_js) { + amount = get_indent_lnum(lnum); + } else { amount = skip_label(lnum, &l); + } start_brace = BRACE_AT_END; } diff --git a/src/nvim/main.c b/src/nvim/main.c index 2f06a2cbf2..7dc299e73b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -12,6 +12,8 @@ #include <string.h> #include <stdbool.h> +#include <msgpack.h> + #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/main.h" @@ -57,6 +59,9 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/signal.h" +#include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" /* Maximum number of commands from + or -c arguments. */ #define MAX_ARG_CMDS 10 @@ -116,9 +121,6 @@ static void init_locale(void); # endif #endif /* NO_VIM_MAIN */ -extern const uint8_t msgpack_metadata[]; -extern const unsigned int msgpack_metadata_size; - /* * Different types of error messages. */ @@ -190,6 +192,7 @@ int main(int argc, char **argv) init_yank(); /* init yank buffers */ alist_init(&global_alist); /* Init the argument list to empty. */ + global_alist.id = 0; /* * Set the default values for the options. @@ -471,11 +474,10 @@ int main(int argc, char **argv) edit_buffers(¶ms); if (params.diff_mode) { - win_T *wp; - /* set options in each window for "vimdiff". */ - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { diff_win_options(wp, TRUE); + } } /* @@ -1027,12 +1029,18 @@ static void command_line_scan(mparm_T *parmp) msg_putchar('\n'); msg_didout = FALSE; mch_exit(0); - } else if (STRICMP(argv[0] + argv_idx, "api-msgpack-metadata") == 0) { - for (unsigned int i = 0; i<msgpack_metadata_size; i++) { - putchar(msgpack_metadata[i]); + } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { + msgpack_sbuffer* b = msgpack_sbuffer_new(); + msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write); + Object md = DICTIONARY_OBJ(api_metadata()); + msgpack_rpc_from_object(md, p); + + for (size_t i = 0; i < b->size; i++) { + putchar(b->data[i]); } + mch_exit(0); - } else if (STRICMP(argv[0] + argv_idx, "embedded-mode") == 0) { + } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { #if !defined(UNIX) @@ -2094,9 +2102,9 @@ static int file_owned(char *fname) { uid_t uid = getuid(); FileInfo file_info; - bool file_owned = os_get_file_info(fname, &file_info) + bool file_owned = os_fileinfo(fname, &file_info) && file_info.stat.st_uid == uid; - bool link_owned = os_get_file_info_link(fname, &file_info) + bool link_owned = os_fileinfo_link(fname, &file_info) && file_info.stat.st_uid == uid; return file_owned && link_owned; } @@ -2213,8 +2221,8 @@ static void usage(void) main_msg(_("-W <scriptout>\tWrite all typed commands to file <scriptout>")); main_msg(_("--startuptime <file>\tWrite startup timing messages to <file>")); main_msg(_("-i <viminfo>\t\tUse <viminfo> instead of .viminfo")); - main_msg(_("--api-msgpack-metadata\tDump API metadata information and exit")); - main_msg(_("--embedded-mode\tUse stdin/stdout as a msgpack-rpc channel. " + main_msg(_("--api-info\t\tDump API metadata serialized to msgpack and exit")); + main_msg(_("--embed\t\tUse stdin/stdout as a msgpack-rpc channel. " "This can be used for embedding Neovim into other programs")); main_msg(_("-h or --help\tPrint Help (this message) and exit")); main_msg(_("--version\t\tPrint version information and exit")); diff --git a/src/nvim/map.c b/src/nvim/map.c index 2e47e8b249..24aa38d67d 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -1,10 +1,12 @@ #include <stdlib.h> #include <stdbool.h> +#include <string.h> #include "nvim/map.h" #include "nvim/map_defs.h" #include "nvim/vim.h" #include "nvim/memory.h" +#include "nvim/os/msgpack_rpc.h" #include "nvim/lib/khash.h" @@ -87,7 +89,23 @@ return rv; \ } +static inline khint_t String_hash(String s) +{ + khint_t h = 0; + for (size_t i = 0; i < s.size && s.data[i]; i++) { + h = (h << 5) - h + (uint8_t)s.data[i]; + } + return h; +} + +static inline bool String_eq(String a, String b) +{ + return strncmp(a.data, b.data, min(a.size, b.size)) == 0; +} + + MAP_IMPL(cstr_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) +MAP_IMPL(String, rpc_method_handler_fn, DEFAULT_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 73698cba22..616516c3e1 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -4,6 +4,8 @@ #include <stdbool.h> #include "nvim/map_defs.h" +#include "nvim/api/private/defs.h" +#include "nvim/os/msgpack_rpc.h" #define MAP_DECLS(T, U) \ KHASH_DECLARE(T##_##U##_map, T, U) \ @@ -23,6 +25,7 @@ MAP_DECLS(cstr_t, uint64_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) +MAP_DECLS(String, rpc_method_handler_fn) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 43469cab45..6339cf8275 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -500,7 +500,6 @@ void fmarks_check_names(buf_T *buf) { char_u *name; int i; - win_T *wp; if (buf->b_ffname == NULL) return; @@ -512,10 +511,10 @@ void fmarks_check_names(buf_T *buf) for (i = 0; i < NMARKS + EXTRA_MARKS; ++i) fmarks_check_one(&namedfm[i], name, buf); - FOR_ALL_WINDOWS(wp) - { - for (i = 0; i < wp->w_jumplistlen; ++i) + FOR_ALL_WINDOWS(wp) { + for (i = 0; i < wp->w_jumplistlen; ++i) { fmarks_check_one(&wp->w_jumplist[i], name, buf); + } } free(name); @@ -1042,7 +1041,6 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a { int i; int fnum = curbuf->b_fnum; - win_T *win; pos_T *posp; if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks) @@ -1085,22 +1083,26 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a /* * Adjust items in all windows related to the current buffer. */ - FOR_ALL_WINDOWS(win) - { + FOR_ALL_WINDOWS(win) { /* marks in the jumplist */ - for (i = 0; i < win->w_jumplistlen; ++i) - if (win->w_jumplist[i].fmark.fnum == fnum) + for (i = 0; i < win->w_jumplistlen; ++i) { + if (win->w_jumplist[i].fmark.fnum == fnum) { col_adjust(&(win->w_jumplist[i].fmark.mark)); + } + } if (win->w_buffer == curbuf) { /* marks in the tag stack */ - for (i = 0; i < win->w_tagstacklen; i++) - if (win->w_tagstack[i].fmark.fnum == fnum) + for (i = 0; i < win->w_tagstacklen; i++) { + if (win->w_tagstack[i].fmark.fnum == fnum) { col_adjust(&(win->w_tagstack[i].fmark.mark)); + } + } /* cursor position for other windows with the same buffer */ - if (win != curwin) + if (win != curwin) { col_adjust(&win->w_cursor); + } } } } @@ -1526,10 +1528,7 @@ void copy_viminfo_marks(vir_T *virp, FILE *fp_out, int count, int eof, int flags fputs((char *)line, fp_out); } if (load_marks) { - win_T *wp; - - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp->w_buffer == curbuf) wp->w_changelistidx = curbuf->b_changelistlen; } diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 82369b739a..827cff2299 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -122,11 +122,11 @@ memfile_T *mf_open(char_u *fname, int flags) * mf_blocknr_max must be rounded up. */ FileInfo file_info; - if (mfp->mf_fd >= 0 - && os_get_file_info_fd(mfp->mf_fd, &file_info) - && file_info.stat.st_blksize >= MIN_SWAP_PAGE_SIZE - && file_info.stat.st_blksize <= MAX_SWAP_PAGE_SIZE) { - mfp->mf_page_size = file_info.stat.st_blksize; + if (mfp->mf_fd >= 0 && os_fileinfo_fd(mfp->mf_fd, &file_info)) { + uint64_t blocksize = os_fileinfo_blocksize(&file_info); + if (blocksize >= MIN_SWAP_PAGE_SIZE && blocksize <= MAX_SWAP_PAGE_SIZE) { + mfp->mf_page_size = blocksize; + } } if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL)) @@ -1017,7 +1017,7 @@ mf_do_open ( */ FileInfo file_info; if ((flags & O_CREAT) - && os_get_file_info_link((char *)mfp->mf_fname, &file_info)) { + && os_fileinfo_link((char *)mfp->mf_fname, &file_info)) { mfp->mf_fd = -1; EMSG(_("E300: Swap file already exists (symlink attack?)")); } else { diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 229de4ae1c..04ee0d7f55 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -686,9 +686,9 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) } } FileInfo file_info; - if (os_get_file_info((char *)buf->b_ffname, &file_info)) { + if (os_fileinfo((char *)buf->b_ffname, &file_info)) { long_to_char((long)file_info.stat.st_mtim.tv_sec, b0p->b0_mtime); - long_to_char((long)os_file_info_get_inode(&file_info), b0p->b0_ino); + long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino); buf_store_file_info(buf, &file_info); buf->b_mtime_read = buf->b_mtime; } else { @@ -961,8 +961,8 @@ void ml_recover(void) FileInfo swp_file_info; mtime = char_to_long(b0p->b0_mtime); if (curbuf->b_ffname != NULL - && os_get_file_info((char *)curbuf->b_ffname, &org_file_info) - && ((os_get_file_info((char *)mfp->mf_fname, &swp_file_info) + && os_fileinfo((char *)curbuf->b_ffname, &org_file_info) + && ((os_fileinfo((char *)mfp->mf_fname, &swp_file_info) && org_file_info.stat.st_mtim.tv_sec > swp_file_info.stat.st_mtim.tv_sec) || org_file_info.stat.st_mtim.tv_sec != mtime)) { @@ -1494,7 +1494,7 @@ static time_t swapfile_info(char_u *fname) /* print the swap file date */ FileInfo file_info; - if (os_get_file_info((char *)fname, &file_info)) { + if (os_fileinfo((char *)fname, &file_info)) { #ifdef UNIX /* print name of owner of the file */ if (os_get_uname(file_info.stat.st_uid, uname, B0_UNAME_SIZE) == OK) { @@ -1630,9 +1630,9 @@ void ml_sync_all(int check_file, int check_char) * call ml_preserve() to get rid of all negative numbered blocks. */ FileInfo file_info; - if (!os_get_file_info((char *)buf->b_ffname, &file_info) + if (!os_fileinfo((char *)buf->b_ffname, &file_info) || file_info.stat.st_mtim.tv_sec != buf->b_mtime_read - || (off_t)file_info.stat.st_size != buf->b_orig_size) { + || os_fileinfo_size(&file_info) != buf->b_orig_size) { ml_preserve(buf, FALSE); did_check_timestamps = FALSE; need_check_timestamps = TRUE; /* give message later */ @@ -3180,7 +3180,7 @@ attention_message ( msg_outtrans(buf->b_fname); MSG_PUTS("\"\n"); FileInfo file_info; - if (os_get_file_info((char *)buf->b_fname, &file_info)) { + if (os_fileinfo((char *)buf->b_fname, &file_info)) { MSG_PUTS(_(" dated: ")); x = file_info.stat.st_mtim.tv_sec; p = ctime(&x); // includes '\n' @@ -3294,7 +3294,7 @@ findswapname ( // Extra security check: When a swap file is a symbolic link, this // is most likely a symlink attack. FileInfo file_info; - bool file_or_link_found = os_get_file_info_link((char *)fname, &file_info); + bool file_or_link_found = os_fileinfo_link((char *)fname, &file_info); if (!file_or_link_found) { break; } @@ -3558,8 +3558,8 @@ fnamecmp_ino ( int retval_s; /* flag: buf_s valid */ FileInfo file_info; - if (os_get_file_info((char *)fname_c, &file_info)) { - ino_c = os_file_info_get_inode(&file_info); + if (os_fileinfo((char *)fname_c, &file_info)) { + ino_c = os_fileinfo_inode(&file_info); } /* @@ -3567,8 +3567,8 @@ fnamecmp_ino ( * the swap file may be outdated. If that fails (e.g. this path is not * valid on this machine), use the inode from block 0. */ - if (os_get_file_info((char *)fname_s, &file_info)) { - ino_s = os_file_info_get_inode(&file_info); + if (os_fileinfo((char *)fname_s, &file_info)) { + ino_s = os_fileinfo_inode(&file_info); } else { ino_s = (uint64_t)ino_block0; } diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 50dcca3c84..1c3d6e372c 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -121,7 +121,8 @@ void *xmalloc(size_t size) void *ret = try_malloc(size); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } return ret; @@ -147,7 +148,8 @@ void *xcalloc(size_t count, size_t size) if (!ret && (!count || !size)) ret = calloc(1, 1); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } @@ -174,7 +176,8 @@ void *xrealloc(void *ptr, size_t size) if (!ret && !size) ret = realloc(ptr, 1); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } @@ -194,7 +197,7 @@ void *xmallocz(size_t size) void *ret; if (total_size < size) { - OUT_STR("Vim: Data too large to fit into virtual memory space\n"); + OUT_STR(_("Vim: Data too large to fit into virtual memory space\n")); preserve_exit(); } @@ -316,7 +319,8 @@ char *xstrdup(const char *str) try_to_free_memory(); ret = strdup(str); if (!ret) { - OUT_STR("Vim: Error: Out of memory.\n"); + OUT_STR(e_outofmem); + out_char('\n'); preserve_exit(); } } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index d31bd44493..d7e9618639 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1877,16 +1877,16 @@ void changed_bytes(linenr_T lnum, colnr_T col) /* Diff highlighting in other diff windows may need to be updated too. */ if (curwin->w_p_diff) { - win_T *wp; linenr_T wlnum; - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_p_diff && wp != curwin) { redraw_win_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) changedOneline(wp->w_buffer, wlnum); } + } } } @@ -1973,17 +1973,18 @@ changed_lines ( /* When the number of lines doesn't change then mark_adjust() isn't * called and other diff buffers still need to be marked for * displaying. */ - win_T *wp; linenr_T wlnum; - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_p_diff && wp != curwin) { redraw_win_later(wp, VALID); wlnum = diff_lnum_win(lnum, wp); - if (wlnum > 0) + if (wlnum > 0) { changed_lines_buf(wp->w_buffer, wlnum, lnume - lnum + wlnum, 0L); + } } + } } changed_common(lnum, col, lnume, xtra); @@ -2214,14 +2215,14 @@ unchanged ( */ void check_status(buf_T *buf) { - win_T *wp; - - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_buffer == buf && wp->w_status_height) { wp->w_redr_status = TRUE; - if (must_redraw < VALID) + if (must_redraw < VALID) { must_redraw = VALID; + } } + } } /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 55b86f61dd..cc82630548 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4008,12 +4008,9 @@ dozet: /* Redraw when 'foldenable' changed */ if (old_fen != curwin->w_p_fen) { - win_T *wp; - if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { /* Adjust 'foldenable' in diff-synced windows. */ - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { wp->w_p_fen = curwin->w_p_fen; changed_window_setting_win(wp); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index f1cb34577b..9b98c84be4 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -48,7 +48,6 @@ #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/provider.h" -#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/api/private/helpers.h" /* @@ -3698,17 +3697,15 @@ op_format ( } if (oap->is_VIsual) { - win_T *wp; - - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp->w_old_cursor_lnum != 0) { /* When lines have been inserted or deleted, adjust the end of * the Visual area to be redrawn. */ - if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) + if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) { wp->w_old_cursor_lnum += old_line_count; - else + } else { wp->w_old_visual_lnum += old_line_count; + } } } } @@ -5258,7 +5255,7 @@ static void get_clipboard(int name) return; err: - msgpack_rpc_free_object(result); + api_free_object(result); free(reg->y_array); reg->y_array = NULL; reg->y_size = 0; @@ -5289,5 +5286,5 @@ static void set_clipboard(int name) ADD(args, ARRAY_OBJ(lines)); Object result = provider_call("clipboard_set", args); - msgpack_rpc_free_object(result); + api_free_object(result); } diff --git a/src/nvim/option.c b/src/nvim/option.c index 6eac1033c4..b26b6ed4cc 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -974,12 +974,6 @@ static struct vimoption {"infercase", "inf", P_BOOL|P_VI_DEF, (char_u *)&p_inf, PV_INF, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"initclipboard","icpb",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_icpb, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"initpython","ipy",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_ipy, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, {"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM, (char_u *)&p_im, PV_NONE, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, @@ -4306,7 +4300,6 @@ did_set_string_option ( /* When 'spelllang' or 'spellfile' is set and there is a window for this * buffer in which 'spell' is set load the wordlists. */ else if (varp == &(curbuf->b_s.b_p_spl) || varp == &(curbuf->b_s.b_p_spf)) { - win_T *wp; int l; if (varp == &(curbuf->b_s.b_p_spf)) { @@ -4317,10 +4310,11 @@ did_set_string_option ( } if (errmsg == NULL) { - FOR_ALL_WINDOWS(wp) - if (wp->w_buffer == curbuf && wp->w_p_spell) { - errmsg = did_set_spelllang(wp); - break; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == curbuf && wp->w_p_spell) { + errmsg = did_set_spelllang(wp); + break; + } } } } @@ -5075,9 +5069,7 @@ set_bool_option ( /* There can be only one window with 'previewwindow' set. */ else if ((int *)varp == &curwin->w_p_pvw) { if (curwin->w_p_pvw) { - win_T *win; - - for (win = firstwin; win != NULL; win = win->w_next) { + FOR_ALL_WINDOWS(win) { if (win->w_p_pvw && win != curwin) { curwin->w_p_pvw = FALSE; return (char_u *)N_("E590: A preview window already exists"); diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 555e9166d6..cd61b6427c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -631,8 +631,6 @@ EXTERN int p_write; /* 'write' */ EXTERN int p_wa; /* 'writeany' */ EXTERN int p_wb; /* 'writebackup' */ EXTERN long p_wd; /* 'writedelay' */ -EXTERN char *p_ipy; // 'initpython' -EXTERN char *p_icpb; // 'initclipboard' /* * "indir" values for buffer-local opions. diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index 39455df862..1670424e4e 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -22,8 +22,10 @@ #include "nvim/memory.h" #include "nvim/os_unix.h" #include "nvim/message.h" +#include "nvim/term.h" #include "nvim/map.h" #include "nvim/log.h" +#include "nvim/misc1.h" #include "nvim/lib/kvec.h" #define CHANNEL_BUFFER_SIZE 0xffff @@ -156,7 +158,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (id > 0) { if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { - msgpack_rpc_free_array(args); + api_free_array(args); return false; } send_event(channel, name, args); @@ -184,7 +186,7 @@ bool channel_send_call(uint64_t id, Channel *channel = NULL; if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { - msgpack_rpc_free_array(args); + api_free_array(args); return false; } @@ -194,11 +196,10 @@ bool channel_send_call(uint64_t id, char buf[256]; snprintf(buf, sizeof(buf), - "Channel %" PRIu64 " was closed due to a high stack depth " - "while processing a RPC call", + "Channel %" PRIu64 " crossed maximum stack depth", channel->id); *result = STRING_OBJ(cstr_to_string(buf)); - msgpack_rpc_free_array(args); + api_free_array(args); return false; } @@ -223,14 +224,6 @@ bool channel_send_call(uint64_t id, channel->enabled && // the channel is still enabled kv_size(channel->call_stack) >= size); // the call didn't return - if (!(kv_size(channel->call_stack) - || channel->enabled - || channel->rpc_call_level)) { - // Close the channel if it has been disabled and we have not been called - // by `parse_msgpack`(It would be unsafe to close the channel otherwise) - close_channel(channel); - } - *errored = frame.errored; *result = frame.result; @@ -274,6 +267,23 @@ void channel_unsubscribe(uint64_t id, char *event) unsubscribe(channel, event); } +/// Closes a channel +/// +/// @param id The channel id +/// @return true if successful, false otherwise +bool channel_close(uint64_t id) +{ + Channel *channel; + + if (!(channel = pmap_get(uint64_t)(channels, id)) || !channel->enabled) { + return false; + } + + channel_kill(channel); + channel->enabled = false; + return true; +} + /// Creates an API channel from stdin/stdout. This is used when embedding /// Neovim static void channel_from_stdio(void) @@ -340,11 +350,11 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) msgpack_unpacked unpacked; msgpack_unpacked_init(&unpacked); - UnpackResult result; + msgpack_unpack_return result; // Deserialize everything we can. - while ((result = msgpack_rpc_unpack(channel->unpacker, &unpacked)) - == kUnpackResultOk) { + while ((result = msgpack_unpacker_next(channel->unpacker, &unpacked)) == + MSGPACK_UNPACK_SUCCESS) { if (kv_size(channel->call_stack) && is_rpc_response(&unpacked.data)) { if (is_valid_rpc_response(&unpacked.data, channel)) { call_stack_pop(&unpacked.data, channel); @@ -371,7 +381,13 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) } } - if (result == kUnpackResultFail) { + if (result == MSGPACK_UNPACK_NOMEM_ERROR) { + OUT_STR(e_outofmem); + out_char('\n'); + preserve_exit(); + } + + if (result == MSGPACK_UNPACK_PARSE_ERROR) { // See src/msgpack/unpack_template.h in msgpack source tree for // causes for this error(search for 'goto _failed') // @@ -450,7 +466,7 @@ static void broadcast_event(char *name, Array args) }); if (!kv_size(subscribed)) { - msgpack_rpc_free_array(args); + api_free_array(args); goto end; } @@ -498,7 +514,13 @@ static void close_channel(Channel *channel) pmap_free(cstr_t)(channel->subscribed_events); kv_destroy(channel->call_stack); + channel_kill(channel); + free(channel); +} + +static void channel_kill(Channel *channel) +{ if (channel->is_job) { if (channel->data.job) { job_stop(channel->data.job); @@ -513,8 +535,6 @@ static void close_channel(Channel *channel) mch_exit(0); } } - - free(channel); } static void close_cb(uv_handle_t *handle) diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index aca7005064..bb4e897887 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -258,20 +258,6 @@ int os_file_is_writable(const char *name) return 0; } -/// Get the size of a file in bytes. -/// -/// @param[out] size pointer to an off_t to put the size into. -/// @return `true` for success, `false` for failure. -bool os_get_file_size(const char *name, off_t *size) -{ - uv_stat_t statbuf; - if (os_stat(name, &statbuf)) { - *size = statbuf.st_size; - return true; - } - return false; -} - /// Rename a file or directory. /// /// @return `OK` for success, `FAIL` for failure. @@ -345,7 +331,7 @@ int os_remove(const char *path) /// @param path Path to the file. /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. -bool os_get_file_info(const char *path, FileInfo *file_info) +bool os_fileinfo(const char *path, FileInfo *file_info) { return os_stat(path, &(file_info->stat)); } @@ -355,7 +341,7 @@ bool os_get_file_info(const char *path, FileInfo *file_info) /// @param path Path to the file. /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. -bool os_get_file_info_link(const char *path, FileInfo *file_info) +bool os_fileinfo_link(const char *path, FileInfo *file_info) { uv_fs_t request; int result = uv_fs_lstat(uv_default_loop(), &request, path, NULL); @@ -369,7 +355,7 @@ bool os_get_file_info_link(const char *path, FileInfo *file_info) /// @param file_descriptor File descriptor of the file. /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. -bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info) +bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info) { uv_fs_t request; int result = uv_fs_fstat(uv_default_loop(), &request, file_descriptor, NULL); @@ -381,7 +367,7 @@ bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info) /// Compare the inodes of two FileInfos /// /// @return `true` if the two FileInfos represent the same file. -bool os_file_info_id_equal(const FileInfo *file_info_1, +bool os_fileinfo_id_equal(const FileInfo *file_info_1, const FileInfo *file_info_2) { return file_info_1->stat.st_ino == file_info_2->stat.st_ino @@ -392,7 +378,7 @@ bool os_file_info_id_equal(const FileInfo *file_info_1, /// /// @param file_info Pointer to the `FileInfo` /// @param[out] file_id Pointer to a `FileID` -void os_file_info_get_id(const FileInfo *file_info, FileID *file_id) +void os_fileinfo_id(const FileInfo *file_info, FileID *file_id) { file_id->inode = file_info->stat.st_ino; file_id->device_id = file_info->stat.st_dev; @@ -403,17 +389,44 @@ void os_file_info_get_id(const FileInfo *file_info, FileID *file_id) /// @deprecated Use `FileID` instead, this function is only needed in memline.c /// @param file_info Pointer to the `FileInfo` /// @return the inode number -uint64_t os_file_info_get_inode(const FileInfo *file_info) +uint64_t os_fileinfo_inode(const FileInfo *file_info) { return file_info->stat.st_ino; } +/// Get the size of a file from a `FileInfo`. +/// +/// @return filesize in bytes. +uint64_t os_fileinfo_size(const FileInfo *file_info) + FUNC_ATTR_NONNULL_ALL +{ + return file_info->stat.st_size; +} + +/// Get the number of hardlinks from a `FileInfo`. +/// +/// @return number of hardlinks. +uint64_t os_fileinfo_hardlinks(const FileInfo *file_info) + FUNC_ATTR_NONNULL_ALL +{ + return file_info->stat.st_nlink; +} + +/// Get the blocksize from a `FileInfo`. +/// +/// @return blocksize in bytes. +uint64_t os_fileinfo_blocksize(const FileInfo *file_info) + FUNC_ATTR_NONNULL_ALL +{ + return file_info->stat.st_blksize; +} + /// Get the `FileID` for a given path /// /// @param path Path to the file. /// @param[out] file_info Pointer to a `FileID` to fill in. /// @return `true` on sucess, `false` for failure. -bool os_get_file_id(const char *path, FileID *file_id) +bool os_fileid(const char *path, FileID *file_id) { uv_stat_t statbuf; if (os_stat(path, &statbuf)) { @@ -429,7 +442,7 @@ bool os_get_file_id(const char *path, FileID *file_id) /// @param file_id_1 Pointer to first `FileID` /// @param file_id_2 Pointer to second `FileID` /// @return `true` if the two `FileID`s represent te same file. -bool os_file_id_equal(const FileID *file_id_1, const FileID *file_id_2) +bool os_fileid_equal(const FileID *file_id_1, const FileID *file_id_2) { return file_id_1->inode == file_id_2->inode && file_id_1->device_id == file_id_2->device_id; @@ -440,7 +453,7 @@ bool os_file_id_equal(const FileID *file_id_1, const FileID *file_id_2) /// @param file_id Pointer to a `FileID` /// @param file_info Pointer to a `FileInfo` /// @return `true` if the `FileID` and the `FileInfo` represent te same file. -bool os_file_id_equal_file_info(const FileID *file_id, +bool os_fileid_equal_fileinfo(const FileID *file_id, const FileInfo *file_info) { return file_id->inode == file_info->stat.st_ino diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c index 9deca9de74..9fb2a49e50 100644 --- a/src/nvim/os/job.c +++ b/src/nvim/os/job.c @@ -197,6 +197,12 @@ Job *job_start(char **argv, job->stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; job->stdio[2].data.stream = (uv_stream_t *)&job->proc_stderr; + // Give all handles a reference to the job + handle_set_job((uv_handle_t *)&job->proc, job); + handle_set_job((uv_handle_t *)&job->proc_stdin, job); + handle_set_job((uv_handle_t *)&job->proc_stdout, job); + handle_set_job((uv_handle_t *)&job->proc_stderr, job); + // Spawn the job if (uv_spawn(uv_default_loop(), &job->proc, &job->proc_opts) != 0) { free_job(job); @@ -204,12 +210,6 @@ Job *job_start(char **argv, return NULL; } - // Give all handles a reference to the job - handle_set_job((uv_handle_t *)&job->proc, job); - handle_set_job((uv_handle_t *)&job->proc_stdin, job); - handle_set_job((uv_handle_t *)&job->proc_stdout, job); - handle_set_job((uv_handle_t *)&job->proc_stderr, job); - job->in = wstream_new(maxmem); wstream_set_stream(job->in, (uv_stream_t *)&job->proc_stdin); // Start the readable streams diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index c6e2af2f1c..d7e3d33c4b 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -17,9 +17,6 @@ # include "os/msgpack_rpc.c.generated.h" #endif -extern const uint8_t msgpack_metadata[]; -extern const unsigned int msgpack_metadata_size; - /// Validates the basic structure of the msgpack-rpc call and fills `res` /// with the basic response structure. /// @@ -39,11 +36,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id, return serialize_response(response_id, err, NIL, sbuffer); } - if (req->via.array.ptr[2].type == MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].via.u64 == 0) { - return serialize_metadata(response_id, channel_id, sbuffer); - } - // dispatch the call Error error = { .set = false }; Object rv = msgpack_rpc_dispatch(channel_id, req, &error); @@ -63,42 +55,6 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id, return serialize_response(response_id, NULL, rv, sbuffer); } -/// Try to unpack a msgpack document from the data in the unpacker buffer. This -/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets -/// the called know if the unpacking failed due to bad input or due to missing -/// data. -/// -/// @param unpacker The unpacker containing the parse buffer -/// @param result The result which will contain the parsed object -/// @return kUnpackResultOk : An object was parsed -/// kUnpackResultFail : Got bad input -/// kUnpackResultNeedMore: Need more data -UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, - msgpack_unpacked* result) - FUNC_ATTR_NONNULL_ALL -{ - if (result->zone != NULL) { - msgpack_zone_free(result->zone); - } - - int res = msgpack_unpacker_execute(unpacker); - - if (res > 0) { - result->zone = msgpack_unpacker_release_zone(unpacker); - result->data = msgpack_unpacker_data(unpacker); - msgpack_unpacker_reset(unpacker); - return kUnpackResultOk; - } - - if (res < 0) { - // Since we couldn't parse it, destroy the data consumed so far - msgpack_unpacker_reset(unpacker); - return kUnpackResultFail; - } - - return kUnpackResultNeedMore; -} - /// Finishes the msgpack-rpc call with an error message. /// /// @param msg The error message @@ -109,12 +65,22 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) size_t len = strlen(msg); // error message - msgpack_pack_raw(res, len); - msgpack_pack_raw_body(res, msg, len); + msgpack_pack_bin(res, len); + msgpack_pack_bin_body(res, msg, len); // Nil result msgpack_pack_nil(res); } +/// Handler executed when an invalid method name is passed +Object msgpack_rpc_handle_missing_method(uint64_t channel_id, + msgpack_object *req, + Error *error) +{ + snprintf(error->msg, sizeof(error->msg), "Invalid method name"); + error->set = true; + return NIL; +} + /// Serializes a msgpack-rpc request or notification(id == 0) WBuffer *serialize_request(uint64_t request_id, String method, @@ -132,14 +98,14 @@ WBuffer *serialize_request(uint64_t request_id, msgpack_pack_uint64(&pac, request_id); } - msgpack_pack_raw(&pac, method.size); - msgpack_pack_raw_body(&pac, method.data, method.size); + msgpack_pack_bin(&pac, method.size); + msgpack_pack_bin_body(&pac, method.data, method.size); msgpack_rpc_from_array(args, &pac); WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), sbuffer->size, refcount, free); - msgpack_rpc_free_array(args); + api_free_array(args); msgpack_sbuffer_clear(sbuffer); return rv; } @@ -160,8 +126,8 @@ WBuffer *serialize_response(uint64_t response_id, if (err_msg) { String err = {.size = strlen(err_msg), .data = err_msg}; // error message - msgpack_pack_raw(&pac, err.size); - msgpack_pack_raw_body(&pac, err.data, err.size); + msgpack_pack_bin(&pac, err.size); + msgpack_pack_bin_body(&pac, err.data, err.size); // Nil result msgpack_pack_nil(&pac); } else { @@ -175,32 +141,7 @@ WBuffer *serialize_response(uint64_t response_id, sbuffer->size, 1, // responses only go though 1 channel free); - msgpack_rpc_free_object(arg); - msgpack_sbuffer_clear(sbuffer); - return rv; -} - -WBuffer *serialize_metadata(uint64_t id, - uint64_t channel_id, - msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_packer pac; - msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_pack_array(&pac, 4); - msgpack_pack_int(&pac, 1); - msgpack_pack_uint64(&pac, id); - // Nil error - msgpack_pack_nil(&pac); - // The result is the [channel_id, metadata] array - msgpack_pack_array(&pac, 2); - msgpack_pack_uint64(&pac, channel_id); - msgpack_pack_raw(&pac, msgpack_metadata_size); - msgpack_pack_raw_body(&pac, msgpack_metadata, msgpack_metadata_size); - WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), - sbuffer->size, - 1, - free); + api_free_object(arg); msgpack_sbuffer_clear(sbuffer); return rv; } @@ -234,9 +175,9 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) return "Message type must be 0"; } - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER - && req->via.array.ptr[2].type != MSGPACK_OBJECT_RAW) { - return "Method must be a positive integer or a string"; + if (req->via.array.ptr[2].type != MSGPACK_OBJECT_BIN + && req->via.array.ptr[2].type != MSGPACK_OBJECT_STR) { + return "Method must be a string"; } if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h index 35f175d2a0..3476d791ea 100644 --- a/src/nvim/os/msgpack_rpc.h +++ b/src/nvim/os/msgpack_rpc.h @@ -25,6 +25,7 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id, /// Initializes the msgpack-rpc method table void msgpack_rpc_init(void); +void msgpack_rpc_init_function_metadata(Dictionary *metadata); /// Dispatches to the actual API function after basic payload validation by /// `msgpack_rpc_call`. It is responsible for validating/converting arguments diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c index e2c277abe4..b14de8245c 100644 --- a/src/nvim/os/msgpack_rpc_helpers.c +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -7,61 +7,67 @@ #include "nvim/vim.h" #include "nvim/memory.h" -#define REMOTE_FUNCS_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ - { \ - *arg = obj->via.u64; \ - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \ - } \ - \ - void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \ - { \ - msgpack_pack_uint64(res, result); \ - } +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc_helpers.c.generated.h" +#endif -#define TYPED_ARRAY_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \ +static msgpack_zone zone; +static msgpack_sbuffer sbuffer; + +#define HANDLE_TYPE_CONVERSION_IMPL(t, lt) \ + bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ + FUNC_ATTR_NONNULL_ALL \ { \ - if (obj->type != MSGPACK_OBJECT_ARRAY) { \ + if (obj->type != MSGPACK_OBJECT_EXT \ + || obj->via.ext.type != kObjectType##t) { \ return false; \ } \ \ - arg->size = obj->via.array.size; \ - arg->items = xcalloc(obj->via.array.size, sizeof(t)); \ + msgpack_object data; \ + msgpack_unpack_return ret = msgpack_unpack(obj->via.ext.ptr, \ + obj->via.ext.size, \ + NULL, \ + &zone, \ + &data); \ \ - for (size_t i = 0; i < obj->via.array.size; i++) { \ - if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \ - return false; \ - } \ + if (ret != MSGPACK_UNPACK_SUCCESS) { \ + return false; \ } \ \ + *arg = data.via.u64; \ return true; \ } \ \ - void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \ + void msgpack_rpc_from_##lt(t o, msgpack_packer *res) \ + FUNC_ATTR_NONNULL_ARG(2) \ { \ - msgpack_pack_array(res, result.size); \ - \ - for (size_t i = 0; i < result.size; i++) { \ - msgpack_rpc_from_##lt(result.items[i], res); \ - } \ - } \ - \ - void msgpack_rpc_free_##lt##array(t##Array value) { \ - for (size_t i = 0; i < value.size; i++) { \ - msgpack_rpc_free_##lt(value.items[i]); \ - } \ - \ - free(value.items); \ + msgpack_packer pac; \ + msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ + msgpack_pack_uint64(&pac, o); \ + msgpack_pack_ext(res, sbuffer.size, kObjectType##t); \ + msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ + msgpack_sbuffer_clear(&sbuffer); \ } +void msgpack_rpc_helpers_init(void) +{ + msgpack_zone_init(&zone, 0xfff); + msgpack_sbuffer_init(&sbuffer); +} + +HANDLE_TYPE_CONVERSION_IMPL(Buffer, buffer) +HANDLE_TYPE_CONVERSION_IMPL(Window, window) +HANDLE_TYPE_CONVERSION_IMPL(Tabpage, tabpage) + bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) + FUNC_ATTR_NONNULL_ALL { *arg = obj->via.boolean; return obj->type == MSGPACK_OBJECT_BOOLEAN; } bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER && obj->via.u64 <= INT64_MAX) { @@ -74,23 +80,27 @@ bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) } bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) + FUNC_ATTR_NONNULL_ALL { *arg = obj->via.dec; return obj->type == MSGPACK_OBJECT_DOUBLE; } bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) + FUNC_ATTR_NONNULL_ALL { - if (obj->type != MSGPACK_OBJECT_RAW) { + if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { + arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); + arg->size = obj->via.bin.size; + } else { return false; } - arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); - arg->size = obj->via.raw.size; return true; } bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) + FUNC_ATTR_NONNULL_ALL { switch (obj->type) { case MSGPACK_OBJECT_NIL: @@ -110,7 +120,8 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeFloat; return msgpack_rpc_to_float(obj, &arg->data.floating); - case MSGPACK_OBJECT_RAW: + case MSGPACK_OBJECT_BIN: + case MSGPACK_OBJECT_STR: arg->type = kObjectTypeString; return msgpack_rpc_to_string(obj, &arg->data.string); @@ -122,21 +133,22 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) arg->type = kObjectTypeDictionary; return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); + case MSGPACK_OBJECT_EXT: + switch (obj->via.ext.type) { + case kObjectTypeBuffer: + return msgpack_rpc_to_buffer(obj, &arg->data.buffer); + case kObjectTypeWindow: + return msgpack_rpc_to_window(obj, &arg->data.window); + case kObjectTypeTabpage: + return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage); + } default: return false; } } -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) -{ - return obj->type == MSGPACK_OBJECT_ARRAY - && obj->via.array.size == 2 - && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row) - && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col); -} - - bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_ARRAY) { return false; @@ -155,6 +167,7 @@ bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) } bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) + FUNC_ATTR_NONNULL_ALL { if (obj->type != MSGPACK_OBJECT_MAP) { return false; @@ -180,6 +193,7 @@ bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) } void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { if (result) { msgpack_pack_true(res); @@ -189,22 +203,26 @@ void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) } void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_int64(res, result); } void msgpack_rpc_from_float(Float result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_double(res, result); } void msgpack_rpc_from_string(String result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { - msgpack_pack_raw(res, result.size); - msgpack_pack_raw_body(res, result.data, result.size); + msgpack_pack_bin(res, result.size); + msgpack_pack_bin_body(res, result.data, result.size); } void msgpack_rpc_from_object(Object result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { switch (result.type) { case kObjectTypeNil: @@ -231,10 +249,6 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) msgpack_rpc_from_array(result.data.array, res); break; - case kObjectTypePosition: - msgpack_rpc_from_position(result.data.position, res); - break; - case kObjectTypeBuffer: msgpack_rpc_from_buffer(result.data.buffer, res); break; @@ -247,36 +261,14 @@ void msgpack_rpc_from_object(Object result, msgpack_packer *res) msgpack_rpc_from_tabpage(result.data.tabpage, res); break; - case kObjectTypeStringArray: - msgpack_rpc_from_stringarray(result.data.stringarray, res); - break; - - case kObjectTypeBufferArray: - msgpack_rpc_from_bufferarray(result.data.bufferarray, res); - break; - - case kObjectTypeWindowArray: - msgpack_rpc_from_windowarray(result.data.windowarray, res); - break; - - case kObjectTypeTabpageArray: - msgpack_rpc_from_tabpagearray(result.data.tabpagearray, res); - break; - case kObjectTypeDictionary: msgpack_rpc_from_dictionary(result.data.dictionary, res); break; } } -void msgpack_rpc_from_position(Position result, msgpack_packer *res) -{ - msgpack_pack_array(res, 2);; - msgpack_pack_int64(res, result.row); - msgpack_pack_int64(res, result.col); -} - void msgpack_rpc_from_array(Array result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_array(res, result.size); @@ -286,6 +278,7 @@ void msgpack_rpc_from_array(Array result, msgpack_packer *res) } void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_map(res, result.size); @@ -294,87 +287,3 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) msgpack_rpc_from_object(result.items[i].value, res); } } - -void msgpack_rpc_free_string(String value) -{ - if (!value.data) { - return; - } - - free(value.data); -} - -void msgpack_rpc_free_object(Object value) -{ - switch (value.type) { - case kObjectTypeNil: - case kObjectTypeBoolean: - case kObjectTypeInteger: - case kObjectTypeFloat: - case kObjectTypePosition: - case kObjectTypeBuffer: - case kObjectTypeWindow: - case kObjectTypeTabpage: - break; - - case kObjectTypeString: - msgpack_rpc_free_string(value.data.string); - break; - - case kObjectTypeArray: - msgpack_rpc_free_array(value.data.array); - break; - - case kObjectTypeStringArray: - msgpack_rpc_free_stringarray(value.data.stringarray); - break; - - case kObjectTypeBufferArray: - msgpack_rpc_free_bufferarray(value.data.bufferarray); - break; - - case kObjectTypeWindowArray: - msgpack_rpc_free_windowarray(value.data.windowarray); - break; - - case kObjectTypeTabpageArray: - msgpack_rpc_free_tabpagearray(value.data.tabpagearray); - break; - - case kObjectTypeDictionary: - msgpack_rpc_free_dictionary(value.data.dictionary); - break; - - default: - abort(); - } -} - -void msgpack_rpc_free_array(Array value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_object(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_dictionary(Dictionary value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i].key); - msgpack_rpc_free_object(value.items[i].value); - } - - free(value.items); -} - -REMOTE_FUNCS_IMPL(Buffer, buffer) -REMOTE_FUNCS_IMPL(Window, window) -REMOTE_FUNCS_IMPL(Tabpage, tabpage) - -TYPED_ARRAY_IMPL(Buffer, buffer) -TYPED_ARRAY_IMPL(Window, window) -TYPED_ARRAY_IMPL(Tabpage, tabpage) -TYPED_ARRAY_IMPL(String, string) - diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h index e3d1e756ef..aede6b1587 100644 --- a/src/nvim/os/msgpack_rpc_helpers.h +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -6,119 +6,11 @@ #include <msgpack.h> -#include "nvim/func_attr.h" #include "nvim/api/private/defs.h" -/// Functions for validating and converting from msgpack types to C types. -/// These are used by `msgpack_rpc_dispatch` to validate and convert each -/// argument. -/// -/// @param obj The object to convert -/// @param[out] arg A pointer to the avalue -/// @return true if the conversion succeeded, false otherwise -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_bufferarray(msgpack_object *obj, BufferArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_windowarray(msgpack_object *obj, WindowArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpagearray(msgpack_object *obj, TabpageArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) - FUNC_ATTR_NONNULL_ALL; - -/// Functions for converting from C types to msgpack types. -/// These are used by `msgpack_rpc_dispatch` to convert return values -/// from the API -/// -/// @param result A pointer to the result -/// @param res A packer that contains the response -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_float(Float result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_position(Position result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_string(String result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_window(Window result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_object(Object result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_bufferarray(BufferArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_windowarray(WindowArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpagearray(TabpageArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_array(Array result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); - -/// Helpers for initializing types that may be freed later -#define msgpack_rpc_init_boolean -#define msgpack_rpc_init_integer -#define msgpack_rpc_init_float -#define msgpack_rpc_init_position -#define msgpack_rpc_init_string = STRING_INIT -#define msgpack_rpc_init_buffer -#define msgpack_rpc_init_window -#define msgpack_rpc_init_tabpage -#define msgpack_rpc_init_object = {.type = kObjectTypeNil} -#define msgpack_rpc_init_stringarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_bufferarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_windowarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_tabpagearray = ARRAY_DICT_INIT -#define msgpack_rpc_init_array = ARRAY_DICT_INIT -#define msgpack_rpc_init_dictionary = ARRAY_DICT_INIT - -/// Helpers for freeing arguments/return value -/// -/// @param value The value to be freed -#define msgpack_rpc_free_boolean(value) -#define msgpack_rpc_free_integer(value) -#define msgpack_rpc_free_float(value) -#define msgpack_rpc_free_position(value) -void msgpack_rpc_free_string(String value); -#define msgpack_rpc_free_buffer(value) -#define msgpack_rpc_free_window(value) -#define msgpack_rpc_free_tabpage(value) -void msgpack_rpc_free_object(Object value); -void msgpack_rpc_free_stringarray(StringArray value); -void msgpack_rpc_free_bufferarray(BufferArray value); -void msgpack_rpc_free_windowarray(WindowArray value); -void msgpack_rpc_free_tabpagearray(TabpageArray value); -void msgpack_rpc_free_array(Array value); -void msgpack_rpc_free_dictionary(Dictionary value); +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc_helpers.h.generated.h" +#endif #endif // NVIM_OS_MSGPACK_RPC_HELPERS_H diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c index 07e757fe0e..2e7a677793 100644 --- a/src/nvim/os/provider.c +++ b/src/nvim/os/provider.c @@ -14,37 +14,34 @@ #include "nvim/log.h" #include "nvim/map.h" #include "nvim/message.h" -#include "nvim/os/msgpack_rpc_helpers.h" #define FEATURE_COUNT (sizeof(features) / sizeof(features[0])) -#define FEATURE(feature_name, provider_bootstrap_command, ...) { \ +#define FEATURE(feature_name, ...) { \ .name = feature_name, \ - .bootstrap_command = provider_bootstrap_command, \ - .argv = NULL, \ .channel_id = 0, \ .methods = (char *[]){__VA_ARGS__, NULL} \ } -static struct feature { - char *name, **bootstrap_command, **argv, **methods; +typedef struct { + char *name, **methods; size_t name_length; uint64_t channel_id; -} features[] = { +} Feature; + +static Feature features[] = { FEATURE("python", - &p_ipy, "python_execute", "python_execute_file", "python_do_range", "python_eval"), FEATURE("clipboard", - &p_icpb, "clipboard_get", "clipboard_set") }; -static Map(cstr_t, uint64_t) *registered_providers = NULL; +static PMap(cstr_t) *registered_providers = NULL; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/provider.c.generated.h" @@ -53,163 +50,101 @@ static Map(cstr_t, uint64_t) *registered_providers = NULL; void provider_init(void) { - registered_providers = map_new(cstr_t, uint64_t)(); + registered_providers = pmap_new(cstr_t)(); } bool provider_has_feature(char *name) { - for (size_t i = 0; i < FEATURE_COUNT; i++) { - struct feature *f = &features[i]; - if (!STRICMP(name, f->name)) { - return f->channel_id || can_execute(f); - } - } - - return false; + Feature *f = find_feature(name); + return f != NULL && channel_exists(f->channel_id); } -bool provider_available(char *method) +bool provider_register(char *name, uint64_t channel_id) { - return map_has(cstr_t, uint64_t)(registered_providers, method); -} + Feature *f = find_feature(name); -bool provider_register(char *method, uint64_t channel_id) -{ - if (map_has(cstr_t, uint64_t)(registered_providers, method)) { + if (!f) { return false; } - // First check if this method is part of a feature, and if so, update - // the feature structure with the channel id - struct feature *f = get_feature_for(method); - if (f) { - DLOG("Registering provider for \"%s\" " - "which is part of the \"%s\" feature", - method, - f->name); - f->channel_id = channel_id; + if (f->channel_id && channel_exists(f->channel_id)) { + ILOG("Feature \"%s\" is already provided by another channel" + "(will be replaced)", name); + } + + DLOG("Registering provider for \"%s\"", name); + f->channel_id = channel_id; + + // Associate all method names with the feature struct + size_t i; + char *method; + for (method = f->methods[i = 0]; method; method = f->methods[++i]) { + pmap_put(cstr_t)(registered_providers, method, f); + DLOG("Channel \"%" PRIu64 "\" will be sent requests for \"%s\"", + channel_id, + method); } - map_put(cstr_t, uint64_t)(registered_providers, xstrdup(method), channel_id); - ILOG("Registered channel %" PRIu64 " as the provider for \"%s\"", + ILOG("Registered channel %" PRIu64 " as the provider for the \"%s\" feature", channel_id, - method); + name); return true; } Object provider_call(char *method, Array args) { - uint64_t channel_id = get_provider_for(method); + Feature *f = pmap_get(cstr_t)(registered_providers, method); - if (!channel_id) { + if (!f || !channel_exists(f->channel_id)) { char buf[256]; snprintf(buf, sizeof(buf), - "Provider for \"%s\" is not available", + "Provider for method \"%s\" is not available", method); - report_error(buf); - msgpack_rpc_free_array(args); + vim_report_error(cstr_as_string(buf)); + api_free_array(args); return NIL; } bool error = false; Object result = NIL; - channel_send_call(channel_id, method, args, &result, &error); + channel_send_call(f->channel_id, method, args, &result, &error); if (error) { - report_error(result.data.string.data); - msgpack_rpc_free_object(result); + vim_report_error(result.data.string); + api_free_object(result); return NIL; } return result; } -static uint64_t get_provider_for(char *method) +void provider_init_feature_metadata(Dictionary *metadata) { - uint64_t channel_id = map_get(cstr_t, uint64_t)(registered_providers, method); + Dictionary md = ARRAY_DICT_INIT; - if (channel_id) { - return channel_id; - } - - // Try to bootstrap if the method is part of a feature - struct feature *f = get_feature_for(method); - - if (!f || !can_execute(f)) { - ELOG("Cannot bootstrap provider for \"%s\"", method); - goto err; - } - - if (f->channel_id) { - ELOG("Already bootstrapped provider for \"%s\"", f->name); - goto err; - } - - f->channel_id = channel_from_job(f->argv); - - if (!f->channel_id) { - ELOG("The provider for \"%s\" failed to bootstrap", f->name); - goto err; - } - - return f->channel_id; - -err: - // Ensure we won't try to restart the provider - if (f) { - f->bootstrap_command = NULL; - f->channel_id = 0; - } - return 0; -} - -static bool can_execute(struct feature *f) -{ - if (!f->bootstrap_command) { - return false; - } - - char *cmd = *f->bootstrap_command; - - if (!cmd || !strlen(cmd)) { - return false; - } - - if (!f->argv) { - f->argv = shell_build_argv((uint8_t *)cmd, NULL); - } - - return os_can_exe((uint8_t *)f->argv[0]); -} - -static void report_error(char *str) -{ - vim_err_write((String) {.data = str, .size = strlen(str)}); - vim_err_write((String) {.data = "\n", .size = 1}); -} - -static bool feature_has_method(struct feature *f, char *method) -{ - size_t i; - char *m; + for (size_t i = 0; i < FEATURE_COUNT; i++) { + Array methods = ARRAY_DICT_INIT; + Feature *f = &features[i]; - for (m = f->methods[i = 0]; m; m = f->methods[++i]) { - if (!STRCMP(method, m)) { - return true; + size_t j; + char *method; + for (method = f->methods[j = 0]; method; method = f->methods[++j]) { + ADD(methods, STRING_OBJ(cstr_to_string(method))); } + + PUT(md, f->name, ARRAY_OBJ(methods)); } - return false; + PUT(*metadata, "features", DICTIONARY_OBJ(md)); } - -static struct feature *get_feature_for(char *method) +static Feature * find_feature(char *name) { for (size_t i = 0; i < FEATURE_COUNT; i++) { - struct feature *f = &features[i]; - if (feature_has_method(f, method)) { + Feature *f = &features[i]; + if (!STRICMP(name, f->name)) { return f; } } diff --git a/src/nvim/os/server.c b/src/nvim/os/server.c index 2e8934ecfb..66dd0ecd88 100644 --- a/src/nvim/os/server.c +++ b/src/nvim/os/server.c @@ -17,6 +17,7 @@ #define MAX_CONNECTIONS 32 #define ADDRESS_MAX_SIZE 256 #define NEOVIM_DEFAULT_TCP_PORT 7450 +#define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS" typedef enum { kServerTypeTcp, @@ -51,13 +52,13 @@ void server_init(void) { servers = pmap_new(cstr_t)(); - if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) { + if (!os_getenv(LISTEN_ADDRESS_ENV_VAR)) { char *listen_address = (char *)vim_tempname(); - os_setenv("NEOVIM_LISTEN_ADDRESS", listen_address, 1); + os_setenv(LISTEN_ADDRESS_ENV_VAR, listen_address, 1); free(listen_address); } - server_start((char *)os_getenv("NEOVIM_LISTEN_ADDRESS")); + server_start((char *)os_getenv(LISTEN_ADDRESS_ENV_VAR)); } /// Teardown the server module diff --git a/src/nvim/os/wstream.c b/src/nvim/os/wstream.c index 44463c7c88..00a53d1628 100644 --- a/src/nvim/os/wstream.c +++ b/src/nvim/os/wstream.c @@ -72,12 +72,12 @@ WStream * wstream_new(size_t maxmem) /// @param wstream The `WStream` instance void wstream_free(WStream *wstream) { if (!wstream->pending_reqs) { - handle_set_wstream((uv_handle_t *)wstream->stream, NULL); if (wstream->free_handle) { uv_close((uv_handle_t *)wstream->stream, close_cb); + } else { + handle_set_wstream((uv_handle_t *)wstream->stream, NULL); + free(wstream); } - - free(wstream); } else { wstream->freed = true; } @@ -238,12 +238,7 @@ static void release_wbuffer(WBuffer *buffer) static void close_cb(uv_handle_t *handle) { - WStream *wstream = handle_get_wstream(handle); - - if (wstream) { - free(wstream); - } - + free(handle_get_wstream(handle)); free(handle->data); free(handle); } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 33b08d7df6..6c79fbd479 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -55,6 +55,7 @@ #include "nvim/os/signal.h" #include "nvim/os/job.h" #include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" #if defined(HAVE_SYS_IOCTL_H) # include <sys/ioctl.h> @@ -166,6 +167,7 @@ void mch_init(void) #endif msgpack_rpc_init(); + msgpack_rpc_helpers_init(); event_init(); } @@ -328,7 +330,7 @@ int len /* buffer size, only used when name gets longer */ struct dirent *dp; FileInfo file_info; - if (os_get_file_info_link((char *)name, &file_info)) { + if (os_fileinfo_link((char *)name, &file_info)) { /* Open the directory where the file is located. */ slash = vim_strrchr(name, '/'); if (slash == NULL) { @@ -354,8 +356,8 @@ int len /* buffer size, only used when name gets longer */ STRLCPY(newname + (tail - name), dp->d_name, MAXPATHL - (tail - name) + 1); FileInfo file_info_new; - if (os_get_file_info_link((char *)newname, &file_info_new) - && os_file_info_id_equal(&file_info, &file_info_new)) { + if (os_fileinfo_link((char *)newname, &file_info_new) + && os_fileinfo_id_equal(&file_info, &file_info_new)) { STRCPY(tail, dp->d_name); break; } diff --git a/src/nvim/path.c b/src/nvim/path.c index 0c18ab7bd4..4e05c506f8 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -61,10 +61,10 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) FileID file_id_1, file_id_2; expand_env(s1, exp1, MAXPATHL); - bool id_ok_1 = os_get_file_id((char *)exp1, &file_id_1); - bool id_ok_2 = os_get_file_id((char *)s2, &file_id_2); + bool id_ok_1 = os_fileid((char *)exp1, &file_id_1); + bool id_ok_2 = os_fileid((char *)s2, &file_id_2); if (!id_ok_1 && !id_ok_2) { - // If os_get_file_id() doesn't work, may compare the names. + // If os_fileid() doesn't work, may compare the names. if (checkname) { vim_FullName(exp1, full1, MAXPATHL, FALSE); vim_FullName(s2, full2, MAXPATHL, FALSE); @@ -77,7 +77,7 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) if (!id_ok_1 || !id_ok_2) { return kOneFileMissing; } - if (os_file_id_equal(&file_id_1, &file_id_2)) { + if (os_fileid_equal(&file_id_1, &file_id_2)) { return kEqualFiles; } return kDifferentFiles; @@ -1304,7 +1304,7 @@ void simplify_filename(char_u *filename) saved_char = p[-1]; p[-1] = NUL; FileInfo file_info; - if (!os_get_file_info_link((char *)filename, &file_info)) { + if (!os_fileinfo_link((char *)filename, &file_info)) { do_strip = TRUE; } p[-1] = saved_char; @@ -1327,7 +1327,7 @@ void simplify_filename(char_u *filename) * components. */ saved_char = *tail; *tail = NUL; - if (os_get_file_info((char *)filename, &file_info)) { + if (os_fileinfo((char *)filename, &file_info)) { do_strip = TRUE; } else @@ -1343,15 +1343,15 @@ void simplify_filename(char_u *filename) * component's parent directory.) */ FileInfo new_file_info; if (p == start && relative) { - os_get_file_info(".", &new_file_info); + os_fileinfo(".", &new_file_info); } else { saved_char = *p; *p = NUL; - os_get_file_info((char *)filename, &new_file_info); + os_fileinfo((char *)filename, &new_file_info); *p = saved_char; } - if (!os_file_info_id_equal(&file_info, &new_file_info)) { + if (!os_fileinfo_id_equal(&file_info, &new_file_info)) { do_strip = FALSE; /* We don't disable stripping of later * components since the unstripped path name is diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2415858e0f..ce2a80adaa 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1294,7 +1294,7 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) int len; int old_KeyTyped = KeyTyped; /* getting file may reset it */ int ok = OK; - int usable_win; + bool usable_win; if (qi == NULL) qi = &ql_info; @@ -1379,14 +1379,16 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) * For ":helpgrep" find a help window or open one. */ if (qf_ptr->qf_type == 1 && (!curwin->w_buffer->b_help || cmdmod.tab != 0)) { - win_T *wp; + win_T *wp = NULL; - if (cmdmod.tab != 0) - wp = NULL; - else - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer != NULL && wp->w_buffer->b_help) + if (cmdmod.tab == 0) { + FOR_ALL_WINDOWS(wp2) { + if (wp2->w_buffer != NULL && wp2->w_buffer->b_help) { + wp = wp2; break; + } + } + } if (wp != NULL && wp->w_buffer->b_nwindows > 0) win_enter(wp, true); else { @@ -1433,26 +1435,29 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) if (qf_ptr->qf_fnum == 0) goto theend; - usable_win = 0; + usable_win = false; ll_ref = curwin->w_llist_ref; if (ll_ref != NULL) { /* Find a window using the same location list that is not a * quickfix window. */ - FOR_ALL_WINDOWS(usable_win_ptr) - if (usable_win_ptr->w_llist == ll_ref - && usable_win_ptr->w_buffer->b_p_bt[0] != 'q') { - usable_win = 1; - break; + FOR_ALL_WINDOWS(wp) { + if (wp->w_llist == ll_ref + && wp->w_buffer->b_p_bt[0] != 'q') { + usable_win = true; + usable_win_ptr = wp; + break; + } } } if (!usable_win) { /* Locate a window showing a normal buffer */ - FOR_ALL_WINDOWS(win) - if (win->w_buffer->b_p_bt[0] == NUL) { - usable_win = 1; - break; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer->b_p_bt[0] == NUL) { + usable_win = true; + break; + } } } @@ -1468,7 +1473,7 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit) { if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) { goto_tabpage_win(tp, wp); - usable_win = 1; + usable_win = true; goto win_found; } } @@ -1501,9 +1506,12 @@ win_found: win = usable_win_ptr; if (win == NULL) { /* Find the window showing the selected file */ - FOR_ALL_WINDOWS(win) - if (win->w_buffer->b_fnum == qf_ptr->qf_fnum) - break; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer->b_fnum == qf_ptr->qf_fnum) { + win = wp; + break; + } + } if (win == NULL) { /* Find a previous usable window */ win = curwin; @@ -2212,13 +2220,13 @@ static int is_qf_win(win_T *win, qf_info_T *qi) */ static win_T *qf_find_win(qf_info_T *qi) { - win_T *win; - - FOR_ALL_WINDOWS(win) - if (is_qf_win(win, qi)) - break; + FOR_ALL_WINDOWS(win) { + if (is_qf_win(win, qi)) { + return win; + } + } - return win; + return NULL; } /* @@ -2564,7 +2572,7 @@ static char_u *get_mef_name(void) STRCAT(name, p + 2); // Don't accept a symbolic link, its a security risk. FileInfo file_info; - bool file_or_link_found = os_get_file_info_link((char *)name, &file_info); + bool file_or_link_found = os_fileinfo_link((char *)name, &file_info); if (!file_or_link_found) { break; } @@ -3478,7 +3486,6 @@ void ex_helpgrep(exarg_T *eap) char_u *lang; qf_info_T *qi = &ql_info; int new_qi = FALSE; - win_T *wp; char_u *au_name = NULL; /* Check for a specified language */ @@ -3501,16 +3508,16 @@ void ex_helpgrep(exarg_T *eap) p_cpo = empty_option; if (eap->cmdidx == CMD_lhelpgrep) { - /* Find an existing help window */ - FOR_ALL_WINDOWS(wp) - if (wp->w_buffer != NULL && wp->w_buffer->b_help) - break; + qi = NULL; - if (wp == NULL) /* Help window not found */ - qi = NULL; - else - qi = wp->w_llist; + /* Find an existing help window */ + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer != NULL && wp->w_buffer->b_help) { + qi = wp->w_llist; + } + } + /* Help window not found */ if (qi == NULL) { /* Allocate a new location list for help text matches */ qi = ll_new_list(); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 811f265902..122c23ed84 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -206,10 +206,7 @@ void redraw_later_clear(void) */ void redraw_all_later(int type) { - win_T *wp; - - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { redraw_win_later(wp, type); } } @@ -224,12 +221,10 @@ void redraw_curbuf_later(int type) void redraw_buf_later(buf_T *buf, int type) { - win_T *wp; - - FOR_ALL_WINDOWS(wp) - { - if (wp->w_buffer == buf) + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == buf) { redraw_win_later(wp, type); + } } } @@ -390,7 +385,6 @@ void update_curbuf(int type) */ void update_screen(int type) { - win_T *wp; static int did_intro = FALSE; int did_one; @@ -438,8 +432,7 @@ void update_screen(int type) check_for_delay(FALSE); if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) type = CLEAR; - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp->w_winrow < msg_scrolled) { if (wp->w_winrow + wp->w_height > msg_scrolled && wp->w_redr_type < REDRAW_TOP @@ -450,8 +443,9 @@ void update_screen(int type) } else { wp->w_redr_type = NOT_VALID; if (wp->w_winrow + wp->w_height + wp->w_status_height - <= msg_scrolled) + <= msg_scrolled) { wp->w_redr_status = TRUE; + } } } } @@ -512,8 +506,7 @@ void update_screen(int type) * Correct stored syntax highlighting info for changes in each displayed * buffer. Each buffer must only be done once. */ - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp->w_buffer->b_mod_set) { win_T *wwp; @@ -534,8 +527,7 @@ void update_screen(int type) */ did_one = FALSE; search_hl.rm.regprog = NULL; - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp->w_redr_type != 0) { cursor_off(); if (!did_one) { @@ -558,8 +550,9 @@ void update_screen(int type) /* Reset b_mod_set flags. Going through all windows is probably faster * than going through all buffers (there could be many buffers). */ - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { wp->w_buffer->b_mod_set = FALSE; + } updating_screen = FALSE; @@ -662,31 +655,28 @@ static void update_finish(void) void update_debug_sign(buf_T *buf, linenr_T lnum) { - win_T *wp; int doit = FALSE; win_foldinfo.fi_level = 0; /* update/delete a specific mark */ - FOR_ALL_WINDOWS(wp) - { - if (buf != NULL && lnum > 0) { - if (wp->w_buffer == buf && lnum >= wp->w_topline - && lnum < wp->w_botline) - { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { - wp->w_redraw_top = lnum; - } - if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { - wp->w_redraw_bot = lnum; - } - redraw_win_later(wp, VALID); - } - } else { - redraw_win_later(wp, VALID); - } - if (wp->w_redr_type != 0) { - doit = TRUE; + FOR_ALL_WINDOWS(wp) { + if (buf != NULL && lnum > 0) { + if (wp->w_buffer == buf && lnum >= wp->w_topline + && lnum < wp->w_botline) { + if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { + wp->w_redraw_top = lnum; + } + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { + wp->w_redraw_bot = lnum; + } + redraw_win_later(wp, VALID); } + } else { + redraw_win_later(wp, VALID); + } + if (wp->w_redr_type != 0) { + doit = TRUE; + } } /* Return when there is nothing to do, screen updating is already @@ -698,13 +688,13 @@ void update_debug_sign(buf_T *buf, linenr_T lnum) /* update all windows that need updating */ update_prepare(); - for (wp = firstwin; wp; wp = wp->w_next) { - if (wp->w_redr_type != 0) { - win_update(wp); - } - if (wp->w_redr_status) { - win_redr_status(wp); - } + FOR_ALL_WINDOWS(wp) { + if (wp->w_redr_type != 0) { + win_update(wp); + } + if (wp->w_redr_status) { + win_redr_status(wp); + } } update_finish(); @@ -1366,8 +1356,9 @@ static void win_update(win_T *wp) (foldmethodIsSyntax(wp) && hasAnyFolding(wp)) || syntax_check_changed(lnum))) - /* match in fixed position might need redraw */ - || wp->w_match_head != NULL + // match in fixed position might need redraw + // if lines were inserted or deleted + || (wp->w_match_head != NULL && buf->b_mod_xlines != 0) ))))) { if (lnum == mod_top) top_to_mod = FALSE; @@ -2658,38 +2649,42 @@ win_line ( shl->startcol = MAXCOL; shl->endcol = MAXCOL; shl->attr_cur = 0; - if (shl->rm.regprog != NULL) { - v = (long)(ptr - line); - next_search_hl(wp, shl, lnum, (colnr_T)v); + v = (long)(ptr - line); + if (cur != NULL) { + cur->pos.cur = 0; + } + next_search_hl(wp, shl, lnum, (colnr_T)v, cur); - /* Need to get the line again, a multi-line regexp may have made it - * invalid. */ - line = ml_get_buf(wp->w_buffer, lnum, FALSE); - ptr = line + v; + // Need to get the line again, a multi-line regexp may have made it + // invalid. + line = ml_get_buf(wp->w_buffer, lnum, false); + ptr = line + v; - if (shl->lnum != 0 && shl->lnum <= lnum) { - if (shl->lnum == lnum) - shl->startcol = shl->rm.startpos[0].col; - else - shl->startcol = 0; - if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) + if (shl->lnum != 0 && shl->lnum <= lnum) { + if (shl->lnum == lnum) { + shl->startcol = shl->rm.startpos[0].col; + } else { + shl->startcol = 0; + } + if (lnum == shl->lnum + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum) { shl->endcol = shl->rm.endpos[0].col; - else + } else { shl->endcol = MAXCOL; - /* Highlight one character for an empty match. */ - if (shl->startcol == shl->endcol) { - if (has_mbyte && line[shl->endcol] != NUL) - shl->endcol += (*mb_ptr2len)(line + shl->endcol); - else - ++shl->endcol; - } - if ((long)shl->startcol < v) { /* match at leftcol */ - shl->attr_cur = shl->attr; - search_attr = shl->attr; - } - area_highlighting = TRUE; } + // Highlight one character for an empty match. + if (shl->startcol == shl->endcol) { + if (has_mbyte && line[shl->endcol] != NUL) { + shl->endcol += (*mb_ptr2len)(line + shl->endcol); + } else { + ++shl->endcol; + } + } + if ((long)shl->startcol < v) { // match at leftcol + shl->attr_cur = shl->attr; + search_attr = shl->attr; + } + area_highlighting = true; } if (shl != &search_hl && cur != NULL) cur = cur->next; @@ -2699,9 +2694,9 @@ win_line ( * when Visual mode is active, because it's not clear what is selected * then. */ if (wp->w_p_cul && lnum == wp->w_cursor.lnum - && !(wp == curwin && VIsual_active)) { + && !(wp == curwin && VIsual_active)) { line_attr = hl_attr(HLF_CUL); - area_highlighting = TRUE; + area_highlighting = true; } off = (unsigned)(current_ScreenLine - ScreenLines); @@ -2942,15 +2937,22 @@ win_line ( shl_flag = TRUE; } else shl = &cur->hl; - while (shl->rm.regprog != NULL) { + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress + while (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress)) { if (shl->startcol != MAXCOL && v >= (long)shl->startcol && v < (long)shl->endcol) { shl->attr_cur = shl->attr; - } else if (v == (long)shl->endcol) { + } else if (v >= (long)shl->endcol) { shl->attr_cur = 0; - next_search_hl(wp, shl, lnum, (colnr_T)v); + next_search_hl(wp, shl, lnum, (colnr_T)v, cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); /* Need to get the line again, a multi-line regexp * may have made it invalid. */ @@ -3563,7 +3565,7 @@ win_line ( } else if (c != NUL) { p_extra = transchar(c); if (n_extra == 0) { - n_extra = byte2cells(c); + n_extra = byte2cells(c) - 1; } if ((dy_flags & DY_UHEX) && wp->w_p_rl) rl_mirror(p_extra); /* reverse "<12>" */ @@ -4552,13 +4554,13 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - win_T *wp; - for (wp = firstwin; wp; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_status_height) { wp->w_redr_status = TRUE; redraw_later(VALID); } + } } /* @@ -4566,13 +4568,12 @@ void status_redraw_all(void) */ void status_redraw_curbuf(void) { - win_T *wp; - - for (wp = firstwin; wp; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_status_height != 0 && wp->w_buffer == curbuf) { wp->w_redr_status = TRUE; redraw_later(VALID); } + } } /* @@ -4580,11 +4581,11 @@ void status_redraw_curbuf(void) */ void redraw_statuslines(void) { - win_T *wp; - - for (wp = firstwin; wp; wp = wp->w_next) - if (wp->w_redr_status) + FOR_ALL_WINDOWS(wp) { + if (wp->w_redr_status) { win_redr_status(wp); + } + } if (redraw_tabline) draw_tabline(); } @@ -5602,9 +5603,16 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) NULL, NULL, TRUE, NULL)) break; } + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress n = 0; - while (shl->first_lnum < lnum && shl->rm.regprog != NULL) { - next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n); + while (shl->first_lnum < lnum && (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress))) { + next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); if (shl->lnum != 0) { shl->first_lnum = shl->lnum + shl->rm.endpos[0].lnum @@ -5634,12 +5642,13 @@ next_search_hl ( win_T *win, match_T *shl, /* points to search_hl or a match */ linenr_T lnum, - colnr_T mincol /* minimal column for a match */ + colnr_T mincol, /* minimal column for a match */ + matchitem_T *cur /* to retrieve match positions if any */ ) { linenr_T l; colnr_T matchcol; - long nmatched; + long nmatched = 0; if (shl->lnum != 0) { /* Check for three situations: @@ -5674,8 +5683,8 @@ next_search_hl ( if (shl->lnum == 0) matchcol = 0; else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { + || (shl->rm.endpos[0].lnum == 0 + && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { char_u *ml; matchcol = shl->rm.startpos[0].col; @@ -5693,20 +5702,22 @@ next_search_hl ( matchcol = shl->rm.endpos[0].col; shl->lnum = lnum; - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, - &(shl->tm) - ); - if (called_emsg || got_int) { - /* Error while handling regexp: stop using this regexp. */ - if (shl == &search_hl) { - /* don't free regprog in the match list, it's a copy */ - vim_regfree(shl->rm.regprog); - SET_NO_HLSEARCH(TRUE); + if (shl->rm.regprog != NULL) { + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm)); + if (called_emsg || got_int) { + // Error while handling regexp: stop using this regexp. + if (shl == &search_hl) { + // don't free regprog in the match list, it's a copy + vim_regfree(shl->rm.regprog); + SET_NO_HLSEARCH(TRUE); + } + shl->rm.regprog = NULL; + shl->lnum = 0; + got_int = FALSE; // avoid the "Type :quit to exit Vim" message + break; } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = FALSE; /* avoid the "Type :quit to exit Vim" message */ - break; + } else if (cur != NULL) { + nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); } if (nmatched == 0) { shl->lnum = 0; /* no match found */ @@ -5722,6 +5733,59 @@ next_search_hl ( } } +static int +next_search_hl_pos( + match_T *shl, // points to a match + linenr_T lnum, + posmatch_T *posmatch, // match positions + colnr_T mincol // minimal column for a match +) +{ + int i; + int bot = -1; + + shl->lnum = 0; + for (i = posmatch->cur; i < MAXPOSMATCH; i++) { + if (posmatch->pos[i].lnum == 0) { + break; + } + if (posmatch->pos[i].col < mincol) { + continue; + } + if (posmatch->pos[i].lnum == lnum) { + if (shl->lnum == lnum) { + // partially sort positions by column numbers + // on the same line + if (posmatch->pos[i].col < posmatch->pos[bot].col) { + llpos_T tmp = posmatch->pos[i]; + + posmatch->pos[i] = posmatch->pos[bot]; + posmatch->pos[bot] = tmp; + } + } else { + bot = i; + shl->lnum = lnum; + } + } + } + posmatch->cur = 0; + if (shl->lnum == lnum) { + colnr_T start = posmatch->pos[bot].col == 0 + ? 0: posmatch->pos[bot].col - 1; + colnr_T end = posmatch->pos[bot].col == 0 + ? MAXCOL : start + posmatch->pos[bot].len; + + shl->rm.startpos[0].lnum = 0; + shl->rm.startpos[0].col = start; + shl->rm.endpos[0].lnum = 0; + shl->rm.endpos[0].col = end; + posmatch->cur = bot + 1; + return true; + } + return false; +} + + static void screen_start_highlight(int attr) { attrentry_T *aep = NULL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index d277d71d99..5e40ae7708 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -4155,8 +4155,6 @@ void spell_free_all(void) // Used after 'encoding' is set and when ":mkspell" was used. void spell_reload(void) { - win_T *wp; - // Initialize the table for spell_iswordp(). init_spell_chartab(); @@ -4164,7 +4162,7 @@ void spell_reload(void) spell_free_all(); // Go through all buffers and handle 'spelllang'. - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { // Only load the wordlists when 'spelllang' is set and there is a // window for this buffer in which 'spell' is set. if (*wp->w_s->b_p_spl != NUL) { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 5f6e09925e..5001d6498e 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -984,16 +984,13 @@ static void syn_stack_free_block(synblock_T *block) */ void syn_stack_free_all(synblock_T *block) { - win_T *wp; - syn_stack_free_block(block); - /* When using "syntax" fold method, must update all folds. */ - FOR_ALL_WINDOWS(wp) - { - if (wp->w_s == block && foldmethodIsSyntax(wp)) + FOR_ALL_WINDOWS(wp) { + if (wp->w_s == block && foldmethodIsSyntax(wp)) { foldUpdateAll(wp); + } } } @@ -1075,14 +1072,12 @@ static void syn_stack_alloc(void) */ void syn_stack_apply_changes(buf_T *buf) { - win_T *wp; - syn_stack_apply_changes_block(&buf->b_s, buf); - FOR_ALL_WINDOWS(wp) - { - if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s)) + FOR_ALL_WINDOWS(wp) { + if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s)) { syn_stack_apply_changes_block(wp->w_s, buf); + } } } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 92fd47ff6b..81dc49e800 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -33,7 +33,8 @@ SCRIPTS := test_autoformat_join.out \ test106.out test107.out \ test_options.out \ test_listlbr.out test_listlbr_utf8.out \ - test_breakindent.out + test_breakindent.out \ + test_insertcount.out SCRIPTS_GUI := test16.out diff --git a/src/nvim/testdir/test55.in b/src/nvim/testdir/test55.in index 85a8063774..8e073f30f2 100644 --- a/src/nvim/testdir/test55.in +++ b/src/nvim/testdir/test55.in @@ -332,6 +332,12 @@ let l = [0, 1, 2, 3] :$put =string(reverse(sort(l))) :$put =string(sort(reverse(sort(l)))) :$put =string(uniq(sort(l))) +:let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] +:$put =string(sort(copy(l), 'n')) +:let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] +:$put =string(sort(copy(l), 1)) +:$put =string(sort(copy(l), 'i')) +:$put =string(sort(copy(l))) :" :" splitting a string to a List :$put =string(split(' aa bb ')) diff --git a/src/nvim/testdir/test55.ok b/src/nvim/testdir/test55.ok index be476e8e93..dfd8060db7 100644 --- a/src/nvim/testdir/test55.ok +++ b/src/nvim/testdir/test55.ok @@ -101,6 +101,10 @@ caught a:000[3] [[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'] ['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]] ['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]] +[-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255] +['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] +['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] +['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}] ['aa', 'bb'] ['aa', 'bb'] ['', 'aa', 'bb', ''] diff --git a/src/nvim/testdir/test63.in b/src/nvim/testdir/test63.in index 74339c3e35..ea66ee6dea 100644 --- a/src/nvim/testdir/test63.in +++ b/src/nvim/testdir/test63.in @@ -1,5 +1,5 @@ Test for ":match", ":2match", ":3match", "clearmatches()", "getmatches()", -"matchadd()", "matcharg()", "matchdelete()", and "setmatches()". +"matchadd()", "matchaddpos()", "matcharg()", "matchdelete()", and "setmatches()". STARTTEST :so small.vim @@ -147,9 +147,26 @@ STARTTEST :unlet rf1 :unlet rf2 :unlet rf3 -:highlight clear MyGroup1 -:highlight clear MyGroup2 -:highlight clear MyGroup3 +:" --- Check that "matchaddpos()" positions matches correctly +:let @r .= "*** Test 11:\n" +:set nolazyredraw +:call setline(1, 'abcdefghijklmnopq') +:call matchaddpos("MyGroup1", [[1, 5], [1, 8, 3]], 10, 3) +:1 +:redraw! +:let v1 = screenattr(1, 1) +:let v5 = screenattr(1, 5) +:let v6 = screenattr(1, 6) +:let v8 = screenattr(1, 8) +:let v10 = screenattr(1, 10) +:let v11 = screenattr(1, 11) +:let @r .= string(getmatches())."\n" +:if v1 != v5 && v6 == v1 && v8 == v5 && v10 == v5 && v11 == v1 +: let @r .= "OK\n" +:else +: let @r .= "FAILED\n" +:endif +:call clearmatches() G"rp :/^Results/,$wq! test.out ENDTEST diff --git a/src/nvim/testdir/test63.ok b/src/nvim/testdir/test63.ok index 14973985eb..f804b693ac 100644 --- a/src/nvim/testdir/test63.ok +++ b/src/nvim/testdir/test63.ok @@ -9,3 +9,6 @@ Results of test63: *** Test 8: OK *** Test 9: OK *** Test 10: OK +*** Test 11: +[{'group': 'MyGroup1', 'id': 3, 'priority': 10, 'pos1': [1, 5, 1], 'pos2': [1, 8, 3]}] +OK diff --git a/src/nvim/testdir/test86.in b/src/nvim/testdir/test86.in index ecb06bafd3..11ff35cfd3 100644 --- a/src/nvim/testdir/test86.in +++ b/src/nvim/testdir/test86.in @@ -9,7 +9,7 @@ STARTTEST :so small.vim :set encoding=latin1 :set noswapfile -:if !has('python') || has('neovim') | e! test.ok | wq! test.out | endif +:if !has('python') || has('nvim') | e! test.ok | wq! test.out | endif :lang C :fun Test() :py import vim diff --git a/src/nvim/testdir/test_insertcount.in b/src/nvim/testdir/test_insertcount.in new file mode 100644 index 0000000000..7a40573e63 --- /dev/null +++ b/src/nvim/testdir/test_insertcount.in @@ -0,0 +1,14 @@ +Tests for repeating insert and replace. + +STARTTEST +:so small.vim +:/Second +4gro +:/^First/,$wq! test.out +:" get here when failed and in Insert mode +:.wq! test.out +ENDTEST + +First line +Second line +Last line diff --git a/src/nvim/testdir/test_insertcount.ok b/src/nvim/testdir/test_insertcount.ok new file mode 100644 index 0000000000..57afab00ff --- /dev/null +++ b/src/nvim/testdir/test_insertcount.ok @@ -0,0 +1,3 @@ +First line +ooooecond line +Last line diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8c7b5b38e9..9fbe21aeb0 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1116,8 +1116,8 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash) */ FileInfo file_info_old; FileInfo file_info_new; - if (os_get_file_info((char *)buf->b_ffname, &file_info_old) - && os_get_file_info((char *)file_name, &file_info_new) + if (os_fileinfo((char *)buf->b_ffname, &file_info_old) + && os_fileinfo((char *)file_name, &file_info_new) && file_info_old.stat.st_gid != file_info_new.stat.st_gid && os_fchown(fd, -1, file_info_old.stat.st_gid) != 0) { os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); @@ -1249,8 +1249,8 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name) * owner of the text file or equal to the current user. */ FileInfo file_info_orig; FileInfo file_info_undo; - if (os_get_file_info((char *)orig_name, &file_info_orig) - && os_get_file_info((char *)file_name, &file_info_undo) + if (os_fileinfo((char *)orig_name, &file_info_orig) + && os_fileinfo((char *)file_name, &file_info_undo) && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid && file_info_undo.stat.st_uid != getuid()) { if (p_verbose > 0) { @@ -2191,12 +2191,10 @@ u_undo_end ( u_add_time(msgbuf, sizeof(msgbuf), uhp->uh_time); { - win_T *wp; - - FOR_ALL_WINDOWS(wp) - { - if (wp->w_buffer == curbuf && wp->w_p_cole > 0) + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == curbuf && wp->w_p_cole > 0) { redraw_win_later(wp, NOT_VALID); + } } } diff --git a/src/nvim/version.c b/src/nvim/version.c index b164f94bb1..bf0dace6ef 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -186,17 +186,17 @@ static char *(features[]) = { static int included_patches[] = { // Add new patch number below this line //410, - //409, + //409 NA //408, //407, //406, //405, - //404, - //403, + //404 NA + //403 NA //402, - //401, - //400, - //399, + //401 NA + //400 NA + //399 NA //398, //397, //396, @@ -208,10 +208,10 @@ static int included_patches[] = { //390, //389, 388, - //387, + 387, //386, //385, - //384, + //384 NA //383, //382, //381, @@ -219,53 +219,53 @@ static int included_patches[] = { //379, //378, //377, - //376, + 376, //375, //374, - //373, + //373 NA //372, 371, 370, - //369, - //368, - //367, + 369, + 368, + 367, //366, //365, //364, //363, - //362, + 362, //361, //360, //359, - //358, - //357, + 358, + 357, //356 NA //355, - //354, + //354 NA 353, 352, - //351, + 351, //350, - //349, - //348, - //347, + 349, + 348, + 347, 346, - //345, - //344, - //343, + 345, + 344, + 343, //342 NA - //341, + 341, //340 NA 339, 338, - //337, + 337, //336, 335, - //334, + 334, //333 NA //332 NA 331, - //330, + 330, 329, 328, 327, @@ -283,7 +283,7 @@ static int included_patches[] = { 315, 314, //313, - //312, + 312, //311, //310, 309, @@ -298,7 +298,7 @@ static int included_patches[] = { //300 NA //299 NA 298, - //297, + 297, 296, 295, 294, @@ -312,19 +312,19 @@ static int included_patches[] = { 286, 285, 284, - //283, + //283 NA 282, 281, 280, 279, - //278, + 278, 277, 276, 275, 274, //273 NA 272, - //271, + //271 NA //270 NA 269, 268, @@ -332,11 +332,11 @@ static int included_patches[] = { 266, 265, 264, - //263, + //263 NA 262, 261, 260, - //259, + //259 NA //258 NA //257 NA //256, @@ -345,7 +345,7 @@ static int included_patches[] = { 253, //252 NA 251, - //250, + //250 NA //249, //248, //247, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 6479aeaafb..3ef291ef7c 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -8,6 +8,8 @@ #ifndef NVIM_VIM_H # define NVIM_VIM_H +#define min(X, Y) (X < Y ? X : Y) + #include "nvim/types.h" #include "nvim/pos.h" // for linenr_T, MAXCOL, etc... diff --git a/src/nvim/window.c b/src/nvim/window.c index 3499b14688..27fb160035 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -982,13 +982,13 @@ static void win_init_some(win_T *newp, win_T *oldp) */ int win_valid(win_T *win) { - win_T *wp; - if (win == NULL) return FALSE; - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp == win) + FOR_ALL_WINDOWS(wp) { + if (wp == win) { return TRUE; + } + } return FALSE; } @@ -997,11 +997,11 @@ int win_valid(win_T *win) */ int win_count(void) { - win_T *wp; int count = 0; - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { ++count; + } return count; } @@ -1677,20 +1677,19 @@ static int last_window(void) * Return TRUE if there is only one window other than "aucmd_win" in the * current tab page. */ -int one_window(void) +bool one_window(void) { - win_T *wp; - int seen_one = FALSE; + bool seen_one = false; - FOR_ALL_WINDOWS(wp) - { + FOR_ALL_WINDOWS(wp) { if (wp != aucmd_win) { - if (seen_one) - return FALSE; - seen_one = TRUE; + if (seen_one) { + return false; + } + seen_one = true; } } - return TRUE; + return true; } /* @@ -2002,6 +2001,10 @@ void win_free_all(void) while (firstwin != NULL) (void)win_free_mem(firstwin, &dummy, NULL); + + // No window should be used after this. Set curwin to NULL to crash + // instead of using freed memory. + curwin = NULL; } #endif @@ -3476,14 +3479,14 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri */ win_T *buf_jump_open_win(buf_T *buf) { - win_T *wp; + FOR_ALL_WINDOWS(wp) { + if (wp->w_buffer == buf) { + win_enter(wp, false); + return wp; + } + } - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) - break; - if (wp != NULL) - win_enter(wp, false); - return wp; + return NULL; } /* @@ -3785,11 +3788,9 @@ void shell_new_columns(void) void win_size_save(garray_T *gap) { - win_T *wp; - ga_init(gap, (int)sizeof(int), 1); ga_grow(gap, win_count() * 2); - for (wp = firstwin; wp != NULL; wp = wp->w_next) { + FOR_ALL_WINDOWS(wp) { ((int *)gap->ga_data)[gap->ga_len++] = wp->w_width + wp->w_vsep_width; ((int *)gap->ga_data)[gap->ga_len++] = wp->w_height; @@ -3802,13 +3803,16 @@ void win_size_save(garray_T *gap) */ void win_size_restore(garray_T *gap) { - win_T *wp; - if (win_count() * 2 == gap->ga_len) { - int i = 0; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]); - win_setheight_win(((int *)gap->ga_data)[i++], wp); + /* The order matters, because frames contain other frames, but it's + * difficult to get right. The easy way out is to do it twice. */ + for (int j = 0; j < 2; ++j) + { + int i = 0; + FOR_ALL_WINDOWS(wp) { + frame_setwidth(wp->w_frame, ((int *)gap->ga_data)[i++]); + win_setheight_win(((int *)gap->ga_data)[i++], wp); + } } /* recompute the window positions */ (void)win_comp_pos(); @@ -4234,14 +4238,14 @@ void win_setminheight(void) { int room; int first = TRUE; - win_T *wp; /* loop until there is a 'winminheight' that is possible */ while (p_wmh > 0) { /* TODO: handle vertical splits */ room = -p_wh; - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { room += wp->w_height - p_wmh; + } if (room >= 0) break; --p_wmh; @@ -4932,20 +4936,21 @@ int min_rows(void) int only_one_window(void) { int count = 0; - win_T *wp; /* If there is another tab page there always is another window. */ if (first_tabpage->tp_next != NULL) return FALSE; - for (wp = firstwin; wp != NULL; wp = wp->w_next) + FOR_ALL_WINDOWS(wp) { if (wp->w_buffer != NULL && (!((wp->w_buffer->b_help && !curbuf->b_help) || wp->w_p_pvw ) || wp == curwin) && wp != aucmd_win - ) + ) { ++count; + } + } return count <= 1; } @@ -5190,16 +5195,18 @@ void restore_buffer(buf_T *save_curbuf) * If no particular ID is desired, -1 must be specified for 'id'. * Return ID of added match, -1 on failure. */ -int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id) +int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos_list) { matchitem_T *cur; matchitem_T *prev; matchitem_T *m; int hlg_id; - regprog_T *regprog; + regprog_T *regprog = NULL; + int rtype = SOME_VALID; - if (*grp == NUL || *pat == NUL) + if (*grp == NUL || (pat != NULL && *pat == NUL)) { return -1; + } if (id < -1 || id == 0) { EMSGN("E799: Invalid ID: %" PRId64 " (must be greater than or equal to 1)", @@ -5220,7 +5227,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id) EMSG2(_(e_nogroup), grp); return -1; } - if ((regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5236,15 +5243,106 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id) } /* Build new match. */ - m = xmalloc(sizeof(matchitem_T)); + m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = vim_strsave(pat); + m->pattern = pat == NULL ? NULL: vim_strsave(pat); m->hlg_id = hlg_id; m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; + // Set up position matches + if (pos_list != NULL) + { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + listitem_T *li; + int i; + + for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; + i++, li = li->li_next) { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; + list_T *subl; + listitem_T *subli; + int error = false; + + if (li->li_tv.v_type == VAR_LIST) { + subl = li->li_tv.vval.v_list; + if (subl == NULL) { + goto fail; + } + subli = subl->lv_first; + if (subli == NULL) { + goto fail; + } + lnum = get_tv_number_chk(&subli->li_tv, &error); + if (error == true) { + goto fail; + } + if (lnum == 0) { + --i; + continue; + } + m->pos.pos[i].lnum = lnum; + subli = subli->li_next; + if (subli != NULL) { + col = get_tv_number_chk(&subli->li_tv, &error); + if (error == true) + goto fail; + subli = subli->li_next; + if (subli != NULL) { + len = get_tv_number_chk(&subli->li_tv, &error); + if (error == true) { + goto fail; + } + } + } + m->pos.pos[i].col = col; + m->pos.pos[i].len = len; + } else if (li->li_tv.v_type == VAR_NUMBER) { + if (li->li_tv.vval.v_number == 0) { + --i; + continue; + } + m->pos.pos[i].lnum = li->li_tv.vval.v_number; + m->pos.pos[i].col = 0; + m->pos.pos[i].len = 0; + } else { + EMSG(_("List or number required")); + goto fail; + } + if (toplnum == 0 || lnum < toplnum) { + toplnum = lnum; + } + if (botlnum == 0 || lnum >= botlnum) { + botlnum = lnum + 1; + } + } + + // Calculate top and bottom lines for redrawing area + if (toplnum != 0){ + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > toplnum) { + wp->w_buffer->b_mod_top = toplnum; + } + if (wp->w_buffer->b_mod_bot < botlnum) { + wp->w_buffer->b_mod_bot = botlnum; + } + } else { + wp->w_buffer->b_mod_set = TRUE; + wp->w_buffer->b_mod_top = toplnum; + wp->w_buffer->b_mod_bot = botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + m->pos.toplnum = toplnum; + m->pos.botlnum = botlnum; + rtype = VALID; + } + } + /* Insert new match. The match list is in ascending order with regard to * the match priorities. */ cur = wp->w_match_head; @@ -5259,8 +5357,12 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id) prev->next = m; m->next = cur; - redraw_later(SOME_VALID); + redraw_later(rtype); return id; + +fail: + free(m); + return -1; } /* @@ -5271,6 +5373,7 @@ int match_delete(win_T *wp, int id, int perr) { matchitem_T *cur = wp->w_match_head; matchitem_T *prev = cur; + int rtype = SOME_VALID; if (id < 1) { if (perr == TRUE) @@ -5294,8 +5397,24 @@ int match_delete(win_T *wp, int id, int perr) prev->next = cur->next; vim_regfree(cur->match.regprog); free(cur->pattern); + if (cur->pos.toplnum != 0) { + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { + wp->w_buffer->b_mod_top = cur->pos.toplnum; + } + if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + } + } else { + wp->w_buffer->b_mod_set = TRUE; + wp->w_buffer->b_mod_top = cur->pos.toplnum; + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + rtype = VALID; + } free(cur); - redraw_later(SOME_VALID); + redraw_later(rtype); return 0; } |