diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/nvim/api/buffer.c | 210 | ||||
-rw-r--r-- | src/nvim/api/private/defs.h | 4 | ||||
-rw-r--r-- | src/nvim/api/private/dispatch.c | 45 | ||||
-rw-r--r-- | src/nvim/api/private/dispatch.h | 2 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 21 | ||||
-rw-r--r-- | src/nvim/api/tabpage.c | 77 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 282 | ||||
-rw-r--r-- | src/nvim/api/window.c | 134 | ||||
-rw-r--r-- | src/nvim/edit.c | 55 | ||||
-rw-r--r-- | src/nvim/eval.c | 35 | ||||
-rw-r--r-- | src/nvim/event/rstream.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_eval.h | 22 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 1 | ||||
-rw-r--r-- | src/nvim/fold.c | 13 | ||||
-rw-r--r-- | src/nvim/if_cscope.c | 43 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 2 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 2 | ||||
-rw-r--r-- | src/nvim/normal.c | 3 | ||||
-rw-r--r-- | src/nvim/option.c | 9 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 8 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 10 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_alot.vim | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_history.vim | 65 | ||||
-rw-r--r-- | src/nvim/testdir/test_tabpage.vim | 189 | ||||
-rw-r--r-- | src/nvim/version.c | 25 | ||||
-rw-r--r-- | src/nvim/window.c | 65 |
28 files changed, 928 insertions, 410 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index cbea6a05c9..49edfda838 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -18,7 +18,8 @@ set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) -set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c) +set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) +set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) @@ -197,8 +198,11 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} ${UNICODE_FILES} ) -add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${API_HEADERS} ${GENERATED_API_DISPATCH} ${API_METADATA} +add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} + ${API_METADATA} + COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} + ${API_HEADERS} ${GENERATED_API_DISPATCH} + ${GENERATED_FUNCS_METADATA} ${API_METADATA} DEPENDS ${API_HEADERS} ${MSGPACK_RPC_HEADERS} diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c4415ddf94..eaaae943d2 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -28,9 +28,9 @@ /// Gets the buffer line count /// -/// @param buffer The buffer handle -/// @param[out] err Details of an error that may have occurred -/// @return The line count +/// @param buffer Buffer handle +/// @param[out] err Error details, if any +/// @return Line count Integer nvim_buf_line_count(Buffer buffer, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -50,10 +50,10 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// for negative indices use /// "nvim_buf_get_lines(buffer, index-1, index, true)" /// -/// @param buffer The buffer handle -/// @param index The line index -/// @param[out] err Details of an error that may have occurred -/// @return The line string +/// @param buffer Buffer handle +/// @param index Line index +/// @param[out] err Error details, if any +/// @return Line string String buffer_get_line(Buffer buffer, Integer index, Error *err) { String rv = { .size = 0 }; @@ -78,10 +78,10 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// for negative indices use /// "nvim_buf_set_lines(buffer, index-1, index, true, [line])" /// -/// @param buffer The buffer handle -/// @param index The line index -/// @param line The new line. -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param index Line index +/// @param line Contents of the new line +/// @param[out] err Error details, if any void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { Object l = STRING_OBJ(line); @@ -97,9 +97,9 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// "nvim_buf_set_lines(buffer, index, index+1, true, [])" /// for negative indices use /// "nvim_buf_set_lines(buffer, index-1, index, true, [])" -/// @param buffer The buffer handle -/// @param index The line index -/// @param[out] err Details of an error that may have occurred +/// @param buffer buffer handle +/// @param index line index +/// @param[out] err Error details, if any void buffer_del_line(Buffer buffer, Integer index, Error *err) { Array array = ARRAY_DICT_INIT; @@ -113,13 +113,13 @@ void buffer_del_line(Buffer buffer, Integer index, Error *err) /// where newstart = start + int(not include_start) - int(start < 0) /// newend = end + int(include_end) - int(end < 0) /// int(bool) = 1 if bool is true else 0 -/// @param buffer The buffer handle -/// @param start The first line index -/// @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[out] err Details of an error that may have occurred -/// @return An array of lines +/// @param buffer Buffer handle +/// @param start First line index +/// @param end 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[out] err Error details, if any +/// @return Array of lines ArrayOf(String) buffer_get_line_slice(Buffer buffer, Integer start, Integer end, @@ -142,12 +142,12 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, /// Out-of-bounds indices are clamped to the nearest valid value, unless /// `strict_indexing` is set. /// -/// @param buffer The buffer handle -/// @param start The first line index -/// @param end The last line index (exclusive) -/// @param strict_indexing whether out-of-bounds should be an error. -/// @param[out] err Details of an error that may have occurred -/// @return An array of lines +/// @param buffer Buffer handle +/// @param start First line index +/// @param end Last line index (exclusive) +/// @param strict_indexing Whether out-of-bounds should be an error. +/// @param[out] err Error details, if any +/// @return Array of lines ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Buffer buffer, Integer start, @@ -191,7 +191,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Object str = STRING_OBJ(cstr_to_string(bufstr)); // Vim represents NULs as NLs, but this may confuse clients. - if (channel_id != INVALID_CHANNEL) { + if (channel_id != INTERNAL_CALL) { strchrsub(str.data.string.data, '\n', '\0'); } @@ -219,14 +219,14 @@ end: /// newend = end + int(include_end) + int(end < 0) /// int(bool) = 1 if bool is true else 0 /// -/// @param buffer The buffer handle -/// @param start The first line index -/// @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 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 +/// @param buffer Buffer handle +/// @param start First line index +/// @param end 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 replacement Array of lines to use as replacement (0-length +// array will delete the line range) +/// @param[out] err Error details, if any void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, @@ -253,12 +253,12 @@ void buffer_set_line_slice(Buffer buffer, /// Out-of-bounds indices are clamped to the nearest valid value, unless /// `strict_indexing` is set. /// -/// @param buffer The buffer handle -/// @param start The first line index -/// @param end The last line index (exclusive) -/// @param strict_indexing whether out-of-bounds should be an error. -/// @param replacement An array of lines to use as replacement -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param start First line index +/// @param end Last line index (exclusive) +/// @param strict_indexing Whether out-of-bounds should be an error. +/// @param replacement Array of lines to use as replacement +/// @param[out] err Error details, if any void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, @@ -312,7 +312,7 @@ void nvim_buf_set_lines(uint64_t channel_id, // line and convert NULs to newlines to avoid truncation. lines[i] = xmallocz(l.size); for (size_t j = 0; j < l.size; j++) { - if (l.data[j] == '\n' && channel_id != INVALID_CHANNEL) { + if (l.data[j] == '\n' && channel_id != INTERNAL_CALL) { api_set_error(err, Exception, _("string cannot contain newlines")); new_len = i + 1; goto end; @@ -411,10 +411,10 @@ end: /// Gets a buffer-scoped (b:) variable. /// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value +/// @param buffer Buffer handle +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Variable value Object nvim_buf_get_var(Buffer buffer, String name, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -428,10 +428,10 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) /// Sets a buffer-scoped (b:) variable /// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -445,9 +445,9 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) /// Removes a buffer-scoped (b:) variable /// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param name Variable name +/// @param[out] err Error details, if any void nvim_buf_del_var(Buffer buffer, String name, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -463,11 +463,11 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) /// /// @deprecated /// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value or nil if there was no previous value. +/// @param buffer Buffer handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. @@ -486,10 +486,10 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) /// /// @deprecated /// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @param buffer Buffer handle +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Old value Object buffer_del_var(Buffer buffer, String name, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -504,10 +504,10 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) /// Gets a buffer option value /// -/// @param buffer The buffer handle -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value +/// @param buffer Buffer handle +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value Object nvim_buf_get_option(Buffer buffer, String name, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -519,13 +519,13 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) return get_option_from(buf, SREQ_BUF, name, err); } -/// Sets a buffer option value. Passing 'nil' as value deletes the option(only +/// Sets a buffer option value. Passing 'nil' as value deletes the option (only /// works if there's a global fallback) /// -/// @param buffer The buffer handle -/// @param name The option name -/// @param value The option value -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param name Option name +/// @param value Option value +/// @param[out] err Error details, if any void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -539,9 +539,9 @@ void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err) /// Gets the buffer number /// -/// @param buffer The buffer handle -/// @param[out] err Details of an error that may have occurred -/// @return The buffer number +/// @param buffer Buffer handle +/// @param[out] err Error details, if any +/// @return Buffer number Integer nvim_buf_get_number(Buffer buffer, Error *err) { Integer rv = 0; @@ -556,9 +556,9 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err) /// Gets the full file name for the buffer /// -/// @param buffer The buffer handle -/// @param[out] err Details of an error that may have occurred -/// @return The buffer name +/// @param buffer Buffer handle +/// @param[out] err Error details, if any +/// @return Buffer name String nvim_buf_get_name(Buffer buffer, Error *err) { String rv = STRING_INIT; @@ -573,9 +573,9 @@ String nvim_buf_get_name(Buffer buffer, Error *err) /// Sets the full file name for a buffer /// -/// @param buffer The buffer handle -/// @param name The buffer name -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param name Buffer name +/// @param[out] err Error details, if any void nvim_buf_set_name(Buffer buffer, String name, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -603,7 +603,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err) /// Checks if a buffer is valid /// -/// @param buffer The buffer handle +/// @param buffer Buffer handle /// @return true if the buffer is valid, false otherwise Boolean nvim_buf_is_valid(Buffer buffer) { @@ -615,11 +615,11 @@ Boolean nvim_buf_is_valid(Buffer buffer) /// /// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) /// -/// @param buffer The buffer handle -/// @param lnum Insert the lines after `lnum`. If negative, it will append -/// to the end of the buffer. -/// @param lines An array of lines -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param lnum Insert the lines after `lnum`. If negative, appends to +/// the end of the buffer. +/// @param lines Array of lines +/// @param[out] err Error details, if any void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, @@ -632,10 +632,10 @@ void buffer_insert(Buffer buffer, /// Return a tuple (row,col) representing the position of the named mark /// -/// @param buffer The buffer handle -/// @param name The mark's name -/// @param[out] err Details of an error that may have occurred -/// @return The (row, col) tuple +/// @param buffer Buffer handle +/// @param name Mark name +/// @param[out] err Error details, if any +/// @return (row, col) tuple ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) { Array rv = ARRAY_DICT_INIT; @@ -694,15 +694,15 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// request an unique src_id at initialization, and later asynchronously add and /// clear highlights in response to buffer changes. /// -/// @param buffer The buffer handle -/// @param src_id Source group to use or 0 to use a new group, -/// or -1 for ungrouped highlight -/// @param hl_group Name of the highlight group to use -/// @param line The line to highlight -/// @param col_start Start of range of columns to highlight -/// @param col_end End of range of columns to highlight, -/// or -1 to highlight to end of line -/// @param[out] err Details of an error that may have occurred +/// @param buffer Buffer handle +/// @param src_id Source group to use or 0 to use a new group, +/// or -1 for ungrouped highlight +/// @param hl_group Name of the highlight group to use +/// @param line Line to highlight +/// @param col_start Start of range of columns to highlight +/// @param col_end End of range of columns to highlight, +/// or -1 to highlight to end of line +/// @param[out] err Error details, if any /// @return The src_id that was used Integer nvim_buf_add_highlight(Buffer buffer, Integer src_id, @@ -740,12 +740,12 @@ Integer nvim_buf_add_highlight(Buffer buffer, /// To clear a source group in the entire buffer, pass in 1 and -1 to /// line_start and line_end respectively. /// -/// @param buffer The buffer handle -/// @param src_id Highlight source group to clear, or -1 to clear all groups. +/// @param buffer Buffer handle +/// @param src_id Highlight source group to clear, or -1 to clear all. /// @param line_start Start of range of lines to clear -/// @param line_end End of range of lines to clear (exclusive) -/// or -1 to clear to end of file. -/// @param[out] err Details of an error that may have occurred +/// @param line_end End of range of lines to clear (exclusive) or -1 to clear +/// to end of file. +/// @param[out] err Error details, if any void nvim_buf_clear_highlight(Buffer buffer, Integer src_id, Integer line_start, diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index a6710193ff..1d5ecd3071 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -33,8 +33,8 @@ typedef enum { /// Used as the message ID of notifications. #define NO_RESPONSE UINT64_MAX -/// Used as channel_id when the call is local -#define INVALID_CHANNEL UINT64_MAX +/// Used as channel_id when the call is local. +#define INTERNAL_CALL UINT64_MAX typedef struct { ErrorType type; diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c new file mode 100644 index 0000000000..9b3bcc380a --- /dev/null +++ b/src/nvim/api/private/dispatch.c @@ -0,0 +1,45 @@ +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <assert.h> +#include <msgpack.h> + +#include "nvim/map.h" +#include "nvim/log.h" +#include "nvim/vim.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" + +#include "nvim/api/buffer.h" +#include "nvim/api/tabpage.h" +#include "nvim/api/ui.h" +#include "nvim/api/vim.h" +#include "nvim/api/window.h" + +static Map(String, MsgpackRpcRequestHandler) *methods = NULL; + +static void msgpack_rpc_add_method_handler(String method, + MsgpackRpcRequestHandler handler) +{ + map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); +} + +MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, + size_t name_len) +{ + String m = { .data = (char *)name, .size = name_len }; + MsgpackRpcRequestHandler rv = + map_get(String, MsgpackRpcRequestHandler)(methods, m); + + if (!rv.fn) { + rv.fn = msgpack_rpc_handle_missing_method; + } + + return rv; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "api/private/dispatch_wrappers.generated.h" +#endif diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index d91456c306..39aabd708a 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -4,7 +4,6 @@ #include "nvim/api/private/defs.h" typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, - uint64_t request_id, Array args, Error *error); @@ -18,6 +17,7 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/dispatch.h.generated.h" +# include "api/private/dispatch_wrappers.h.generated.h" #endif #endif // NVIM_API_PRIVATE_DISPATCH_H diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d80ee7dc67..c0ee735d1a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -7,6 +7,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/handle.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -27,6 +28,7 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.c.generated.h" +# include "api/private/funcs_metadata.generated.h" #endif /// Start block that may cause vimscript exceptions @@ -761,7 +763,7 @@ Dictionary api_metadata(void) static Dictionary metadata = ARRAY_DICT_INIT; if (!metadata.size) { - msgpack_rpc_init_function_metadata(&metadata); + init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); } @@ -769,6 +771,22 @@ Dictionary api_metadata(void) return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; } +static void init_function_metadata(Dictionary *metadata) +{ + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + if (msgpack_unpack_next(&unpacked, + (const char *)funcs_metadata, + sizeof(funcs_metadata), + NULL) != MSGPACK_UNPACK_SUCCESS) { + abort(); + } + Object functions; + msgpack_rpc_to_object(&unpacked.data, &functions); + msgpack_unpacked_destroy(&unpacked); + PUT(*metadata, "functions", functions); +} + static void init_error_type_metadata(Dictionary *metadata) { Dictionary types = ARRAY_DICT_INIT; @@ -784,6 +802,7 @@ static void init_error_type_metadata(Dictionary *metadata) PUT(*metadata, "error_types", DICTIONARY_OBJ(types)); } + static void init_type_metadata(Dictionary *metadata) { Dictionary types = ARRAY_DICT_INIT; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 8b1fb041e2..9e61ec1871 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -11,9 +11,9 @@ /// Gets the windows in a tabpage /// -/// @param tabpage The tabpage -/// @param[out] err Details of an error that may have occurred -/// @return The windows in `tabpage` +/// @param tabpage Tabpage +/// @param[out] err Error details, if any +/// @return List of windows in `tabpage` ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) { Array rv = ARRAY_DICT_INIT; @@ -39,10 +39,10 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) /// Gets a tab-scoped (t:) variable /// -/// @param tabpage The tab page handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value +/// @param tabpage Tabpage handle +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Variable value Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -56,10 +56,10 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) /// Sets a tab-scoped (t:) variable /// -/// @param tabpage handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred +/// @param tabpage Tabpage handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, @@ -76,9 +76,9 @@ void nvim_tabpage_set_var(Tabpage tabpage, /// Removes a tab-scoped (t:) variable /// -/// @param tabpage handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred +/// @param tabpage Tabpage handle +/// @param name Variable name +/// @param[out] err Error details, if any void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -94,11 +94,11 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) /// /// @deprecated /// -/// @param tabpage handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value or nil if there was no previous value. +/// @param tabpage Tabpage handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. @@ -117,10 +117,10 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) /// /// @deprecated /// -/// @param tabpage handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @param tabpage Tabpage handle +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Old value Object tabpage_del_var(Tabpage tabpage, String name, Error *err) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -132,11 +132,11 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) return dict_set_value(tab->tp_vars, name, NIL, true, true, err); } -/// Gets the current window in a tab page +/// Gets the current window in a tabpage /// -/// @param tabpage The tab page handle -/// @param[out] err Details of an error that may have occurred -/// @return The Window handle +/// @param tabpage Tabpage handle +/// @param[out] err Error details, if any +/// @return Window handle Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) { Window rv = 0; @@ -159,10 +159,27 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) } } -/// Checks if a tab page is valid +/// Gets the tabpage number /// -/// @param tabpage The tab page handle -/// @return true if the tab page is valid, false otherwise +/// @param tabpage Tabpage handle +/// @param[out] err Error details, if any +/// @return Tabpage number +Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err) +{ + Integer rv = 0; + tabpage_T *tab = find_tab_by_handle(tabpage, err); + + if (!tab) { + return rv; + } + + return tabpage_index(tab); +} + +/// Checks if a tabpage is valid +/// +/// @param tabpage Tabpage handle +/// @return true if the tabpage is valid, false otherwise Boolean nvim_tabpage_is_valid(Tabpage tabpage) { Error stub = ERROR_INIT; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d8cdad961b..491375bd73 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -33,24 +33,26 @@ # include "api/vim.c.generated.h" #endif -/// Executes an ex-mode command str +/// Executes an ex-command. +/// On VimL error: Returns the VimL error; v:errmsg is not updated. /// -/// @param str The command str -/// @param[out] err Details of an error that may have occurred -void nvim_command(String str, Error *err) +/// @param command Ex-command string +/// @param[out] err Error details (including actual VimL error), if any +void nvim_command(String command, Error *err) { // Run the command try_start(); - do_cmdline_cmd(str.data); + do_cmdline_cmd(command.data); update_screen(VALID); try_end(err); } -/// Passes input keys to Neovim +/// Passes input keys to Nvim. +/// On VimL error: Does not fail, but updates v:errmsg. /// -/// @param keys to be typed -/// @param mode specifies the mapping options -/// @param escape_csi the string needs escaping for K_SPECIAL/CSI bytes +/// @param keys to be typed +/// @param mode mapping options +/// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys` /// @see feedkeys() /// @see vim_strsave_escape_csi void nvim_feedkeys(String keys, String mode, Boolean escape_csi) @@ -102,13 +104,15 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) } } -/// Passes input keys to Neovim. Unlike `nvim_feedkeys`, this will use a -/// lower-level input buffer and the call is not deferred. -/// This is the most reliable way to emulate real user input. +/// Passes keys to Nvim as raw user-input. +/// On VimL error: Does not fail, but updates v:errmsg. +/// +/// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call +/// is not deferred. This is the most reliable way to emulate real user input. /// /// @param keys to be typed -/// @return The number of bytes actually written, which can be lower than -/// requested if the buffer becomes full. +/// @return Number of bytes actually written (can be fewer than +/// requested if the buffer becomes full). Integer nvim_input(String keys) FUNC_API_ASYNC { @@ -152,19 +156,19 @@ String nvim_command_output(String str, Error *err) return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT)); } -/// Evaluates the expression str using the Vim internal expression -/// evaluator (see |expression|). -/// Dictionaries and lists are recursively expanded. +/// Evaluates a VimL expression (:help expression). +/// Dictionaries and Lists are recursively expanded. +/// On VimL error: Returns a generic error; v:errmsg is not updated. /// -/// @param str The expression str -/// @param[out] err Details of an error that may have occurred -/// @return The expanded object -Object nvim_eval(String str, Error *err) +/// @param expr VimL expression string +/// @param[out] err Error details, if any +/// @return Evaluation result or expanded object +Object nvim_eval(String expr, Error *err) { Object rv = OBJECT_INIT; // Evaluate the expression try_start(); - typval_T *expr_result = eval_expr((char_u *) str.data, NULL); + typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); if (!expr_result) { api_set_error(err, Exception, _("Failed to evaluate expression")); @@ -180,11 +184,12 @@ Object nvim_eval(String str, Error *err) return rv; } -/// Call the given function with the given arguments stored in an array. +/// Calls a VimL function with the given arguments. +/// On VimL error: Returns a generic error; v:errmsg is not updated. /// -/// @param fname Function to call -/// @param args Functions arguments packed in an Array -/// @param[out] err Details of an error that may have occurred +/// @param fname Function to call +/// @param args Function arguments packed in an Array +/// @param[out] err Error details, if any /// @return Result of the function call Object nvim_call_function(String fname, Array args, Error *err) { @@ -229,12 +234,12 @@ free_vim_args: return rv; } -/// Calculates the number of display cells `str` occupies, tab is counted as -/// one cell. +/// Calculates the number of display cells occupied by `text`. +/// <Tab> counts as one cell. /// -/// @param str Some text -/// @param[out] err Details of an error that may have occurred -/// @return The number of cells +/// @param text Some text +/// @param[out] err Error details, if any +/// @return Number of cells Integer nvim_strwidth(String str, Error *err) { if (str.size > INT_MAX) { @@ -245,9 +250,9 @@ Integer nvim_strwidth(String str, Error *err) return (Integer) mb_string2cells((char_u *) str.data); } -/// Gets a list of paths contained in 'runtimepath' +/// Gets the paths contained in 'runtimepath'. /// -/// @return The list of paths +/// @return List of paths ArrayOf(String) nvim_list_runtime_paths(void) { Array rv = ARRAY_DICT_INIT; @@ -285,10 +290,10 @@ ArrayOf(String) nvim_list_runtime_paths(void) return rv; } -/// Changes Vim working directory +/// Changes the global working directory. /// -/// @param dir The new working directory -/// @param[out] err Details of an error that may have occurred +/// @param dir Directory path +/// @param[out] err Error details, if any void nvim_set_current_dir(String dir, Error *err) { if (dir.size >= MAXPATHL) { @@ -315,8 +320,8 @@ void nvim_set_current_dir(String dir, Error *err) /// Gets the current line /// -/// @param[out] err Details of an error that may have occurred -/// @return The current line string +/// @param[out] err Error details, if any +/// @return Current line string String nvim_get_current_line(Error *err) { return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); @@ -324,8 +329,8 @@ String nvim_get_current_line(Error *err) /// Sets the current line /// -/// @param line The line contents -/// @param[out] err Details of an error that may have occurred +/// @param line Line contents +/// @param[out] err Error details, if any void nvim_set_current_line(String line, Error *err) { buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); @@ -333,36 +338,36 @@ void nvim_set_current_line(String line, Error *err) /// Deletes the current line /// -/// @param[out] err Details of an error that may have occurred +/// @param[out] err Error details, if any void nvim_del_current_line(Error *err) { buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } -/// Gets a global variable +/// Gets a global (g:) variable /// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Variable value Object nvim_get_var(String name, Error *err) { return dict_get_value(&globvardict, name, err); } -/// Sets a global variable +/// Sets a global (g:) variable /// -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any void nvim_set_var(String name, Object value, Error *err) { dict_set_value(&globvardict, name, value, false, false, err); } -/// Removes a global variable +/// Removes a global (g:) variable /// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred +/// @param name Variable name +/// @param[out] err Error details, if any void nvim_del_var(String name, Error *err) { dict_set_value(&globvardict, name, NIL, true, false, err); @@ -372,10 +377,10 @@ void nvim_del_var(String name, Error *err) /// /// @deprecated /// -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value or nil if there was no previous value. +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. @@ -388,19 +393,19 @@ Object vim_set_var(String name, Object value, Error *err) /// /// @deprecated /// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Old value Object vim_del_var(String name, Error *err) { return dict_set_value(&globvardict, name, NIL, true, true, err); } -/// Gets a vim variable +/// Gets a v: variable /// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Variable value Object nvim_get_vvar(String name, Error *err) { return dict_get_value(&vimvardict, name, err); @@ -408,9 +413,9 @@ Object nvim_get_vvar(String name, Error *err) /// Gets an option value string /// -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value Object nvim_get_option(String name, Error *err) { return get_option_from(NULL, SREQ_GLOBAL, name, err); @@ -418,9 +423,9 @@ Object nvim_get_option(String name, Error *err) /// Sets an option value /// -/// @param name The option name -/// @param value The new option value -/// @param[out] err Details of an error that may have occurred +/// @param name Option name +/// @param value New option value +/// @param[out] err Error details, if any void nvim_set_option(String name, Object value, Error *err) { set_option_to(NULL, SREQ_GLOBAL, name, value, err); @@ -428,7 +433,7 @@ void nvim_set_option(String name, Object value, Error *err) /// Writes a message to vim output buffer /// -/// @param str The message +/// @param str Message void nvim_out_write(String str) { write_msg(str, false); @@ -436,16 +441,17 @@ void nvim_out_write(String str) /// Writes a message to vim error buffer /// -/// @param str The message +/// @param str Message void nvim_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 `nvim_err_write` +/// Writes a message to vim error buffer. Appends a linefeed to ensure all +/// contents are written. /// -/// @param str The message +/// @param str Message +/// @see nvim_err_write() void nvim_err_writeln(String str) { nvim_err_write(str); @@ -454,7 +460,7 @@ void nvim_err_writeln(String str) /// Gets the current list of buffer handles /// -/// @return The number of buffers +/// @return List of buffer handles ArrayOf(Buffer) nvim_list_bufs(void) { Array rv = ARRAY_DICT_INIT; @@ -475,7 +481,7 @@ ArrayOf(Buffer) nvim_list_bufs(void) /// Gets the current buffer /// -/// @reqturn The buffer handle +/// @return Buffer handle Buffer nvim_get_current_buf(void) { return curbuf->handle; @@ -483,8 +489,8 @@ Buffer nvim_get_current_buf(void) /// Sets the current buffer /// -/// @param id The buffer handle -/// @param[out] err Details of an error that may have occurred +/// @param id Buffer handle +/// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -505,7 +511,7 @@ void nvim_set_current_buf(Buffer buffer, Error *err) /// Gets the current list of window handles /// -/// @return The number of windows +/// @return List of window handles ArrayOf(Window) nvim_list_wins(void) { Array rv = ARRAY_DICT_INIT; @@ -526,7 +532,7 @@ ArrayOf(Window) nvim_list_wins(void) /// Gets the current window /// -/// @return The window handle +/// @return Window handle Window nvim_get_current_win(void) { return curwin->handle; @@ -534,7 +540,7 @@ Window nvim_get_current_win(void) /// Sets the current window /// -/// @param handle The window handle +/// @param handle Window handle void nvim_set_current_win(Window window, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -555,7 +561,7 @@ void nvim_set_current_win(Window window, Error *err) /// Gets the current list of tabpage handles /// -/// @return The number of tab pages +/// @return List of tabpage handles ArrayOf(Tabpage) nvim_list_tabpages(void) { Array rv = ARRAY_DICT_INIT; @@ -574,18 +580,18 @@ ArrayOf(Tabpage) nvim_list_tabpages(void) return rv; } -/// Gets the current tab page +/// Gets the current tabpage /// -/// @return The tab page handle +/// @return Tabpage handle Tabpage nvim_get_current_tabpage(void) { return curtab->handle; } -/// Sets the current tab page +/// Sets the current tabpage /// -/// @param handle The tab page handle -/// @param[out] err Details of an error that may have occurred +/// @param handle Tabpage handle +/// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) { tabpage_T *tp = find_tab_by_handle(tabpage, err); @@ -606,8 +612,8 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) /// Subscribes to event broadcasts /// -/// @param channel_id The channel id (passed automatically by the dispatcher) -/// @param event The event type string +/// @param channel_id Channel id (passed automatically by the dispatcher) +/// @param event Event type string void nvim_subscribe(uint64_t channel_id, String event) FUNC_API_NOEVAL { @@ -620,8 +626,8 @@ void nvim_subscribe(uint64_t channel_id, String event) /// Unsubscribes to event broadcasts /// -/// @param channel_id The channel id (passed automatically by the dispatcher) -/// @param event The event type string +/// @param channel_id Channel id (passed automatically by the dispatcher) +/// @param event Event type string void nvim_unsubscribe(uint64_t channel_id, String event) FUNC_API_NOEVAL { @@ -663,13 +669,101 @@ Array nvim_get_api_info(uint64_t channel_id) return rv; } +/// Call many api methods atomically +/// +/// This has two main usages: Firstly, to perform several requests from an +/// async context atomically, i.e. without processing requests from other rpc +/// clients or redrawing or allowing user interaction in between. Note that api +/// methods that could fire autocommands or do event processing still might do +/// so. For instance invoking the :sleep command might call timer callbacks. +/// Secondly, it can be used to reduce rpc overhead (roundtrips) when doing +/// many requests in sequence. +/// +/// @param calls an array of calls, where each call is described by an array +/// with two elements: the request name, and an array of arguments. +/// @param[out] err Details of a validation error of the nvim_multi_request call +/// itself, i e malformatted `calls` parameter. Errors from called methods will +/// be indicated in the return value, see below. +/// +/// @return an array with two elements. The first is an array of return +/// values. The second is NIL if all calls succeeded. If a call resulted in +/// an error, it is a three-element array with the zero-based index of the call +/// which resulted in an error, the error type and the error message. If an +/// error ocurred, the values from all preceding calls will still be returned. +Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err) + FUNC_API_NOEVAL +{ + Array rv = ARRAY_DICT_INIT; + Array results = ARRAY_DICT_INIT; + Error nested_error = ERROR_INIT; + + size_t i; // also used for freeing the variables + for (i = 0; i < calls.size; i++) { + if (calls.items[i].type != kObjectTypeArray) { + api_set_error(err, + Validation, + _("All items in calls array must be arrays")); + goto validation_error; + } + Array call = calls.items[i].data.array; + if (call.size != 2) { + api_set_error(err, + Validation, + _("All items in calls array must be arrays of size 2")); + goto validation_error; + } + + if (call.items[0].type != kObjectTypeString) { + api_set_error(err, + Validation, + _("name must be String")); + goto validation_error; + } + String name = call.items[0].data.string; + + if (call.items[1].type != kObjectTypeArray) { + api_set_error(err, + Validation, + _("args must be Array")); + goto validation_error; + } + Array args = call.items[1].data.array; + + MsgpackRpcRequestHandler handler = msgpack_rpc_get_handler_for(name.data, + name.size); + Object result = handler.fn(channel_id, args, &nested_error); + if (nested_error.set) { + // error handled after loop + break; + } + + ADD(results, result); + } + + ADD(rv, ARRAY_OBJ(results)); + if (nested_error.set) { + Array errval = ARRAY_DICT_INIT; + ADD(errval, INTEGER_OBJ((Integer)i)); + ADD(errval, INTEGER_OBJ(nested_error.type)); + ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg))); + ADD(rv, ARRAY_OBJ(errval)); + } else { + ADD(rv, NIL); + } + return rv; + +validation_error: + api_free_array(results); + 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. /// -/// @param message The message to write -/// @param to_err true if it should be treated as an error message (use -/// `emsg` instead of `msg` to print each line) +/// @param message Message to write +/// @param to_err true: message is an error (uses `emsg` instead of `msg`) static void write_msg(String message, bool to_err) { static size_t out_pos = 0, err_pos = 0; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 166e43f698..ef881fa0eb 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -15,9 +15,9 @@ /// Gets the current buffer in a window /// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return The buffer handle +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return Buffer handle Buffer nvim_win_get_buf(Window window, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -31,9 +31,9 @@ Buffer nvim_win_get_buf(Window window, Error *err) /// Gets the cursor position in the window /// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the (row, col) tuple +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return (row, col) tuple ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) { Array rv = ARRAY_DICT_INIT; @@ -49,9 +49,9 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) /// Sets the cursor position in the window /// -/// @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 +/// @param window Window handle +/// @param pos (row, col) tuple representing the new position +/// @param[out] err Error details, if any void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -95,9 +95,9 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) /// Gets the window height /// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the height in rows +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return Height as a count of rows Integer nvim_win_get_height(Window window, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -112,9 +112,9 @@ Integer nvim_win_get_height(Window window, Error *err) /// Sets the window height. This will only succeed if the screen is split /// horizontally. /// -/// @param window The window handle -/// @param height the new height in rows -/// @param[out] err Details of an error that may have occurred +/// @param window Window handle +/// @param height Height as a count of rows +/// @param[out] err Error details, if any void nvim_win_set_height(Window window, Integer height, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -138,9 +138,9 @@ void nvim_win_set_height(Window window, Integer height, Error *err) /// Gets the window width /// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the width in columns +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return Width as a count of columns Integer nvim_win_get_width(Window window, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -155,9 +155,9 @@ Integer nvim_win_get_width(Window window, Error *err) /// Sets the window width. This will only succeed if the screen is split /// vertically. /// -/// @param window The window handle -/// @param width the new width in columns -/// @param[out] err Details of an error that may have occurred +/// @param window Window handle +/// @param width Width as a count of columns +/// @param[out] err Error details, if any void nvim_win_set_width(Window window, Integer width, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -181,10 +181,10 @@ void nvim_win_set_width(Window window, Integer width, Error *err) /// Gets a window-scoped (w:) variable /// -/// @param window The window handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value +/// @param window Window handle +/// @param name Variable name +/// @param[out] err Error details, if any +/// @return Variable value Object nvim_win_get_var(Window window, String name, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -198,10 +198,10 @@ Object nvim_win_get_var(Window window, String name, Error *err) /// Sets a window-scoped (w:) variable /// -/// @param window The window handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred +/// @param window Window handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any void nvim_win_set_var(Window window, String name, Object value, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -215,9 +215,9 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err) /// Removes a window-scoped (w:) variable /// -/// @param window The window handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred +/// @param window Window handle +/// @param name Variable name +/// @param[out] err Error details, if any void nvim_win_del_var(Window window, String name, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -233,11 +233,11 @@ void nvim_win_del_var(Window window, String name, Error *err) /// /// @deprecated /// -/// @param window The window handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value or nil if there was no previous value. +/// @param window Window handle +/// @param name Variable name +/// @param value Variable value +/// @param[out] err Error details, if any +/// @return Old value or nil if there was no previous value. /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. @@ -256,10 +256,10 @@ Object window_set_var(Window window, String name, Object value, Error *err) /// /// @deprecated /// -/// @param window The window handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The old value +/// @param window Window handle +/// @param name variable name +/// @param[out] err Error details, if any +/// @return Old value Object window_del_var(Window window, String name, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -273,10 +273,10 @@ Object window_del_var(Window window, String name, Error *err) /// Gets a window option value /// -/// @param window The window handle -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value +/// @param window Window handle +/// @param name Option name +/// @param[out] err Error details, if any +/// @return Option value Object nvim_win_get_option(Window window, String name, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -291,10 +291,10 @@ Object nvim_win_get_option(Window window, String name, Error *err) /// Sets a window option value. Passing 'nil' as value deletes the option(only /// works if there's a global fallback) /// -/// @param window The window handle -/// @param name The option name -/// @param value The option value -/// @param[out] err Details of an error that may have occurred +/// @param window Window handle +/// @param name Option name +/// @param value Option value +/// @param[out] err Error details, if any void nvim_win_set_option(Window window, String name, Object value, Error *err) { win_T *win = find_window_by_handle(window, err); @@ -308,9 +308,9 @@ void nvim_win_set_option(Window window, String name, Object value, Error *err) /// Gets the window position in display cells. First position is zero. /// -/// @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 +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return (row, col) tuple with the window position ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) { Array rv = ARRAY_DICT_INIT; @@ -324,11 +324,11 @@ ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) return rv; } -/// Gets the window tab page +/// Gets the window tabpage /// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return The tab page that contains the window +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return Tabpage that contains the window Tabpage nvim_win_get_tabpage(Window window, Error *err) { Tabpage rv = 0; @@ -341,9 +341,29 @@ Tabpage nvim_win_get_tabpage(Window window, Error *err) return rv; } +/// Gets the window number +/// +/// @param window Window handle +/// @param[out] err Error details, if any +/// @return Window number +Integer nvim_win_get_number(Window window, Error *err) +{ + Integer rv = 0; + win_T *win = find_window_by_handle(window, err); + + if (!win) { + return rv; + } + + int tabnr; + win_get_tabwin(window, &tabnr, (int *)&rv); + + return rv; +} + /// Checks if a window is valid /// -/// @param window The window handle +/// @param window Window handle /// @return true if the window is valid, false otherwise Boolean nvim_win_is_valid(Window window) { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 51c9fb1556..24744f1437 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -462,8 +462,7 @@ static void insert_enter(InsertState *s) o_lnum = curwin->w_cursor.lnum; } - foldUpdateAll(curwin); - foldOpenCursor(); + foldUpdateAfterInsert(); if (s->cmdchar != 'r' && s->cmdchar != 'v') { apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, false, curbuf); } @@ -665,9 +664,10 @@ static int insert_execute(VimState *state, int key) // Pressing CTRL-Y selects the current match. When // compl_enter_selects is set the Enter key does the same. - if (s->c == Ctrl_Y - || (compl_enter_selects - && (s->c == CAR || s->c == K_KENTER || s->c == NL))) { + if ((s->c == Ctrl_Y + || (compl_enter_selects + && (s->c == CAR || s->c == K_KENTER || s->c == NL))) + && stop_arrow() == OK) { ins_compl_delete(); ins_compl_insert(); } @@ -2351,9 +2351,6 @@ void set_completion(colnr_T startcol, list_T *list) } ins_compl_clear(); - if (stop_arrow() == FAIL) - return; - compl_direction = FORWARD; if (startcol > curwin->w_cursor.col) startcol = curwin->w_cursor.col; @@ -3264,14 +3261,19 @@ static bool ins_compl_prep(int c) } else { int prev_col = curwin->w_cursor.col; - /* put the cursor on the last char, for 'tw' formatting */ - if (prev_col > 0) + // put the cursor on the last char, for 'tw' formatting + if (prev_col > 0) { dec_cursor(); - if (stop_arrow() == OK) + } + + if (!arrow_used && !ins_need_undo) { insertchar(NUL, 0, -1); + } + if (prev_col > 0 - && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) + && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) { inc_cursor(); + } } // If the popup menu is displayed pressing CTRL-Y means accepting @@ -3948,16 +3950,20 @@ static int ins_compl_get_exp(pos_T *ini) /* Delete the old text being completed. */ static void ins_compl_delete(void) { - int i; + int col; - /* - * In insert mode: Delete the typed part. - * In replace mode: Put the old characters back, if any. - */ - 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. + // In insert mode: Delete the typed part. + // In replace mode: Put the old characters back, if any. + col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); + if ((int)curwin->w_cursor.col > col) { + if (stop_arrow() == FAIL) { + return; + } + backspace_until_column(col); + } + + // TODO(vim): is this sufficient for redrawing? Redrawing everything + // causes flicker, thus we can't do that. changed_cline_bef_curs(); // clear v:completed_item set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); @@ -4319,8 +4325,11 @@ static int ins_complete(int c, bool enable_pum) colnr_T curs_col; /* cursor column */ int n; int save_w_wrow; + int insert_match; compl_direction = ins_compl_key2dir(c); + insert_match = ins_compl_use_match(c); + if (!compl_started) { /* First time we hit ^N or ^P (in a row, I mean) */ @@ -4653,6 +4662,8 @@ static int ins_complete(int c, bool enable_pum) showmode(); edit_submode_extra = NULL; ui_flush(); + } else if (insert_match && stop_arrow() == FAIL) { + return FAIL; } compl_shown_match = compl_curr_match; @@ -4662,7 +4673,7 @@ static int ins_complete(int c, bool enable_pum) * Find next match (and following matches). */ save_w_wrow = curwin->w_wrow; - n = ins_compl_next(TRUE, ins_compl_key2count(c), ins_compl_use_match(c)); + n = ins_compl_next(true, ins_compl_key2count(c), insert_match); /* may undisplay the popup menu */ ins_compl_upd_pum(); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cae032f437..76f33e7d8c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7133,7 +7133,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) } Error err = ERROR_INIT; - Object result = fn(INVALID_CHANNEL, NO_RESPONSE, args, &err); + Object result = fn(INTERNAL_CALL, args, &err); if (err.set) { nvim_err_writeln(cstr_as_string(err.msg)); @@ -9527,24 +9527,35 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; int error = FALSE; - /* Position the cursor. Needed after a message that ends in a space. */ - ui_cursor_goto(msg_row, msg_col); - ++no_mapping; ++allow_keys; for (;; ) { - if (argvars[0].v_type == VAR_UNKNOWN) - /* getchar(): blocking wait. */ + // Position the cursor. Needed after a message that ends in a space, + // or if event processing caused a redraw. + ui_cursor_goto(msg_row, msg_col); + + if (argvars[0].v_type == VAR_UNKNOWN) { + // getchar(): blocking wait. + if (!(char_avail() || using_script() || input_available())) { + input_enable_events(); + (void)os_inchar(NULL, 0, -1, 0); + input_disable_events(); + if (!multiqueue_empty(main_loop.events)) { + multiqueue_process_events(main_loop.events); + continue; + } + } n = safe_vgetc(); - else if (get_tv_number_chk(&argvars[0], &error) == 1) - /* getchar(1): only check if char avail */ + } else if (get_tv_number_chk(&argvars[0], &error) == 1) { + // getchar(1): only check if char avail n = vpeekc_any(); - else if (error || vpeekc_any() == NUL) - /* illegal argument or getchar(0) and no char avail: return zero */ + } else if (error || vpeekc_any() == NUL) { + // illegal argument or getchar(0) and no char avail: return zero n = 0; - else - /* getchar(0) and char avail: return char */ + } else { + // getchar(0) and char avail: return char n = safe_vgetc(); + } if (n == K_IGNORE) continue; diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 5126dfd84e..92efc9fa2e 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -112,8 +112,8 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` // won't be called) && cnt != 0) { - DLOG("Closing Stream(%p) because of %s(%zd)", stream, - uv_strerror((int)cnt), cnt); + DLOG("Closing Stream (%p): %s (%s)", stream, + uv_err_name((int)cnt), os_strerror((int)cnt)); // Read error or EOF, either way stop the stream and invoke the callback // with eof == true uv_read_stop(uvstream); diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 30871c7711..f61e01d25b 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -23,19 +23,19 @@ struct eslist_elem { #define CSTACK_LEN 50 struct condstack { - short cs_flags[CSTACK_LEN]; /* CSF_ flags */ - char cs_pending[CSTACK_LEN]; /* CSTP_: what's pending in ":finally"*/ + int cs_flags[CSTACK_LEN]; // CSF_ flags + char cs_pending[CSTACK_LEN]; // CSTP_: what's pending in ":finally" union { - void *csp_rv[CSTACK_LEN]; /* return typeval for pending return */ - void *csp_ex[CSTACK_LEN]; /* exception for pending throw */ + void *csp_rv[CSTACK_LEN]; // return typeval for pending return + void *csp_ex[CSTACK_LEN]; // exception for pending throw } cs_pend; - void *cs_forinfo[CSTACK_LEN]; /* info used by ":for" */ - int cs_line[CSTACK_LEN]; /* line nr of ":while"/":for" line */ - int cs_idx; /* current entry, or -1 if none */ - int cs_looplevel; /* nr of nested ":while"s and ":for"s */ - int cs_trylevel; /* nr of nested ":try"s */ - eslist_T *cs_emsg_silent_list; /* saved values of "emsg_silent" */ - char cs_lflags; /* loop flags: CSL_ flags */ + void *cs_forinfo[CSTACK_LEN]; // info used by ":for" + int cs_line[CSTACK_LEN]; // line nr of ":while"/":for" line + int cs_idx; // current entry, or -1 if none + int cs_looplevel; // nr of nested ":while"s and ":for"s + int cs_trylevel; // nr of nested ":try"s + eslist_T *cs_emsg_silent_list; // saved values of "emsg_silent" + int cs_lflags; // loop flags: CSL_ flags }; # define cs_rettv cs_pend.csp_rv # define cs_exception cs_pend.csp_ex diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4254697241..7444eb8a38 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4396,6 +4396,7 @@ static HistoryType hist_char2type(const int c) case '>': { return HIST_DEBUG; } + case NUL: case '/': case '?': { return HIST_SEARCH; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 70030b8525..c84d738cb4 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -788,6 +788,19 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) } } +/// Updates folds when leaving insert-mode. +void foldUpdateAfterInsert(void) +{ + if (foldmethodIsManual(curwin) // foldmethod=manual: No need to update. + // These foldmethods are too slow, do not auto-update on insert-leave. + || foldmethodIsSyntax(curwin) || foldmethodIsExpr(curwin)) { + return; + } + + foldUpdateAll(curwin); + foldOpenCursor(); +} + /* foldUpdateAll() {{{2 */ /* * Update all lines in a window for folding. diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 3ed85677fc..0b20647771 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1761,8 +1761,8 @@ static void cs_print_tags_priv(char **matches, char **cntxts, */ static int cs_read_prompt(size_t i) { - char ch; - char *buf = NULL; /* buffer for possible error message from cscope */ + int ch; + char *buf = NULL; // buffer for possible error message from cscope size_t bufpos = 0; char *cs_emsg = _("E609: Cscope error: %s"); size_t cs_emsg_len = strlen(cs_emsg); @@ -1774,35 +1774,34 @@ static int cs_read_prompt(size_t i) size_t maxlen = IOSIZE - cs_emsg_len; for (;; ) { - while ((ch = (char)getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0]) - /* if there is room and char is printable */ + while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0]) { + // if there is room and char is printable if (bufpos < maxlen - 1 && vim_isprintc(ch)) { // lazy buffer allocation if (buf == NULL) { buf = xmalloc(maxlen); } - { - /* append character to the message */ - buf[bufpos++] = ch; - buf[bufpos] = NUL; - if (bufpos >= epromptlen - && strcmp(&buf[bufpos - epromptlen], eprompt) == 0) { - /* remove eprompt from buf */ - buf[bufpos - epromptlen] = NUL; - - /* print message to user */ - (void)EMSG2(cs_emsg, buf); + // append character to the message + buf[bufpos++] = (char)ch; + buf[bufpos] = NUL; + if (bufpos >= epromptlen + && strcmp(&buf[bufpos - epromptlen], eprompt) == 0) { + // remove eprompt from buf + buf[bufpos - epromptlen] = NUL; + + // print message to user + (void)EMSG2(cs_emsg, buf); - /* send RETURN to cscope */ - (void)putc('\n', csinfo[i].to_fp); - (void)fflush(csinfo[i].to_fp); + // send RETURN to cscope + (void)putc('\n', csinfo[i].to_fp); + (void)fflush(csinfo[i].to_fp); - /* clear buf */ - bufpos = 0; - buf[bufpos] = NUL; - } + // clear buf + bufpos = 0; + buf[bufpos] = NUL; } } + } for (size_t n = 0; n < strlen(CSCOPE_PROMPT); ++n) { if (n > 0) diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index fef1d08db7..98636263b9 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -452,7 +452,7 @@ static void on_request_event(void **argv) Array args = e->args; uint64_t request_id = e->request_id; Error error = ERROR_INIT; - Object result = handler.fn(channel->id, request_id, args, &error); + Object result = handler.fn(channel->id, args, &error); if (request_id != NO_RESPONSE) { // send the response msgpack_packer response; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 33e9bb1c07..14e1c2d978 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -455,7 +455,6 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) /// Handler executed when an invalid method name is passed Object msgpack_rpc_handle_missing_method(uint64_t channel_id, - uint64_t request_id, Array args, Error *error) { @@ -466,7 +465,6 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, /// Handler executed when malformated arguments are passed Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, - uint64_t request_id, Array args, Error *error) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6dcbf50750..76e3829bee 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5925,8 +5925,7 @@ static void nv_replace(cmdarg_T *cap) set_last_insert(cap->nchar); } - foldUpdateAll(curwin); - foldOpenCursor(); + foldUpdateAfterInsert(); } /* diff --git a/src/nvim/option.c b/src/nvim/option.c index e2a5d38bee..81919c00d2 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1032,6 +1032,15 @@ void set_init_3(void) xfree(p); } + if (bufempty()) { + int idx_ffs = findoption((char_u *)"ffs"); + + // Apply the first entry of 'fileformats' to the initial buffer. + if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { + set_fileformat(default_fileformat(), OPT_LOCAL); + } + } + set_title_defaults(); } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 008952fa97..3c821936e9 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -163,8 +163,12 @@ int os_nodetype(const char *name) // saves us the hassle. int nodetype = NODE_WRITABLE; - int fd = os_open(name, O_RDONLY, 0); - switch(uv_guess_handle(fd)) { + int fd = os_open(name, O_RDONLY +#ifdef O_NONBLOCK + | O_NONBLOCK +#endif + , 0); + switch (uv_guess_handle(fd)) { case UV_TTY: // FILE_TYPE_CHAR nodetype = NODE_WRITABLE; break; diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index e9a3dcbff8..18ee008d66 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -545,6 +545,16 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, static void shell_write_cb(Stream *stream, void *data, int status) { + if (status) { + // Can happen if system() tries to send input to a shell command that was + // backgrounded (:call system("cat - &", "foo")). #3529 #5241 + EMSG2(_("E5677: Error writing input to shell-command: %s"), + uv_err_name(status)); + } + if (stream->closed) { // Process may have exited before this write. + ELOG("stream was already closed"); + return; + } stream_close(stream, NULL, NULL); } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 4d21887240..67e7c97905 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -34,6 +34,7 @@ NEW_TESTS = \ test_cscope.res \ test_hardcopy.res \ test_help_tagjump.res \ + test_history.res \ test_langmap.res \ test_syntax.res \ test_usercommands.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 7169b6076f..036a4c0470 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -10,5 +10,6 @@ source test_menu.vim source test_popup.vim source test_regexp_utf8.vim source test_syn_attr.vim +source test_tabpage.vim source test_unlet.vim source test_matchadd_conceal_utf8.vim diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim new file mode 100644 index 0000000000..ee6acfffc3 --- /dev/null +++ b/src/nvim/testdir/test_history.vim @@ -0,0 +1,65 @@ +" Tests for the history functions + +if !has('cmdline_hist') + finish +endif + +set history=7 + +function History_Tests(hist) + " First clear the history + call histadd(a:hist, 'dummy') + call assert_true(histdel(a:hist)) + call assert_equal(-1, histnr(a:hist)) + call assert_equal('', histget(a:hist)) + + call assert_true(histadd(a:hist, 'ls')) + call assert_true(histadd(a:hist, 'buffers')) + call assert_equal('buffers', histget(a:hist)) + call assert_equal('ls', histget(a:hist, -2)) + call assert_equal('ls', histget(a:hist, 1)) + call assert_equal('', histget(a:hist, 5)) + call assert_equal('', histget(a:hist, -5)) + call assert_equal(2, histnr(a:hist)) + call assert_true(histdel(a:hist, 2)) + call assert_false(histdel(a:hist, 7)) + call assert_equal(1, histnr(a:hist)) + call assert_equal('ls', histget(a:hist, -1)) + + call assert_true(histadd(a:hist, 'buffers')) + call assert_true(histadd(a:hist, 'ls')) + call assert_equal('ls', histget(a:hist, -1)) + call assert_equal(4, histnr(a:hist)) + + " Test for removing entries matching a pattern + for i in range(1, 3) + call histadd(a:hist, 'text_' . i) + endfor + call assert_true(histdel(a:hist, 'text_\d\+')) + call assert_equal('ls', histget(a:hist, -1)) + + " Test for freeing the entire history list + for i in range(1, 7) + call histadd(a:hist, 'text_' . i) + endfor + call histdel(a:hist) + for i in range(1, 7) + call assert_equal('', histget(a:hist, i)) + call assert_equal('', histget(a:hist, i - 7 - 1)) + endfor +endfunction + +function Test_History() + for h in ['cmd', ':', '', 'search', '/', '?', 'expr', '=', 'input', '@', 'debug', '>'] + call History_Tests(h) + endfor + + " Negative tests + call assert_false(histdel('abc')) + call assert_equal('', histget('abc')) + call assert_fails('call histdel([])', 'E730:') + call assert_equal('', histget(10)) + call assert_fails('call histget([])', 'E730:') + call assert_equal(-1, histnr('abc')) + call assert_fails('call histnr([])', 'E730:') +endfunction diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim new file mode 100644 index 0000000000..e6b85d6e14 --- /dev/null +++ b/src/nvim/testdir/test_tabpage.vim @@ -0,0 +1,189 @@ +" Tests for tabpage + +function Test_tabpage() + bw! + " Simple test for opening and closing a tab page + tabnew + call assert_equal(2, tabpagenr()) + quit + + " Open three tab pages and use ":tabdo" + 0tabnew + 1tabnew + $tabnew + tabdo call append(line('$'), tabpagenr()) + tabclose! 2 + tabrewind + let line1 = getline('$') + undo + q + tablast + let line2 = getline('$') + q! + call append(line('$'), line1) + call append(line('$'), line2) + unlet line1 line2 + call assert_equal(['', '3', '1', '4'], getline(1, '$')) + " + " Test for settabvar() and gettabvar() functions. Open a new tab page and + " set 3 variables to a number, string and a list. Verify that the variables + " are correctly set. + tabnew + tabfirst + call settabvar(2, 'val_num', 100) + call settabvar(2, 'val_str', 'SetTabVar test') + call settabvar(2, 'val_list', ['red', 'blue', 'green']) + " + call assert_true(gettabvar(2, 'val_num') == 100 && gettabvar(2, 'val_str') == 'SetTabVar test' && gettabvar(2, 'val_list') == ['red', 'blue', 'green']) + + tabnext 2 + call assert_true(t:val_num == 100 && t:val_str == 'SetTabVar test' && t:val_list == ['red', 'blue', 'green']) + tabclose + + if has('gui') || has('clientserver') + " Test for ":tab drop exist-file" to keep current window. + sp test1 + tab drop test1 + call assert_true(tabpagenr('$') == 1 && winnr('$') == 2 && winnr() == 1) + close + " + " + " Test for ":tab drop new-file" to keep current window of tabpage 1. + split + tab drop newfile + call assert_true(tabpagenr('$') == 2 && tabpagewinnr(1, '$') == 2 && tabpagewinnr(1) == 1) + tabclose + q + " + " + " Test for ":tab drop multi-opend-file" to keep current tabpage and window. + new test1 + tabnew + new test1 + tab drop test1 + call assert_true(tabpagenr() == 2 && tabpagewinnr(2, '$') == 2 && tabpagewinnr(2) == 1) + tabclose + q + endif + " + " + for i in range(9) | tabnew | endfor + normal! 1gt + call assert_equal(1, tabpagenr()) + tabmove 5 + call assert_equal(5, tabpagenr()) + .tabmove + call assert_equal(5, tabpagenr()) + tabmove - + call assert_equal(4, tabpagenr()) + tabmove + + call assert_equal(5, tabpagenr()) + tabmove -2 + call assert_equal(3, tabpagenr()) + tabmove +4 + call assert_equal(7, tabpagenr()) + tabmove + call assert_equal(10, tabpagenr()) + tabmove -20 + call assert_equal(1, tabpagenr()) + tabmove +20 + call assert_equal(10, tabpagenr()) + 0tabmove + call assert_equal(1, tabpagenr()) + $tabmove + call assert_equal(10, tabpagenr()) + tabmove 0 + call assert_equal(1, tabpagenr()) + tabmove $ + call assert_equal(10, tabpagenr()) + 3tabmove + call assert_equal(4, tabpagenr()) + 7tabmove 5 + call assert_equal(5, tabpagenr()) + call assert_fails("tabmove foo", 'E474:') +endfunc + +" Test autocommands +function Test_tabpage_with_autocmd() + if !has('autocmd') + return + endif + tabonly! + command -nargs=1 -bar C :call add(s:li, '=== ' . <q-args> . ' ===')|<args> + augroup TestTabpageGroup + au! + autocmd TabEnter * call add(s:li, 'TabEnter') + autocmd WinEnter * call add(s:li, 'WinEnter') + autocmd BufEnter * call add(s:li, 'BufEnter') + autocmd TabLeave * call add(s:li, 'TabLeave') + autocmd WinLeave * call add(s:li, 'WinLeave') + autocmd BufLeave * call add(s:li, 'BufLeave') + augroup END + + let s:li = [] + let t:a='a' + C tab split + call assert_equal(['=== tab split ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter'], s:li) + let s:li = [] + let t:a='b' + C tabnew + call assert_equal(['=== tabnew ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufLeave', 'BufEnter'], s:li) + let t:a='c' + let s:li = split(join(map(range(1, tabpagenr('$')), 'gettabvar(v:val, "a")')) , '\s\+') + call assert_equal(['a', 'b', 'c'], s:li) + + let s:li = [] + C call map(range(1, tabpagenr('$')), 'settabvar(v:val, ''a'', v:val*2)') + call assert_equal(["=== call map(range(1, tabpagenr('$')), 'settabvar(v:val, ''a'', v:val*2)') ==="], s:li) + let s:li = split(join(map(range(1, tabpagenr('$')), 'gettabvar(v:val, "a")')) , '\s\+') + call assert_equal(['2', '4', '6'], s:li) + + let s:li = [] + let w:a='a' + C vsplit + call assert_equal(['=== vsplit ===', 'WinLeave', 'WinEnter'], s:li) + let s:li = [] + let w:a='a' + let tabn=tabpagenr() + let winr=range(1, winnr('$')) + C tabnext 1 + call assert_equal(['=== tabnext 1 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li) + let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') + call assert_equal(['a', 'a'], s:li) + let s:li = [] + C call map(copy(winr), 'settabwinvar('.tabn.', v:val, ''a'', v:val*2)') + let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') + call assert_equal(['2', '4'], s:li) + + augroup TabDestructive + autocmd TabEnter * :C tabnext 2 | C tabclose 3 + augroup END + let s:li = [] + C tabnext 3 + call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ==='], s:li) + call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')]) + + autocmd! TabDestructive TabEnter + let s:li = [] + C tabnew + call assert_equal(['=== tabnew ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufLeave', 'BufEnter'], s:li) + let s:li = [] + C tabnext 1 + call assert_equal(['=== tabnext 1 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li) + + autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3 + let s:li = [] + C tabnext 3 + call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ===', 'BufEnter', '=== tabclose 3 ==='], s:li) + call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')]) + + delcommand C + autocmd! TabDestructive + augroup! TabDestructive + autocmd! TestTabpageGroup + augroup! TestTabpageGroup + tabonly! + bw! +endfunction + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/version.c b/src/nvim/version.c index e6ef600f8c..2dfe5d3248 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -214,7 +214,7 @@ static int included_patches[] = { // 2230, // 2229, // 2228, - // 2227, + 2227, // 2226, // 2225, // 2224, @@ -608,7 +608,7 @@ static int included_patches[] = { // 1836, // 1835, // 1834, - // 1833, + 1833, 1832, 1831, // 1830 NA @@ -739,7 +739,7 @@ static int included_patches[] = { // 1707, // 1706 NA // 1705 NA - // 1704, + 1704, 1703, // 1702, // 1701, @@ -824,7 +824,7 @@ static int included_patches[] = { // 1622 NA // 1621 NA 1620, - // 1619, + 1619, // 1618 NA // 1617 NA // 1616 NA @@ -855,7 +855,7 @@ static int included_patches[] = { // 1591, // 1590, // 1589, - // 1588, + 1588, // 1587 NA // 1586, // 1585, @@ -878,7 +878,7 @@ static int included_patches[] = { 1568, 1567, // 1566 NA - // 1565, + 1565, // 1564, // 1563, // 1562 NA @@ -949,7 +949,7 @@ static int included_patches[] = { // 1497 NA // 1496 NA // 1495 NA - // 1494, + 1494, // 1493 NA 1492, 1491, @@ -2652,14 +2652,13 @@ void intro_message(int colon) N_(NVIM_VERSION_LONG), "", N_("by Bram Moolenaar et al."), - N_("Vim is open source and freely distributable"), - "", - N_("Type \":Tutor\" or \":help nvim\" to get started!"), - "", - N_("Still have questions? https://neovim.io/community"), + N_("Nvim is open source and freely distributable"), + N_("https://neovim.io/community"), "", + N_("type :help nvim<Enter> if you are new! "), + N_("type :CheckHealth<Enter> to optimize Nvim"), N_("type :q<Enter> to exit "), - N_("type :help<Enter> or <F1> for on-line help"), + N_("type :help<Enter> for help "), "", N_("Help poor children in Uganda!"), N_("type :help iccf<Enter> for information "), diff --git a/src/nvim/window.c b/src/nvim/window.c index a259654d52..03a2e9a842 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -278,10 +278,11 @@ newwindow: /* cursor to last accessed (previous) window */ case 'p': case Ctrl_P: - if (prevwin == NULL) + if (!win_valid(prevwin)) { beep_flush(); - else + } else { win_goto(prevwin); + } break; /* exchange current and next window */ @@ -3768,8 +3769,15 @@ win_free ( hash_init(&wp->w_vars->dv_hashtab); unref_var_dict(wp->w_vars); - if (prevwin == wp) + if (prevwin == wp) { prevwin = NULL; + } + FOR_ALL_TABS(ttp) { + if (ttp->tp_prevwin == wp) { + ttp->tp_prevwin = NULL; + } + } + win_free_lsize(wp); for (i = 0; i < wp->w_tagstacklen; ++i) @@ -5716,45 +5724,46 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - win_T *wp; - tabpage_T *tp; int id = get_tv_number(&argvars[0]); - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - for (wp = tp == curtab ? firstwin : tp->tp_firstwin; - wp != NULL; wp = wp->w_next) { - if (wp->handle == id) { - goto_tabpage_win(tp, wp); - return 1; - } + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->handle == id) { + goto_tabpage_win(tp, wp); + return 1; } } return 0; } -void win_id2tabwin(typval_T *argvars, list_T *list) +void win_get_tabwin(handle_T id, int *tabnr, int *winnr) { - win_T *wp; - tabpage_T *tp; - int winnr = 1; - int tabnr = 1; - int id = get_tv_number(&argvars[0]); + *tabnr = 0; + *winnr = 0; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - for (wp = tp == curtab ? firstwin : tp->tp_firstwin; - wp != NULL; wp = wp->w_next) { + int tnum = 1, wnum = 1; + FOR_ALL_TABS(tp) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->handle == id) { - list_append_number(list, tabnr); - list_append_number(list, winnr); + *winnr = wnum; + *tabnr = tnum; return; } - winnr++; + wnum++; } - tabnr++; - winnr = 1; + tnum++; + wnum = 1; } - list_append_number(list, 0); - list_append_number(list, 0); +} + +void win_id2tabwin(typval_T *argvars, list_T *list) +{ + int winnr = 1; + int tabnr = 1; + int id = get_tv_number(&argvars[0]); + + win_get_tabwin(id, &tabnr, &winnr); + list_append_number(list, tabnr); + list_append_number(list, winnr); } int win_id2win(typval_T *argvars) |