aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt10
-rw-r--r--src/nvim/api/buffer.c210
-rw-r--r--src/nvim/api/private/defs.h4
-rw-r--r--src/nvim/api/private/dispatch.c45
-rw-r--r--src/nvim/api/private/dispatch.h2
-rw-r--r--src/nvim/api/private/helpers.c21
-rw-r--r--src/nvim/api/tabpage.c77
-rw-r--r--src/nvim/api/vim.c282
-rw-r--r--src/nvim/api/window.c134
-rw-r--r--src/nvim/edit.c55
-rw-r--r--src/nvim/eval.c35
-rw-r--r--src/nvim/event/rstream.c4
-rw-r--r--src/nvim/ex_eval.h22
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/fold.c13
-rw-r--r--src/nvim/if_cscope.c43
-rw-r--r--src/nvim/msgpack_rpc/channel.c2
-rw-r--r--src/nvim/msgpack_rpc/helpers.c2
-rw-r--r--src/nvim/normal.c3
-rw-r--r--src/nvim/option.c9
-rw-r--r--src/nvim/os/fs.c8
-rw-r--r--src/nvim/os/shell.c10
-rw-r--r--src/nvim/testdir/Makefile1
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_history.vim65
-rw-r--r--src/nvim/testdir/test_tabpage.vim189
-rw-r--r--src/nvim/version.c25
-rw-r--r--src/nvim/window.c65
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)