aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--Makefile4
-rw-r--r--contrib/local.mk.example2
-rw-r--r--runtime/autoload/health.vim3
-rw-r--r--runtime/autoload/health/nvim.vim51
-rw-r--r--runtime/autoload/remote/host.vim2
-rw-r--r--runtime/doc/deprecated.txt4
-rw-r--r--runtime/doc/eval.txt32
-rw-r--r--runtime/doc/insert.txt2
-rw-r--r--runtime/doc/vim_diff.txt18
-rw-r--r--runtime/ftplugin/man.vim2
-rw-r--r--runtime/syntax/vim.vim8
-rw-r--r--scripts/gendispatch.lua104
-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
-rw-r--r--test/functional/api/buffer_spec.lua3
-rw-r--r--test/functional/api/server_notifications_spec.lua1
-rw-r--r--test/functional/api/tabpage_spec.lua19
-rw-r--r--test/functional/api/vim_spec.lua149
-rw-r--r--test/functional/api/window_spec.lua27
-rw-r--r--test/functional/core/job_spec.lua8
-rw-r--r--test/functional/eval/execute_spec.lua12
-rw-r--r--test/functional/eval/system_spec.lua (renamed from test/functional/shell/viml_system_spec.lua)27
-rw-r--r--test/functional/eval/timer_spec.lua68
-rw-r--r--test/functional/ex_cmds/bang_filter_spec.lua (renamed from test/functional/shell/bang_filter_spec.lua)0
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua (renamed from test/functional/dict_notifications_spec.lua)0
-rw-r--r--test/functional/ex_cmds/write_spec.lua41
-rw-r--r--test/functional/helpers.lua20
-rw-r--r--test/functional/legacy/assert_spec.lua15
-rw-r--r--test/functional/legacy/quickfix_spec.lua117
-rw-r--r--test/functional/terminal/api_spec.lua12
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua48
-rw-r--r--test/functional/viml/completion_spec.lua56
59 files changed, 1612 insertions, 585 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 93e153be53..4aa6a2de32 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -256,6 +256,10 @@ option(
if(TRAVIS_CI_BUILD)
message(STATUS "Travis CI build enabled.")
add_definitions(-Werror)
+ if(DEFINED ENV{BUILD_32BIT})
+ # Get some test coverage for unsigned char
+ add_definitions(-funsigned-char)
+ endif()
endif()
if(CMAKE_BUILD_TYPE MATCHES Debug)
diff --git a/Makefile b/Makefile
index ef4b9cdcb5..8fdf007260 100644
--- a/Makefile
+++ b/Makefile
@@ -13,10 +13,6 @@ CLINT_ERRORS_FILE_PATH := /reports/clint/errors.json
BUILD_TYPE ?= $(shell (type ninja > /dev/null 2>&1 && echo "Ninja") || \
echo "Unix Makefiles")
-ifneq (,$(PREFIX))
- CMAKE_FLAGS += -DCMAKE_INSTALL_PREFIX:PATH="$(PREFIX)"
-endif
-
ifeq (,$(BUILD_TOOL))
ifeq (Ninja,$(BUILD_TYPE))
ifneq ($(shell cmake --help 2>/dev/null | grep Ninja),)
diff --git a/contrib/local.mk.example b/contrib/local.mk.example
index 6959107ea2..a0b2d034e1 100644
--- a/contrib/local.mk.example
+++ b/contrib/local.mk.example
@@ -2,7 +2,7 @@
# Individual entries must be uncommented to take effect.
# By default, the installation prefix is '/usr/local'.
-# PREFIX := /usr/local/nvim-latest
+# CMAKE_EXTRA_FLAGS += -DCMAKE_INSTALL_PREFIX=/usr/local/nvim-latest
# These CFLAGS can be used in addition to those specified in CMakeLists.txt:
# CMAKE_EXTRA_FLAGS="-DCMAKE_C_FLAGS=-ftrapv -Wlogical-op"
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index 783c30cbf6..336adc65e5 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -13,6 +13,9 @@ function! s:enhance_syntax() abort
syntax keyword healthSuggestion SUGGESTIONS
highlight link healthSuggestion String
+
+ " We do not care about markdown syntax errors in :CheckHealth output.
+ highlight! link markdownError Normal
endfunction
" Runs the specified healthchecks.
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index d769525373..60e56034e1 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -57,6 +57,57 @@ function! s:check_manifest() abort
endif
endfunction
+function! s:check_tmux() abort
+ if empty($TMUX) || !executable('tmux')
+ return
+ endif
+ call health#report_start('tmux configuration')
+ let suggestions = ["Set escape-time in ~/.tmux.conf: set-option -sg escape-time 10",
+ \ 'See https://github.com/neovim/neovim/wiki/FAQ']
+ let cmd = 'tmux show-option -qvgs escape-time'
+ let out = system(cmd)
+ let tmux_esc_time = substitute(out, '\v(\s|\r|\n)', '', 'g')
+
+ if v:shell_error
+ call health#report_error('command failed: '.cmd."\n".out)
+ elseif empty(tmux_esc_time)
+ call health#report_error('escape-time is not set', suggestions)
+ elseif tmux_esc_time > 500
+ call health#report_error(
+ \ 'escape-time ('.tmux_esc_time.') is higher than 300ms', suggestions)
+ else
+ call health#report_ok('escape-time = '.tmux_esc_time.'ms')
+ endif
+endfunction
+
+function! s:check_terminfo() abort
+ if !executable('infocmp')
+ return
+ endif
+ call health#report_start('terminfo')
+ let suggestions = [
+ \ "Set key_backspace to \\177 (ASCII BACKSPACE). Run these commands:\n"
+ \ .'infocmp $TERM | sed ''s/kbs=^[hH]/kbs=\\177/'' > $TERM.ti'
+ \ ."\n"
+ \ .'tic $TERM.ti',
+ \ 'See https://github.com/neovim/neovim/wiki/FAQ']
+ let cmd = 'infocmp -L'
+ let out = system(cmd)
+ let kbs_entry = matchstr(out, 'key_backspace=\S*')
+
+ if v:shell_error
+ call health#report_error('command failed: '.cmd."\n".out)
+ elseif !empty(matchstr(out, '\Vkey_backspace=^H'))
+ call health#report_error('key_backspace (kbs) entry is ^H (ASCII DELETE): '
+ \ .kbs_entry, suggestions)
+ else
+ call health#report_info('key_backspace terminfo entry: '
+ \ .(empty(kbs_entry) ? '? (not found)' : kbs_entry))
+ endif
+endfunction
+
function! health#nvim#check() abort
call s:check_manifest()
+ call s:check_tmux()
+ call s:check_terminfo()
endfunction
diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim
index 1f30b91ab8..51f7e5886f 100644
--- a/runtime/autoload/remote/host.vim
+++ b/runtime/autoload/remote/host.vim
@@ -190,7 +190,7 @@ function! s:RegistrationCommands(host) abort
call remote#host#RegisterClone(host_id, a:host)
let pattern = s:plugin_patterns[a:host]
let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1)
- let paths = map(paths, 'tr(v:val,"\\","/")') " Normalize slashes #4795
+ let paths = map(paths, 'tr(resolve(v:val),"\\","/")') " Normalize slashes #4795
let paths = uniq(sort(paths))
if empty(paths)
return []
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 2b69929cfe..6997d331e4 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -6,6 +6,10 @@
Nvim *deprecated*
+The items listed below are "deprecated". This means they will be removed in
+the future. They should not be used in new scripts, and old scripts should be
+updated.
+
==============================================================================
Normal commands ~
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 1eb5ec45c0..cfd62bacfe 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -6840,23 +6840,30 @@ system({cmd} [, {input}]) *system()* *E677*
Get the output of the shell command {cmd} as a |string|. {cmd}
will be run the same as in |jobstart()|. See |systemlist()|
to get the output as a |List|.
-
- When {input} is given and is a string this string is written
- to a file and passed as stdin to the command. The string is
- written as-is, you need to take care of using the correct line
- separators yourself.
- If {input} is given and is a |List| it is written to the file
- in a way |writefile()| does with {binary} set to "b" (i.e.
- with a newline between each list item with newlines inside
- list items converted to NULs).
- Pipes are not used.
+ Not to be used for interactive commands.
+
+ If {input} is a string it is written to a pipe and passed as
+ stdin to the command. The string is written as-is, line
+ separators are not changed.
+ If {input} is a |List| it is written to the pipe as
+ |writefile()| does with {binary} set to "b" (i.e. with
+ a newline between each list item, and newlines inside list
+ items converted to NULs).
+ *E5677*
+ Note: system() cannot write to or read from backgrounded ("&")
+ shell commands, e.g.: >
+ :echo system("cat - &", "foo"))
+< which is equivalent to: >
+ $ echo foo | bash -c 'cat - &'
+< The pipes are disconnected (unless overridden by shell
+ redirection syntax) before input can reach it. Use
+ |jobstart()| instead.
Note: Use |shellescape()| or |::S| with |expand()| or
|fnamemodify()| to escape special characters in a command
argument. Newlines in {cmd} may cause the command to fail.
The characters in 'shellquote' and 'shellxquote' may also
cause trouble.
- This is not to be used for interactive commands.
The result is a String. Example: >
:let files = system("ls " . shellescape(expand('%:h')))
@@ -6871,9 +6878,6 @@ system({cmd} [, {input}]) *system()* *E677*
The command executed is constructed using several options when
{cmd} is a string: 'shell' 'shellcmdflag' {cmd}
- The command will be executed in "cooked" mode, so that a
- CTRL-C will interrupt the command (on Unix at least).
-
The resulting error code can be found in |v:shell_error|.
This function will fail in |restricted-mode|.
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 818d6cf64c..a82e17c857 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1436,7 +1436,7 @@ original HTML files completion of tags (and only tags) isn't context aware.
RUBY *ft-ruby-omni* {Nvim}
-NOTE: Completion for ruby code is not currently provided by Nvim.
+NOTE: |compl-omni| for Ruby code requires |provider-ruby| to be installed.
Ruby completion will parse your buffer on demand in order to provide a list of
completions. These completions will be drawn from modules loaded by 'require'
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 249208911b..bb1f993ab6 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -76,6 +76,15 @@ Python plugins |provider-python|
Clipboard integration |provider-clipboard|
+USER EXPERIENCE ~
+
+A major goal of Nvim is to work intuitively and consistently. For example,
+Nvim does not have `-X`, a platform-specific option available in some Vim
+builds (with potential surprises: http://stackoverflow.com/q/14635295). Nvim
+avoids features that cannot be provided on all platforms--instead that is
+delegated to external plugins/extensions.
+
+
OTHER FEATURES ~
|bracketed-paste-mode| is built-in and enabled by default.
@@ -99,6 +108,7 @@ Options:
Commands:
|:CheckHealth|
+ |:drop| is available on all platforms
|:Man| is available by default, with many improvements such as completion
Functions:
@@ -131,10 +141,10 @@ are always available and may be used simultaneously in separate plugins. The
`neovim` pip package must be installed to use Python plugins in Nvim (see
|provider-python|).
-|:!| and |system()| do not support "interactive" commands; use |:terminal| for
-that instead. Terminal Vim supports interactive |:!| and |system()|, but gui
-Vim does not. See ":help gui-pty" in Vim:
- http://vimdoc.sourceforge.net/htmldoc/gui_x11.html#gui-pty
+|:!| does not support "interactive" commands. Use |:terminal| instead.
+(GUI Vim has a similar limitation, see ":help gui-pty" in Vim.)
+
+|system()| does not support writing/reading "backgrounded" commands. |E5677|
|mkdir()| behaviour changed:
1. Assuming /tmp/foo does not exist and /tmp can be written to
diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim
index 02d2b4e557..f6fefd0155 100644
--- a/runtime/ftplugin/man.vim
+++ b/runtime/ftplugin/man.vim
@@ -21,7 +21,7 @@ if has('vim_starting')
" all caps it is impossible to tell what the original capitilization was.
let ref = tolower(matchstr(getline(1), '^\S\+'))
let b:man_sect = man#extract_sect_and_name_ref(ref)[0]
- execute 'file man://'.ref
+ execute 'silent file man://'.ref
endif
setlocal buftype=nofile
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 5c570eb52a..32e871ea79 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -5,6 +5,14 @@
" Version: 7.4-45
" Automatically generated keyword lists: {{{1
+" #############################################################################
+" #############################################################################
+" Note: Be careful when merging the upstream version of this file.
+" Much of this is generated by scripts/genvimvim.lua (result is installed
+" to: $VIMRUNTIME/syntax/vim/generated.vim)
+" #############################################################################
+" #############################################################################
+
" Quit when a syntax file was already loaded {{{2
if exists("b:current_syntax")
finish
diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua
index 00ed84212b..94789e1ef0 100644
--- a/scripts/gendispatch.lua
+++ b/scripts/gendispatch.lua
@@ -52,16 +52,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path
-- names of all headers relative to the source root (for inclusion in the
-- generated file)
headers = {}
--- output c file(dispatch function + metadata serialized with msgpack)
-outputf = arg[#arg-1]
--- output mpack file (metadata)
+-- output h file with generated dispatch functions
+dispatch_outputf = arg[#arg-2]
+-- output h file with packed metadata
+funcs_metadata_outputf = arg[#arg-1]
+-- output metadata mpack file, for use by other build scripts
mpack_outputf = arg[#arg]
-- set of function names, used to detect duplicates
function_names = {}
-- read each input file, parse and append to the api metadata
-for i = 2, #arg - 2 do
+for i = 2, #arg - 3 do
local full_path = arg[i]
local parts = {}
for part in string.gmatch(full_path, '[^/]+') do
@@ -165,66 +167,27 @@ for _,f in ipairs(functions) do
end
--- start building the output
-output = io.open(outputf, 'wb')
-
-output:write([[
-#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"
+funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb')
+funcs_metadata_output:write([[
+static const uint8_t funcs_metadata[] = {
]])
-for i = 1, #headers do
- if headers[i]:sub(-12) ~= '.generated.h' then
- output:write('\n#include "nvim/'..headers[i]..'"')
- end
-end
-
-output:write([[
-
-
-static const uint8_t msgpack_metadata[] = {
-
-]])
-- serialize the API metadata using msgpack and embed into the resulting
-- binary for easy querying by clients
packed_exported_functions = mpack.pack(exported_functions)
for i = 1, #packed_exported_functions do
- output:write(string.byte(packed_exported_functions, i)..', ')
+ funcs_metadata_output:write(string.byte(packed_exported_functions, i)..', ')
if i % 10 == 0 then
- output:write('\n ')
+ funcs_metadata_output:write('\n ')
end
end
-output:write([[
+funcs_metadata_output:write([[
};
-
-void msgpack_rpc_init_function_metadata(Dictionary *metadata)
-{
- msgpack_unpacked unpacked;
- msgpack_unpacked_init(&unpacked);
- if (msgpack_unpack_next(&unpacked,
- (const char *)msgpack_metadata,
- sizeof(msgpack_metadata),
- NULL) != MSGPACK_UNPACK_SUCCESS) {
- abort();
- }
- Object functions;
- msgpack_rpc_to_object(&unpacked.data, &functions);
- msgpack_unpacked_destroy(&unpacked);
- PUT(*metadata, "functions", functions);
-}
-
]])
+funcs_metadata_output:close()
+
+-- start building the dispatch wrapper output
+output = io.open(dispatch_outputf, 'wb')
local function real_type(type)
local rv = type
@@ -246,7 +209,7 @@ for i = 1, #functions do
if fn.impl_name == nil then
local args = {}
- output:write('Object handle_'..fn.name..'(uint64_t channel_id, uint64_t request_id, Array args, Error *error)')
+ output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
output:write('\n{')
output:write('\n Object ret = NIL;')
-- Declare/initialize variables that will hold converted arguments
@@ -336,22 +299,12 @@ end
-- Generate a function that initializes method names with handler functions
output:write([[
-static Map(String, MsgpackRpcRequestHandler) *methods = NULL;
-
-void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler)
-{
- map_put(String, MsgpackRpcRequestHandler)(methods, method, handler);
-}
-
void msgpack_rpc_init_method_table(void)
{
methods = map_new(String, MsgpackRpcRequestHandler)();
]])
--- Keep track of the maximum method name length in order to avoid walking
--- strings longer than that when searching for a method handler
-local max_fname_len = 0
for i = 1, #functions do
local fn = functions[i]
output:write(' msgpack_rpc_add_method_handler('..
@@ -360,32 +313,9 @@ for i = 1, #functions do
'(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name)..
', .async = '..tostring(fn.async)..'});\n')
- if #fn.name > max_fname_len then
- max_fname_len = #fn.name
- end
end
output:write('\n}\n\n')
-
-output:write([[
-MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
- size_t name_len)
-{
- String m = {
- .data=(char *)name,
- .size=MIN(name_len, ]]..max_fname_len..[[)
- };
- MsgpackRpcRequestHandler rv =
- map_get(String, MsgpackRpcRequestHandler)(methods, m);
-
- if (!rv.fn) {
- rv.fn = msgpack_rpc_handle_missing_method;
- }
-
- return rv;
-}
-]])
-
output:close()
mpack_output = io.open(mpack_outputf, 'wb')
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)
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 71ec213b97..3d3a2bb046 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -1,4 +1,3 @@
--- Sanity checks for buffer_* API calls via msgpack-rpc
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer
local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq
@@ -6,7 +5,7 @@ local curbufmeths, ok = helpers.curbufmeths, helpers.ok
local funcs, request = helpers.funcs, helpers.request
local NIL = helpers.NIL
-describe('buffer_* functions', function()
+describe('api/buf', function()
before_each(clear)
-- access deprecated functions
diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua
index 88e8c60560..78639d7ed7 100644
--- a/test/functional/api/server_notifications_spec.lua
+++ b/test/functional/api/server_notifications_spec.lua
@@ -1,4 +1,3 @@
--- Tests for nvim notifications
local helpers = require('test.functional.helpers')(after_each)
local eq, clear, eval, execute, nvim, next_message =
helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim,
diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua
index 47dede8b44..e10f30085f 100644
--- a/test/functional/api/tabpage_spec.lua
+++ b/test/functional/api/tabpage_spec.lua
@@ -1,4 +1,3 @@
--- Sanity checks for tabpage_* API calls via msgpack-rpc
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, tabpage, curtab, eq, ok =
helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq,
@@ -8,7 +7,7 @@ local funcs = helpers.funcs
local request = helpers.request
local NIL = helpers.NIL
-describe('tabpage_* functions', function()
+describe('api/tabpage', function()
before_each(clear)
describe('list_wins and get_win', function()
@@ -51,6 +50,22 @@ describe('tabpage_* functions', function()
end)
end)
+ describe('get_number', function()
+ it('works', function()
+ local tabs = nvim('list_tabpages')
+ eq(1, tabpage('get_number', tabs[1]))
+
+ nvim('command', 'tabnew')
+ local tab1, tab2 = unpack(nvim('list_tabpages'))
+ eq(1, tabpage('get_number', tab1))
+ eq(2, tabpage('get_number', tab2))
+
+ nvim('command', '-tabmove')
+ eq(2, tabpage('get_number', tab1))
+ eq(1, tabpage('get_number', tab2))
+ end)
+ end)
+
describe('is_valid', function()
it('works', function()
nvim('command', 'tabnew')
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 6b30c0e81c..ce6c52e334 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1,4 +1,3 @@
--- Sanity checks for vim_* API calls via msgpack-rpc
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local NIL = helpers.NIL
@@ -9,10 +8,10 @@ local meths = helpers.meths
local funcs = helpers.funcs
local request = helpers.request
-describe('vim_* functions', function()
+describe('api', function()
before_each(clear)
- describe('command', function()
+ describe('nvim_command', function()
it('works', function()
local fname = helpers.tmpname()
nvim('command', 'new')
@@ -29,9 +28,18 @@ describe('vim_* functions', function()
f:close()
os.remove(fname)
end)
+
+ it("VimL error: fails (VimL error), does NOT update v:errmsg", function()
+ -- Most API methods return generic errors (or no error) if a VimL
+ -- expression fails; nvim_command returns the VimL error details.
+ local status, rv = pcall(nvim, "command", "bogus_command")
+ eq(false, status) -- nvim_command() failed.
+ eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned.
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ end)
end)
- describe('eval', function()
+ describe('nvim_eval', function()
it('works', function()
nvim('command', 'let g:v1 = "a"')
nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]')
@@ -46,18 +54,41 @@ describe('vim_* functions', function()
it('works under deprecated name', function()
eq(2, request("vim_eval", "1+1"))
end)
+
+ it("VimL error: fails (generic error), does NOT update v:errmsg", function()
+ local status, rv = pcall(nvim, "eval", "bogus expression")
+ eq(false, status) -- nvim_eval() failed.
+ ok(nil ~= string.find(rv, "Failed to evaluate expression"))
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ end)
end)
- describe('call_function', function()
+ describe('nvim_call_function', function()
it('works', function()
nvim('call_function', 'setqflist', {{{ filename = 'something', lnum = 17}}, 'r'})
eq(17, nvim('call_function', 'getqflist', {})[1].lnum)
eq(17, nvim('call_function', 'eval', {17}))
eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'}))
end)
+
+ it("VimL error: fails (generic error), does NOT update v:errmsg", function()
+ local status, rv = pcall(nvim, "call_function", "bogus function", {"arg1"})
+ eq(false, status) -- nvim_call_function() failed.
+ ok(nil ~= string.find(rv, "Error calling function"))
+ eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ end)
end)
- describe('strwidth', function()
+ describe('nvim_input', function()
+ it("VimL error: does NOT fail, updates v:errmsg", function()
+ local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>")
+ local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:")
+ eq(true, status) -- nvim_input() did not fail.
+ eq("E117:", v_errnum) -- v:errmsg was updated.
+ end)
+ end)
+
+ describe('nvim_strwidth', function()
it('works', function()
eq(3, nvim('strwidth', 'abc'))
-- 6 + (neovim)
@@ -70,7 +101,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set}_current_line', function()
+ describe('nvim_get_current_line, nvim_set_current_line', function()
it('works', function()
eq('', nvim('get_current_line'))
nvim('set_current_line', 'abc')
@@ -78,7 +109,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set,del}_var', function()
+ describe('nvim_get_var, nvim_set_var, nvim_del_var', function()
it('works', function()
nvim('set_var', 'lua', {1, 2, {['3'] = 1}})
eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua'))
@@ -109,7 +140,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set}_option', function()
+ describe('nvim_get_option, nvim_set_option', function()
it('works', function()
ok(nvim('get_option', 'equalalways'))
nvim('set_option', 'equalalways', false)
@@ -117,7 +148,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set}_current_buf and list_bufs', function()
+ describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
it('works', function()
eq(1, #nvim('list_bufs'))
eq(nvim('list_bufs')[1], nvim('get_current_buf'))
@@ -129,7 +160,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set}_current_win and list_wins', function()
+ describe('nvim_{get,set}_current_win, nvim_list_wins', function()
it('works', function()
eq(1, #nvim('list_wins'))
eq(nvim('list_wins')[1], nvim('get_current_win'))
@@ -142,7 +173,7 @@ describe('vim_* functions', function()
end)
end)
- describe('{get,set}_current_tabpage and list_tabpages', function()
+ describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function()
it('works', function()
eq(1, #nvim('list_tabpages'))
eq(nvim('list_tabpages')[1], nvim('get_current_tabpage'))
@@ -161,7 +192,7 @@ describe('vim_* functions', function()
end)
end)
- describe('replace_termcodes', function()
+ describe('nvim_replace_termcodes', function()
it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true))
end)
@@ -183,7 +214,7 @@ describe('vim_* functions', function()
end)
end)
- describe('feedkeys', function()
+ describe('nvim_feedkeys', function()
it('CSI escaping', function()
local function on_setup()
-- notice the special char(…) \xe2\80\xa6
@@ -210,7 +241,7 @@ describe('vim_* functions', function()
end)
end)
- describe('err_write', function()
+ describe('nvim_err_write', function()
local screen
before_each(function()
@@ -298,6 +329,94 @@ describe('vim_* functions', function()
end)
end)
+ describe('nvim_call_atomic', function()
+ it('works', function()
+ meths.buf_set_lines(0, 0, -1, true, {'first'})
+ local req = {
+ {'nvim_get_current_line', {}},
+ {'nvim_set_current_line', {'second'}},
+ }
+ eq({{'first', NIL}, NIL}, meths.call_atomic(req))
+ eq({'second'}, meths.buf_get_lines(0, 0, -1, true))
+ end)
+
+ it('allows multiple return values', function()
+ local req = {
+ {'nvim_set_var', {'avar', true}},
+ {'nvim_set_var', {'bvar', 'string'}},
+ {'nvim_get_var', {'avar'}},
+ {'nvim_get_var', {'bvar'}},
+ }
+ eq({{NIL, NIL, true, 'string'}, NIL}, meths.call_atomic(req))
+ end)
+
+ it('is aborted by errors in call', function()
+ local error_types = meths.get_api_info()[2].error_types
+ local req = {
+ {'nvim_set_var', {'one', 1}},
+ {'nvim_buf_set_lines', {}},
+ {'nvim_set_var', {'two', 2}},
+ }
+ eq({{NIL}, {1, error_types.Exception.id,
+ 'Wrong number of arguments: expecting 5 but got 0'}},
+ meths.call_atomic(req))
+ eq(1, meths.get_var('one'))
+ eq(false, pcall(meths.get_var, 'two'))
+
+ -- still returns all previous successful calls
+ req = {
+ {'nvim_set_var', {'avar', 5}},
+ {'nvim_set_var', {'bvar', 'string'}},
+ {'nvim_get_var', {'avar'}},
+ {'nvim_buf_get_lines', {0, 10, 20, true}},
+ {'nvim_get_var', {'bvar'}},
+ }
+ eq({{NIL, NIL, 5}, {3, error_types.Validation.id, 'Index out of bounds'}},
+ meths.call_atomic(req))
+
+ req = {
+ {'i_am_not_a_method', {'xx'}},
+ {'nvim_set_var', {'avar', 10}},
+ }
+ eq({{}, {0, error_types.Exception.id, 'Invalid method name'}},
+ meths.call_atomic(req))
+ eq(5, meths.get_var('avar'))
+ end)
+
+ it('throws error on malformated arguments', function()
+ local req = {
+ {'nvim_set_var', {'avar', 1}},
+ {'nvim_set_var'},
+ {'nvim_set_var', {'avar', 2}},
+ }
+ local status, err = pcall(meths.call_atomic, req)
+ eq(false, status)
+ ok(err:match(' All items in calls array must be arrays of size 2') ~= nil)
+ -- call before was done, but not after
+ eq(1, meths.get_var('avar'))
+
+ req = {
+ {'nvim_set_var', {'bvar', {2,3}}},
+ 12,
+ }
+ status, err = pcall(meths.call_atomic, req)
+ eq(false, status)
+ ok(err:match('All items in calls array must be arrays') ~= nil)
+ eq({2,3}, meths.get_var('bvar'))
+
+ req = {
+ {'nvim_set_current_line', 'little line'},
+ {'nvim_set_var', {'avar', 3}},
+ }
+ status, err = pcall(meths.call_atomic, req)
+ eq(false, status)
+ ok(err:match('args must be Array') ~= nil)
+ -- call before was done, but not after
+ eq(1, meths.get_var('avar'))
+ eq({''}, meths.buf_get_lines(0, 0, -1, true))
+ end)
+ end)
+
it('can throw exceptions', function()
local status, err = pcall(nvim, 'get_option', 'invalid-option')
eq(false, status)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 2c43e4db2c..465bda6bc9 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1,4 +1,3 @@
--- Sanity checks for window_* API calls via msgpack-rpc
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf,
@@ -29,7 +28,7 @@ local function is_visible(str)
return false
end
-describe('window_* functions', function()
+describe('api/win', function()
before_each(clear)
describe('get_buf', function()
@@ -200,6 +199,30 @@ describe('window_* functions', function()
end)
end)
+ describe('get_number', function()
+ it('works', function()
+ local wins = nvim('list_wins')
+ eq(1, window('get_number', wins[1]))
+
+ nvim('command', 'split')
+ local win1, win2 = unpack(nvim('list_wins'))
+ eq(1, window('get_number', win1))
+ eq(2, window('get_number', win2))
+
+ nvim('command', 'wincmd J')
+ eq(2, window('get_number', win1))
+ eq(1, window('get_number', win2))
+
+ nvim('command', 'tabnew')
+ local win3 = nvim('list_wins')[3]
+ -- First tab page
+ eq(2, window('get_number', win1))
+ eq(1, window('get_number', win2))
+ -- Second tab page
+ eq(1, window('get_number', win3))
+ end)
+ end)
+
describe('is_valid', function()
it('works', function()
nvim('command', 'split')
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index adbe17d4b5..5872ebe8ee 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -121,14 +121,14 @@ describe('jobs', function()
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
- it('can preserve newlines', function()
+ it('preserves newlines', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', 'call jobsend(j, "a\\n\\nc\\n\\n\\n\\nb\\n\\n")')
eq({'notification', 'stdout',
{0, {'a', '', 'c', '', '', '', 'b', '', ''}}}, next_msg())
end)
- it('can preserve NULs', function()
+ it('preserves NULs', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', 'call jobsend(j, ["\n123\n", "abc\\nxyz\n", ""])')
eq({'notification', 'stdout', {0, {'\n123\n', 'abc\nxyz\n', ''}}},
@@ -137,7 +137,7 @@ describe('jobs', function()
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
- it('can avoid sending final newline', function()
+ it('avoids sending final newline', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', 'call jobsend(j, ["some data", "without\nfinal nl"])')
eq({'notification', 'stdout', {0, {'some data', 'without\nfinal nl'}}},
@@ -146,7 +146,7 @@ describe('jobs', function()
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
- it('can close the job streams with jobclose', function()
+ it('closes the job streams with jobclose', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', 'call jobclose(j, "stdin")')
eq({'notification', 'exit', {0, 0}}, next_msg())
diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua
index 10a6de6eb4..b5b481435a 100644
--- a/test/functional/eval/execute_spec.lua
+++ b/test/functional/eval/execute_spec.lua
@@ -70,16 +70,16 @@ describe('execute()', function()
end)
it('silences command run inside', function()
- local screen = Screen.new(20, 5)
+ local screen = Screen.new(40, 5)
screen:attach()
screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} )
feed(':let g:mes = execute("echon 42")<CR>')
screen:expect([[
- ^ |
- {0:~ }|
- {0:~ }|
- {0:~ }|
- |
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :let g:mes = execute("echon 42") |
]])
eq('42', eval('g:mes'))
end)
diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/eval/system_spec.lua
index b8de7cc86f..6393477260 100644
--- a/test/functional/shell/viml_system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -1,10 +1,7 @@
--- Specs for
--- - `system()`
--- - `systemlist()`
-
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, eval, feed, nvim =
- helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.nvim
+local eq, clear, eval, execute, feed, nvim =
+ helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.feed,
+ helpers.nvim
local Screen = require('test.functional.ui.screen')
@@ -120,12 +117,30 @@ describe('system()', function()
it('returns the program output', function()
eq("echoed", eval('system("echo -n echoed")'))
end)
+ it('to backgrounded command does not crash', function()
+ -- This is indeterminate, just exercise the codepath. May get E5677.
+ execute('call system("echo -n echoed &")')
+ local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
+ if v_errnum then
+ eq("E5677:", v_errnum)
+ end
+ eq(2, eval("1+1")) -- Still alive?
+ end)
end)
describe('passing input', function()
it('returns the program output', function()
eq("input", eval('system("cat -", "input")'))
end)
+ it('to backgrounded command does not crash', function()
+ -- This is indeterminate, just exercise the codepath. May get E5677.
+ execute('call system("cat - &")')
+ local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
+ if v_errnum then
+ eq("E5677:", v_errnum)
+ end
+ eq(2, eval("1+1")) -- Still alive?
+ end)
end)
describe('passing a lot of input', function()
diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua
index 295c763d74..fba9466b78 100644
--- a/test/functional/eval/timer_spec.lua
+++ b/test/functional/eval/timer_spec.lua
@@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen')
local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs
+local curbufmeths = helpers.curbufmeths
describe('timers', function()
before_each(function()
@@ -62,19 +63,76 @@ describe('timers', function()
end)
it('are paused when event processing is disabled', function()
- -- this is not the intended behavior, but at least there will
- -- not be a burst of queued up callbacks
- execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
run(nil, nil, nil, 100)
local count = eval("g:val")
+ -- shows two line error message and thus invokes the return prompt.
+ -- if we start to allow event processing here, we need to change this test.
+ execute("throw 'fatal error'")
+ run(nil, nil, nil, 300)
+ feed("<cr>")
+ local diff = eval("g:val") - count
+ ok(0 <= diff and diff <= 4)
+ end)
+
+ it('are triggered in blocking getchar() call', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
nvim_async("command", "let g:c = getchar()")
run(nil, nil, nil, 300)
feed("c")
- local diff = eval("g:val") - count
- ok(0 <= diff and diff <= 2)
+ local count = eval("g:val")
+ ok(count >= 5)
eq(99, eval("g:c"))
end)
+ it('can invoke redraw in blocking getchar() call', function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ })
+
+ curbufmeths.set_lines(0, -1, true, {"ITEM 1", "ITEM 2"})
+ source([[
+ func! AddItem(timer)
+ call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3'])
+ redraw
+ endfunc
+ call timer_start(200, 'AddItem')
+ ]])
+ nvim_async("command", "let g:c2 = getchar()")
+
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ screen:sleep(200)
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ feed("3")
+ eq(51, eval("g:c2"))
+ screen:expect([[
+ ^ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
it('can be stopped', function()
local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})")
eq(0,eval("g:val"))
diff --git a/test/functional/shell/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua
index a320e6d018..a320e6d018 100644
--- a/test/functional/shell/bang_filter_spec.lua
+++ b/test/functional/ex_cmds/bang_filter_spec.lua
diff --git a/test/functional/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index dc87312911..dc87312911 100644
--- a/test/functional/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua
index c1bc5d3140..4ac9f312ef 100644
--- a/test/functional/ex_cmds/write_spec.lua
+++ b/test/functional/ex_cmds/write_spec.lua
@@ -1,20 +1,26 @@
--- Specs for :write
-
local helpers = require('test.functional.helpers')(after_each)
-local eq, eval, clear, write_file, execute, source =
- helpers.eq, helpers.eval, helpers.clear, helpers.write_file,
- helpers.execute, helpers.source
+local eq, eval, clear, write_file, execute, source, insert =
+ helpers.eq, helpers.eval, helpers.clear, helpers.write_file,
+ helpers.execute, helpers.source, helpers.insert
if helpers.pending_win32(pending) then return end
describe(':write', function()
- after_each(function()
+ local function cleanup()
os.remove('test_bkc_file.txt')
os.remove('test_bkc_link.txt')
+ os.remove('test_fifo')
+ end
+ before_each(function()
+ clear()
+ cleanup()
+ end)
+ after_each(function()
+ cleanup()
end)
it('&backupcopy=auto preserves symlinks', function()
- clear('--cmd', 'set backupcopy=auto')
+ execute('set backupcopy=auto')
write_file('test_bkc_file.txt', 'content0')
execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
source([[
@@ -27,7 +33,7 @@ describe(':write', function()
end)
it('&backupcopy=no replaces symlink with new file', function()
- clear('--cmd', 'set backupcopy=no')
+ execute('set backupcopy=no')
write_file('test_bkc_file.txt', 'content0')
execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
source([[
@@ -38,4 +44,23 @@ describe(':write', function()
eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')"))
eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')"))
end)
+
+ it("appends FIFO file", function()
+ if eval("executable('mkfifo')") == 0 then
+ pending('missing "mkfifo" command', function()end)
+ return
+ end
+
+ local text = "some fifo text from write_spec"
+ assert(os.execute("mkfifo test_fifo"))
+ insert(text)
+
+ -- Blocks until a consumer reads the FIFO.
+ execute("write >> test_fifo")
+
+ -- Read the FIFO, this will unblock the :write above.
+ local fifo = assert(io.open("test_fifo"))
+ eq(text.."\n", fifo:read("*all"))
+ fifo:close()
+ end)
end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 325f41e506..ff62b4de86 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -1,11 +1,13 @@
require('coxpcall')
local lfs = require('lfs')
-local ChildProcessStream = require('nvim.child_process_stream')
-local SocketStream = require('nvim.socket_stream')
-local TcpStream = require('nvim.tcp_stream')
-local Session = require('nvim.session')
local global_helpers = require('test.helpers')
+-- nvim client: Found in .deps/usr/share/lua/<version>/nvim/ if "bundled".
+local Session = require('nvim.session')
+local TcpStream = require('nvim.tcp_stream')
+local SocketStream = require('nvim.socket_stream')
+local ChildProcessStream = require('nvim.child_process_stream')
+
local check_logs = global_helpers.check_logs
local neq = global_helpers.neq
local eq = global_helpers.eq
@@ -134,10 +136,14 @@ local function stop()
session:stop()
end
+-- Executes an ex-command. VimL errors manifest as client (lua) errors, but
+-- v:errmsg will not be updated.
local function nvim_command(cmd)
request('nvim_command', cmd)
end
+-- Evaluates a VimL expression.
+-- Fails on VimL error, but does not update v:errmsg.
local function nvim_eval(expr)
return request('nvim_eval', expr)
end
@@ -158,10 +164,14 @@ local os_name = (function()
end)
end)()
+-- Executes a VimL function.
+-- Fails on VimL error, but does not update v:errmsg.
local function nvim_call(name, ...)
return request('nvim_call_function', name, {...})
end
+-- Sends user input to Nvim.
+-- Does not fail on VimL error, but v:errmsg will be updated.
local function nvim_feed(input)
while #input > 0 do
local written = request('nvim_input', input)
@@ -279,6 +289,8 @@ local function insert(...)
nvim_feed('<ESC>')
end
+-- Executes an ex-command by user input. Because nvim_input() is used, VimL
+-- errors will not manifest as client (lua) errors. Use command() for that.
local function execute(...)
for _, v in ipairs({...}) do
if v:sub(1, 1) ~= '/' then
diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua
index 42dd25023a..8a042be7f7 100644
--- a/test/functional/legacy/assert_spec.lua
+++ b/test/functional/legacy/assert_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local nvim, call = helpers.meths, helpers.call
local clear, eq = helpers.clear, helpers.eq
local source, execute = helpers.source, helpers.execute
+local exc_exec = helpers.exc_exec
local function expected_errors(errors)
eq(errors, nvim.get_vvar('errors'))
@@ -62,6 +63,20 @@ describe('assert function:', function()
call('assert_equal', 'true', 'false')
expected_errors({"Expected 'true' but got 'false'"})
end)
+
+ it('should change v:errors when expected is not equal to actual', function()
+ source([[
+ function CheckAssert()
+ let s:v = {}
+ let s:x = {"a": s:v}
+ let s:v["b"] = s:x
+ let s:w = {"c": s:x, "d": ''}
+ call assert_equal(s:w, '')
+ endfunction
+ ]])
+ eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
+ exc_exec('call CheckAssert()'))
+ end)
end)
-- assert_notequal({expected}, {actual}[, {msg}])
diff --git a/test/functional/legacy/quickfix_spec.lua b/test/functional/legacy/quickfix_spec.lua
index df8f2625db..7657b8641b 100644
--- a/test/functional/legacy/quickfix_spec.lua
+++ b/test/functional/legacy/quickfix_spec.lua
@@ -277,6 +277,118 @@ describe('helpgrep', function()
augroup! testgroup
endfunction
+
+ " This will test for problems in quickfix:
+ " A. incorrectly copying location lists which caused the location list to show
+ " a different name than the file that was actually being displayed.
+ " B. not reusing the window for which the location list window is opened but
+ " instead creating new windows.
+ " C. make sure that the location list window is not reused instead of the
+ " window it belongs to.
+ "
+ " Set up the test environment:
+ function! ReadTestProtocol(name)
+ let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '')
+ let word = substitute(base, '\v(.*)\..*', '\1', '')
+
+ setl modifiable
+ setl noreadonly
+ setl noswapfile
+ setl bufhidden=delete
+ %del _
+ " For problem 2:
+ " 'buftype' has to be set to reproduce the constant opening of new windows
+ setl buftype=nofile
+
+ call setline(1, word)
+
+ setl nomodified
+ setl nomodifiable
+ setl readonly
+ exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '')
+ endfunction
+
+ function Test_locationlist()
+ enew
+
+ augroup testgroup
+ au!
+ autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>"))
+ augroup END
+
+ let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ]
+
+ let qflist = []
+ for word in words
+ call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', })
+ " NOTE: problem 1:
+ " intentionally not setting 'lnum' so that the quickfix entries are not
+ " valid
+ call setloclist(0, qflist, ' ')
+ endfor
+
+ " Test A
+ lrewind
+ enew
+ lopen
+ lnext
+ lnext
+ lnext
+ lnext
+ vert split
+ wincmd L
+ lopen
+ wincmd p
+ lnext
+ let fileName = expand("%")
+ wincmd p
+ let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '')
+ let fileName = substitute(fileName, '\\', '/', 'g')
+ let locationListFileName = substitute(locationListFileName, '\\', '/', 'g')
+ call assert_equal("test://bar.txt", fileName)
+ call assert_equal("test://bar.txt", locationListFileName)
+
+ wincmd n | only
+
+ " Test B:
+ lrewind
+ lopen
+ 2
+ exe "normal \<CR>"
+ wincmd p
+ 3
+ exe "normal \<CR>"
+ wincmd p
+ 4
+ exe "normal \<CR>"
+ call assert_equal(2, winnr('$'))
+ wincmd n | only
+
+ " Test C:
+ lrewind
+ lopen
+ " Let's move the location list window to the top to check whether it (the
+ " first window found) will be reused when we try to open new windows:
+ wincmd K
+ 2
+ exe "normal \<CR>"
+ wincmd p
+ 3
+ exe "normal \<CR>"
+ wincmd p
+ 4
+ exe "normal \<CR>"
+ 1wincmd w
+ call assert_equal('quickfix', &buftype)
+ 2wincmd w
+ let bufferName = expand("%")
+ let bufferName = substitute(bufferName, '\\', '/', 'g')
+ call assert_equal('test://quux.txt', bufferName)
+
+ wincmd n | only
+
+ augroup! testgroup
+ endfunction
]])
end)
@@ -339,4 +451,9 @@ describe('helpgrep', function()
call('Test_locationlist_curwin_was_closed')
expected_empty()
end)
+
+ it('checks locationlist protocol read', function()
+ call('Test_locationlist')
+ expected_empty()
+ end)
end)
diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua
index 58d6c75940..045bdb0749 100644
--- a/test/functional/terminal/api_spec.lua
+++ b/test/functional/terminal/api_spec.lua
@@ -22,7 +22,7 @@ describe('api', function()
-- Start the socket from the child nvim.
child_session.feed_data(":echo serverstart('"..socket_name.."')\n")
- -- Wait for socket creation by abusing expect().
+ -- Wait for socket creation.
screen:expect([[
{1: } |
{4:~ }|
@@ -37,6 +37,16 @@ describe('api', function()
local socket_session2 = helpers.connect(socket_name)
child_session.feed_data("i[tui] insert-mode")
+ -- Wait for stdin to be processed.
+ screen:expect([[
+ [tui] insert-mode{1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true}))
ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true}))
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 09b4eaa8d5..4247be0417 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -13,6 +13,42 @@ describe(':terminal', function()
clear()
screen = Screen.new(50, 4)
screen:attach({rgb=false})
+ end)
+
+ it("does not interrupt Press-ENTER prompt #2748", function()
+ -- Ensure that :messages shows Press-ENTER.
+ source([[
+ echomsg "msg1"
+ echomsg "msg2"
+ ]])
+ -- Invoke a command that emits frequent terminal activity.
+ execute([[terminal while true; do echo X; done]])
+ helpers.feed([[<C-\><C-N>]])
+ screen:expect([[
+ X |
+ X |
+ ^X |
+ |
+ ]])
+ helpers.sleep(10) -- Let some terminal activity happen.
+ execute("messages")
+ screen:expect([[
+ X |
+ msg1 |
+ msg2 |
+ Press ENTER or type command to continue^ |
+ ]])
+ end)
+
+end)
+
+describe(':terminal (with fake shell)', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 4)
+ screen:attach({rgb=false})
-- shell-test.c is a fake shell that prints its arguments and exits.
nvim('set_option', 'shell', nvim_dir..'/shell-test')
nvim('set_option', 'shellcmdflag', 'EXE')
@@ -20,12 +56,12 @@ describe(':terminal', function()
-- Invokes `:terminal {cmd}` using a fake shell (shell-test.c) which prints
-- the {cmd} and exits immediately .
- local function terminal_run_fake_shell_cmd(cmd)
+ local function terminal_with_fake_shell(cmd)
execute("terminal "..(cmd and cmd or ""))
end
it('with no argument, acts like termopen()', function()
- terminal_run_fake_shell_cmd()
+ terminal_with_fake_shell()
wait()
screen:expect([[
ready $ |
@@ -36,7 +72,7 @@ describe(':terminal', function()
end)
it('executes a given command through the shell', function()
- terminal_run_fake_shell_cmd('echo hi')
+ terminal_with_fake_shell('echo hi')
wait()
screen:expect([[
ready $ echo hi |
@@ -47,7 +83,7 @@ describe(':terminal', function()
end)
it('allows quotes and slashes', function()
- terminal_run_fake_shell_cmd([[echo 'hello' \ "world"]])
+ terminal_with_fake_shell([[echo 'hello' \ "world"]])
wait()
screen:expect([[
ready $ echo 'hello' \ "world" |
@@ -66,14 +102,14 @@ describe(':terminal', function()
end)
it('ignores writes if the backing stream closes', function()
- terminal_run_fake_shell_cmd()
+ terminal_with_fake_shell()
helpers.feed('iiXXXXXXX')
wait()
-- Race: Though the shell exited (and streams were closed by SIGCHLD
-- handler), :terminal cleanup is pending on the main-loop.
-- This write should be ignored (not crash, #5445).
helpers.feed('iiYYYYYYY')
- wait()
+ eq(2, eval("1+1")) -- Still alive?
end)
end)
diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua
index 0897c2d836..da3400fb27 100644
--- a/test/functional/viml/completion_spec.lua
+++ b/test/functional/viml/completion_spec.lua
@@ -317,6 +317,62 @@ describe('completion', function()
end)
end)
+ describe('completeopt+=noinsert does not add blank undo items', function()
+ before_each(function()
+ source([[
+ function! TestComplete() abort
+ call complete(1, ['foo', 'bar'])
+ return ''
+ endfunction
+ ]])
+ execute('set completeopt+=noselect,noinsert')
+ execute('inoremap <right> <c-r>=TestComplete()<cr>')
+ end)
+
+ local tests = {
+ ['<up>, <down>, <cr>'] = {'<down><cr>', '<up><cr>'},
+ ['<c-n>, <c-p>, <c-y>'] = {'<c-n><c-y>', '<c-p><c-y>'},
+ }
+
+ for name, seq in pairs(tests) do
+ it('using ' .. name, function()
+ feed('iaaa<esc>')
+ feed('A<right>' .. seq[1] .. '<esc>')
+ feed('A<right><esc>A<right><esc>')
+ feed('A<cr>bbb<esc>')
+ feed('A<right>' .. seq[2] .. '<esc>')
+ feed('A<right><esc>A<right><esc>')
+ feed('A<cr>ccc<esc>')
+ feed('A<right>' .. seq[1] .. '<esc>')
+ feed('A<right><esc>A<right><esc>')
+
+ local expected = {
+ {'foo', 'bar', 'foo'},
+ {'foo', 'bar', 'ccc'},
+ {'foo', 'bar'},
+ {'foo', 'bbb'},
+ {'foo'},
+ {'aaa'},
+ {''},
+ }
+
+ for i = 1, #expected do
+ if i > 1 then
+ feed('u')
+ end
+ eq(expected[i], eval('getline(1, "$")'))
+ end
+
+ for i = #expected, 1, -1 do
+ if i < #expected then
+ feed('<c-r>')
+ end
+ eq(expected[i], eval('getline(1, "$")'))
+ end
+ end)
+ end
+ end)
+
describe("refresh:always", function()
before_each(function()
source([[