aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--runtime/doc/autocmd.txt15
-rw-r--r--runtime/doc/diff.txt2
-rw-r--r--runtime/doc/intro.txt3
-rw-r--r--runtime/doc/motion.txt5
-rw-r--r--runtime/doc/usr_01.txt9
-rw-r--r--runtime/doc/usr_05.txt5
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor11
-rw-r--r--src/nvim/api/private/helpers.c17
-rw-r--r--src/nvim/api/vim.c112
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/channel.c148
-rw-r--r--src/nvim/charset.c4
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/msgpack_rpc/channel.c19
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h1
-rw-r--r--src/nvim/os/pty_process_unix.c5
-rw-r--r--src/nvim/os/pty_process_win.c5
-rw-r--r--src/nvim/screen.c3
-rw-r--r--src/nvim/tag.c11
-rw-r--r--src/nvim/terminal.c5
-rw-r--r--src/nvim/testdir/Makefile2
-rw-r--r--src/nvim/testdir/test_fold.vim57
-rw-r--r--test/functional/api/vim_spec.lua109
-rw-r--r--test/functional/ex_cmds/write_spec.lua20
-rw-r--r--test/functional/ex_cmds/wviminfo_spec.lua18
-rw-r--r--test/functional/legacy/011_autocommands_spec.lua5
-rw-r--r--test/functional/legacy/097_glob_path_spec.lua2
-rw-r--r--test/functional/legacy/delete_spec.lua6
-rw-r--r--third-party/CMakeLists.txt3
30 files changed, 509 insertions, 100 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5a2987f01..4cafdef73f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -290,13 +290,14 @@ option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
-if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-undefined")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined")
# For O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags on older systems
# (pre POSIX.1-2008: glibc 2.11 and earlier). #4042
+ # For ptsname(). #6743
add_definitions(-D_GNU_SOURCE)
endif()
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 0b7cd5b2cd..df36969fe3 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -261,6 +261,8 @@ Name triggered by ~
|SwapExists| detected an existing swap file
|TermOpen| when a terminal job starts
|TermClose| when a terminal job ends
+|ChanOpen| after a channel opened
+|ChanInfo| after a channel has its state changed
Options
|FileType| when the 'filetype' option has been set
@@ -487,6 +489,19 @@ BufWriteCmd Before writing the whole buffer to a file.
*BufWritePost*
BufWritePost After writing the whole buffer to a file
(should undo the commands for BufWritePre).
+ *ChanInfo*
+ChanInfo State of channel changed, for instance the
+ client of a RPC channel described itself.
+ Sets these |v:event| keys:
+ info
+ See |nvim_get_chan_info| for the format of the
+ info Dictionary.
+ *ChanOpen*
+ChanOpen Just after a channel was opened.
+ Sets these |v:event| keys:
+ info
+ See |nvim_get_chan_info| for the format of the
+ info Dictionary.
*CmdUndefined*
CmdUndefined When a user command is used but it isn't
defined. Useful for defining a command only
diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt
index b9dccc42a8..c6c827a748 100644
--- a/runtime/doc/diff.txt
+++ b/runtime/doc/diff.txt
@@ -165,7 +165,7 @@ loaded. Since Vim doesn't allow having two buffers for the same file, you
need another buffer. This command is useful: >
command DiffOrig vert new | set buftype=nofile | read ++edit # | 0d_
\ | diffthis | wincmd p | diffthis
-(this is in |vimrc_example.vim|). Use ":DiffOrig" to see the differences
+Use ":DiffOrig" to see the differences
between the current buffer and the file it was loaded from.
A buffer that is unloaded cannot be used for the diff. But it does work for
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index 6c40264d86..e7fb632de8 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -655,9 +655,6 @@ Q Switch to "Ex" mode. This is a bit like typing ":"
Vim will enter this mode by default if it's invoked as
"ex" on the command-line.
Use the ":vi" command |:visual| to exit "Ex" mode.
- Note: In older versions of Vim "Q" formatted text,
- that is now done with |gq|. But if you use the
- |vimrc_example.vim| script "Q" works like "gq".
*gQ*
gQ Switch to "Ex" mode like with "Q", but really behave
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 600eb3ab9e..6f3a585ff3 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -761,9 +761,8 @@ g'{mark} g`{mark}
Jump to the {mark}, but don't change the jumplist when
jumping within the current buffer. Example: >
g`"
-< jumps to the last known position in a file. See
- $VIMRUNTIME/vimrc_example.vim.
- Also see |:keepjumps|.
+< jumps to the last known position in a file.
+ See also |:keepjumps|.
*:marks*
:marks List all the current marks (not a motion command).
diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt
index f3a5728d72..bc55e7cdce 100644
--- a/runtime/doc/usr_01.txt
+++ b/runtime/doc/usr_01.txt
@@ -57,12 +57,11 @@ make them visible with: >
==============================================================================
*01.2* Vim installed *setup-vimrc_example*
-It's not required for this tutorial, but we provide an example vimrc you may
-use:
+To create an empty vimrc: >
- :!cp -i $VIMRUNTIME/vimrc_example.vim ~/.config/nvim/init.vim
-
-If the file already exists you probably want to keep it.
+ :call mkdir(stdpath('config'),'p')
+ :exe 'edit' stdpath('config').'/init.vim'
+ :write
For more info see |vimrc|.
diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt
index cb7bf94ddc..d1491e6b31 100644
--- a/runtime/doc/usr_05.txt
+++ b/runtime/doc/usr_05.txt
@@ -58,10 +58,9 @@ to write a Vim script file: |usr_41.txt|.
==============================================================================
*05.2* The example vimrc file explained *vimrc_example.vim*
-In the first chapter was explained how the example vimrc file can be used.
-The file can be found here:
+In the first chapter was explained how to create a vimrc file. >
- $VIMRUNTIME/vimrc_example.vim ~
+ :exe 'edit' stdpath('config').'/init.vim'
In this section we will explain the various commands used in this file. This
will give you hints about how to set up your own preferences. Not everything
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index dce98d53a4..0d03103bca 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -906,16 +906,13 @@ You can find help on just about any subject, by giving an argument to the
Vim has many more features than Vi, but most of them are disabled by
default. To start using more features you have to create a "vimrc" file.
- 1. Start editing the "vimrc" file. This depends on your system:
- `:e ~/.config/nvim/init.vim`{vim} for Unix-like systems
+ 1. Start editing the "vimrc" file.
+ `:call mkdir(stdpath('config'),'p')`{vim}
+ `:exe 'edit' stdpath('config').'/init.vim'`{vim}
- 2. Now read the example "vimrc" file contents:
- `:r $VIMRUNTIME/vimrc_example.vim`{vim}
-
- 3. Write the file with:
+ 2. Write the file with:
`:w`{vim}
- The next time you start Vim it will use syntax highlighting.
You can add all your preferred settings to this "vimrc" file.
For more information type `:help vimrc-intro`{vim}.
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 17ee3ed711..692a0b51fd 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1030,6 +1030,16 @@ Array copy_array(Array array)
return rv;
}
+Dictionary copy_dictionary(Dictionary dict)
+{
+ Dictionary rv = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < dict.size; i++) {
+ KeyValuePair item = dict.items[i];
+ PUT(rv, item.key.data, copy_object(item.value));
+ }
+ return rv;
+}
+
/// Creates a deep clone of an object
Object copy_object(Object obj)
{
@@ -1047,12 +1057,7 @@ Object copy_object(Object obj)
return ARRAY_OBJ(copy_array(obj.data.array));
case kObjectTypeDictionary: {
- Dictionary rv = ARRAY_DICT_INIT;
- for (size_t i = 0; i < obj.data.dictionary.size; i++) {
- KeyValuePair item = obj.data.dictionary.items[i];
- PUT(rv, item.key.data, copy_object(item.value));
- }
- return DICTIONARY_OBJ(rv);
+ return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
}
default:
abort();
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 7a951d4e67..f587948cf0 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -990,6 +990,118 @@ Array nvim_get_api_info(uint64_t channel_id)
return rv;
}
+/// Identify the client for nvim. Can be called more than once, but subsequent
+/// calls will remove earlier info, which should be resent if it is still
+/// valid. (This could happen if a library first identifies the channel, and a
+/// plugin using that library later overrides that info)
+///
+/// @param name short name for the connected client
+/// @param version Dictionary describing the version, with the following
+/// possible keys (all optional)
+/// - "major" major version (defaults to 0 if not set, for no release yet)
+/// - "minor" minor version
+/// - "patch" patch number
+/// - "prerelease" string describing a prerelease, like "dev" or "beta1"
+/// - "commit" hash or similar identifier of commit
+/// @param type Must be one of the following values. A client library should
+/// use "remote" if the library user hasn't specified other value.
+/// - "remote" remote client that connected to nvim.
+/// - "ui" gui frontend
+/// - "embedder" application using nvim as a component, for instance
+/// IDE/editor implementing a vim mode.
+/// - "host" plugin host, typically started by nvim
+/// - "plugin" single plugin, started by nvim
+/// @param methods Builtin methods in the client. For a host, this does not
+/// include plugin methods which will be discovered later.
+/// The key should be the method name, the values are dicts with
+/// the following (optional) keys:
+/// - "async" if true, send as a notification. If false or unspecified,
+/// use a blocking request
+/// - "nargs" Number of arguments. Could be a single integer or an array
+/// two integers, minimum and maximum inclusive.
+/// Further keys might be added in later versions of nvim and unknown keys
+/// are thus ignored. Clients must only use keys defined in this or later
+/// versions of nvim!
+///
+/// @param attributes Informal attributes describing the client. Clients might
+/// define their own keys, but the following are suggested:
+/// - "website" Website of client (for instance github repository)
+/// - "license" Informal descripton of the license, such as "Apache 2",
+/// "GPLv3" or "MIT"
+/// - "logo" URI or path to image, preferably small logo or icon.
+/// .png or .svg format is preferred.
+///
+void nvim_set_client_info(uint64_t channel_id, String name,
+ Dictionary version, String type,
+ Dictionary methods, Dictionary attributes,
+ Error *err)
+ FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
+{
+ Dictionary info = ARRAY_DICT_INIT;
+ PUT(info, "name", copy_object(STRING_OBJ(name)));
+
+ version = copy_dictionary(version);
+ bool has_major = false;
+ for (size_t i = 0; i < version.size; i++) {
+ if (strequal(version.items[i].key.data, "major")) {
+ has_major = true;
+ break;
+ }
+ }
+ if (!has_major) {
+ PUT(version, "major", INTEGER_OBJ(0));
+ }
+ PUT(info, "version", DICTIONARY_OBJ(version));
+
+ PUT(info, "type", copy_object(STRING_OBJ(type)));
+ PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
+ PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
+
+ rpc_set_client_info(channel_id, info);
+}
+
+/// Get information about a channel.
+///
+/// @returns a Dictionary, describing a channel with the
+/// following keys:
+/// - "stream" the stream underlying the channel
+/// - "stdio" stdin and stdout of this Nvim instance
+/// - "stderr" stderr of this Nvim instance
+/// - "socket" TCP/IP socket or named pipe
+/// - "job" job with communication over its stdio
+/// - "mode" how data received on the channel is interpreted
+/// - "bytes" send and recieve raw bytes
+/// - "terminal" a |terminal| instance interprets ASCII sequences
+/// - "rpc" |RPC| communication on the channel is active
+/// - "pty" Name of pseudoterminal, if one is used (optional).
+/// On a POSIX system, this will be a device path like
+/// /dev/pts/1. Even if the name is unknown, the key will
+/// still be present to indicate a pty is used. This is
+/// currently the case when using winpty on windows.
+/// - "buffer" buffer with connected |terminal| instance (optional)
+/// - "client" information about the client on the other end of the
+/// RPC channel, if it has added it using
+/// |nvim_set_client_info|. (optional)
+///
+Dictionary nvim_get_chan_info(Integer chan, Error *err)
+ FUNC_API_SINCE(4)
+{
+ if (chan < 0) {
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+ return channel_info((uint64_t)chan);
+}
+
+/// Get information about all open channels.
+///
+/// @returns Array of Dictionaries, each describing a channel with
+/// the format specified at |nvim_get_chan_info|.
+Array nvim_list_chans(void)
+ FUNC_API_SINCE(4)
+{
+ return channel_all_info();
+}
+
/// Calls many API methods atomically.
///
/// This has two main usages:
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index f07a92ab87..d0a3f38c6b 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -19,6 +19,8 @@ return {
'BufWriteCmd', -- write buffer using command
'BufWritePost', -- after writing a buffer
'BufWritePre', -- before writing a buffer
+ 'ChanOpen', -- channel was opened
+ 'ChanInfo', -- info was received about channel
'CmdLineEnter', -- after entering cmdline mode
'CmdLineLeave', -- before leaving cmdline mode
'CmdUndefined', -- command undefined
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index b37fa10b12..64d743891b 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -1,11 +1,13 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
#include "nvim/channel.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/event/socket.h"
+#include "nvim/fileio.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/shell.h"
@@ -145,6 +147,9 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
return false;
}
break;
+
+ default:
+ abort();
}
return true;
@@ -180,47 +185,11 @@ static Channel *channel_alloc(ChannelStreamType type)
return chan;
}
-/// Not implemented, only logging for now
void channel_create_event(Channel *chan, const char *ext_source)
{
#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
- const char *stream_desc;
- const char *mode_desc;
const char *source;
- switch (chan->streamtype) {
- case kChannelStreamProc:
- if (chan->stream.proc.type == kProcessTypePty) {
- stream_desc = "pty job";
- } else {
- stream_desc = "job";
- }
- break;
-
- case kChannelStreamStdio:
- stream_desc = "stdio";
- break;
-
- case kChannelStreamSocket:
- stream_desc = "socket";
- break;
-
- case kChannelStreamInternal:
- stream_desc = "socket (internal)";
- break;
-
- default:
- stream_desc = "?";
- }
-
- if (chan->is_rpc) {
- mode_desc = ", rpc";
- } else if (chan->term) {
- mode_desc = ", terminal";
- } else {
- mode_desc = "";
- }
-
if (ext_source) {
// TODO(bfredl): in a future improved traceback solution,
// external events should be included.
@@ -230,12 +199,21 @@ void channel_create_event(Channel *chan, const char *ext_source)
source = (const char *)IObuff;
}
- ILOG("new channel %" PRIu64 " (%s%s): %s", chan->id, stream_desc,
- mode_desc, source);
+ Dictionary info = channel_info(chan->id);
+ typval_T tv = TV_INITIAL_VALUE;
+ // TODO(bfredl): do the conversion in one step. Also would be nice
+ // to pretty print top level dict in defined order
+ (void)object_to_vim(DICTIONARY_OBJ(info), &tv, NULL);
+ char *str = encode_tv2json(&tv, NULL);
+ ILOG("new channel %" PRIu64 " (%s) : %s", chan->id, source, str);
+ xfree(str);
+ api_free_dictionary(info);
+
#else
- (void)chan;
(void)ext_source;
#endif
+
+ channel_info_changed(chan, true);
}
void channel_incref(Channel *chan)
@@ -755,3 +733,95 @@ static void term_close(void *data)
multiqueue_put(chan->events, term_delayed_free, 1, data);
}
+void channel_info_changed(Channel *chan, bool new)
+{
+ event_T event = new ? EVENT_CHANOPEN : EVENT_CHANINFO;
+ if (has_event(event)) {
+ channel_incref(chan);
+ multiqueue_put(main_loop.events, set_info_event,
+ 2, chan, event);
+ }
+}
+
+static void set_info_event(void **argv)
+{
+ Channel *chan = argv[0];
+ event_T event = (event_T)(ptrdiff_t)argv[1];
+
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+ Dictionary info = channel_info(chan->id);
+ typval_T retval;
+ (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
+ tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict);
+
+ apply_autocmds(event, NULL, NULL, false, curbuf);
+
+ tv_dict_clear(dict);
+ api_free_dictionary(info);
+ channel_decref(chan);
+}
+
+Dictionary channel_info(uint64_t id)
+{
+ Channel *chan = find_channel(id);
+ if (!chan) {
+ return (Dictionary)ARRAY_DICT_INIT;
+ }
+
+ Dictionary info = ARRAY_DICT_INIT;
+ PUT(info, "id", INTEGER_OBJ((Integer)chan->id));
+
+ const char *stream_desc, *mode_desc;
+ switch (chan->streamtype) {
+ case kChannelStreamProc:
+ stream_desc = "job";
+ if (chan->stream.proc.type == kProcessTypePty) {
+ const char *name = pty_process_tty_name(&chan->stream.pty);
+ PUT(info, "pty", STRING_OBJ(cstr_to_string(name)));
+ }
+ break;
+
+ case kChannelStreamStdio:
+ stream_desc = "stdio";
+ break;
+
+ case kChannelStreamStderr:
+ stream_desc = "stderr";
+ break;
+
+ case kChannelStreamInternal:
+ PUT(info, "internal", BOOLEAN_OBJ(true));
+ // FALLTHROUGH
+
+ case kChannelStreamSocket:
+ stream_desc = "socket";
+ break;
+
+ default:
+ abort();
+ }
+ PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc)));
+
+ if (chan->is_rpc) {
+ mode_desc = "rpc";
+ PUT(info, "client", DICTIONARY_OBJ(rpc_client_info(chan)));
+ } else if (chan->term) {
+ mode_desc = "terminal";
+ PUT(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term)));
+ } else {
+ mode_desc = "bytes";
+ }
+ PUT(info, "mode", STRING_OBJ(cstr_to_string(mode_desc)));
+
+ return info;
+}
+
+Array channel_all_info(void)
+{
+ Channel *channel;
+ Array ret = ARRAY_DICT_INIT;
+ map_foreach_value(channels, channel, {
+ ADD(ret, DICTIONARY_OBJ(channel_info(channel->id)));
+ });
+ return ret;
+}
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 7d5f80c531..4f70bcc41a 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1018,7 +1018,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// needs a break here
if (wp->w_p_lbr
&& vim_isbreak(c)
- && !vim_isbreak(s[1])
+ && !vim_isbreak((int)s[1])
&& wp->w_p_wrap
&& (wp->w_width != 0)) {
// Count all characters from first non-blank after a blank up to next
@@ -1042,7 +1042,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
c = *s;
if (!(c != NUL
- && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) {
+ && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) {
break;
}
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index ad9cd4d562..7a95f4ab0c 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -2445,7 +2445,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level,
if (lvl < level) {
/* End of fold found, update the length when it got shorter. */
if (fp->fd_len != flp->lnum - fp->fd_top) {
- if (fp->fd_top + fp->fd_len > bot + 1) {
+ if (fp->fd_top + fp->fd_len - 1 > bot) {
/* fold continued below bot */
if (getlevel == foldlevelMarker
|| getlevel == foldlevelExpr
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 32781cf4d9..26b84b7cc7 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -61,6 +61,7 @@ void rpc_start(Channel *channel)
rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
rpc->subscribed_events = pmap_new(cstr_t)();
rpc->next_request_id = 1;
+ rpc->info = (Dictionary)ARRAY_DICT_INIT;
kv_init(rpc->call_stack);
if (channel->streamtype != kChannelStreamInternal) {
@@ -553,6 +554,7 @@ void rpc_free(Channel *channel)
pmap_free(cstr_t)(channel->rpc.subscribed_events);
kv_destroy(channel->rpc.call_stack);
+ api_free_dictionary(channel->rpc.info);
}
static bool is_rpc_response(msgpack_object *obj)
@@ -642,6 +644,23 @@ static WBuffer *serialize_response(uint64_t channel_id,
return rv;
}
+void rpc_set_client_info(uint64_t id, Dictionary info)
+{
+ Channel *chan = find_rpc_channel(id);
+ if (!chan) {
+ abort();
+ }
+
+ api_free_dictionary(chan->rpc.info);
+ chan->rpc.info = info;
+ channel_info_changed(chan, false);
+}
+
+Dictionary rpc_client_info(Channel *chan)
+{
+ return copy_dictionary(chan->rpc.info);
+}
+
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
#define REQ "[request] "
#define RES "[response] "
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index 6d8362e8b7..bfa7f7b87c 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -31,6 +31,7 @@ typedef struct {
msgpack_unpacker *unpacker;
uint64_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
+ Dictionary info;
} RpcState;
#endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H
diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c
index dfe2cfbb8d..71bb2d8a5e 100644
--- a/src/nvim/os/pty_process_unix.c
+++ b/src/nvim/os/pty_process_unix.c
@@ -115,6 +115,11 @@ error:
return status;
}
+const char *pty_process_tty_name(PtyProcess *ptyproc)
+{
+ return ptsname(ptyproc->tty_fd);
+}
+
void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height)
FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c
index a6774a6275..c5f8efadff 100644
--- a/src/nvim/os/pty_process_win.c
+++ b/src/nvim/os/pty_process_win.c
@@ -183,6 +183,11 @@ cleanup:
return status;
}
+const char *pty_process_tty_name(PtyProcess *ptyproc)
+{
+ return "?";
+}
+
void pty_process_resize(PtyProcess *ptyproc, uint16_t width,
uint16_t height)
FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index f15afa619f..4299002084 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -3454,7 +3454,8 @@ win_line (
}
// Found last space before word: check for line break.
- if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) {
+ if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
+ && !vim_isbreak((int)(*ptr))) {
int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0;
char_u *p = ptr - (mb_off + 1);
// TODO: is passing p for start of the line OK?
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index ba2727f0d7..d7bdf97c48 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2404,11 +2404,14 @@ jumpto_tag (
}
}
- /* If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
- * open a new tab page. */
+ // If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
+ // open a new tab page.
if (postponed_split || cmdmod.tab != 0) {
- (void)win_split(postponed_split > 0 ? postponed_split : 0,
- postponed_split_flags);
+ if (win_split(postponed_split > 0 ? postponed_split : 0,
+ postponed_split_flags) == FAIL) {
+ RedrawingDisabled--;
+ goto erret;
+ }
RESET_BINDING(curwin);
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 31875fac31..39cb2b6372 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -619,6 +619,11 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
}
}
+Buffer terminal_buf(const Terminal *term)
+{
+ return term->buf_handle;
+}
+
// }}}
// libvterm callbacks {{{
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index cd7a6cbc9c..c1ede08c31 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -89,7 +89,7 @@ NEW_TESTS ?= \
test_profile.res \
test_put.res \
test_quickfix.res \
- Test_quotestar.res \
+ test_quotestar.res \
test_recover.res \
test_retab.res \
test_scrollbind.res \
diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim
index 7c6d38d7ec..0c2ec08af3 100644
--- a/src/nvim/testdir/test_fold.vim
+++ b/src/nvim/testdir/test_fold.vim
@@ -9,8 +9,8 @@ func! Test_address_fold()
call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
\ 'after fold 1', 'after fold 2', 'after fold 3'])
setl fen fdm=marker
- " The next ccommands should all copy the same part of the buffer,
- " regardless of the adressing type, since the part to be copied
+ " The next commands should all copy the same part of the buffer,
+ " regardless of the addressing type, since the part to be copied
" is folded away
:1y
call assert_equal(['int FuncName() {/*{{{*/', '1', '2', '3', '4', '5', '}/*}}}*/'], getreg(0,1,1))
@@ -381,3 +381,56 @@ func Test_foldopen_exception()
endtry
call assert_match('E492:', a)
endfunc
+
+func Test_folddoopen_folddoclosed()
+ new
+ call setline(1, range(1, 9))
+ set foldmethod=manual
+ 1,3 fold
+ 6,8 fold
+
+ " Test without range.
+ folddoopen s/$/o/
+ folddoclosed s/$/c/
+ call assert_equal(['1c', '2c', '3c',
+ \ '4o', '5o',
+ \ '6c', '7c', '8c',
+ \ '9o'], getline(1, '$'))
+
+ " Test with range.
+ call setline(1, range(1, 9))
+ 1,8 folddoopen s/$/o/
+ 4,$ folddoclosed s/$/c/
+ call assert_equal(['1', '2', '3',
+ \ '4o', '5o',
+ \ '6c', '7c', '8c',
+ \ '9'], getline(1, '$'))
+
+ set foldmethod&
+ bw!
+endfunc
+
+func Test_fold_error()
+ new
+ call setline(1, [1, 2])
+
+ for fm in ['indent', 'expr', 'syntax', 'diff']
+ exe 'set foldmethod=' . fm
+ call assert_fails('norm zf', 'E350:')
+ call assert_fails('norm zd', 'E351:')
+ call assert_fails('norm zE', 'E352:')
+ endfor
+
+ set foldmethod=manual
+ call assert_fails('norm zd', 'E490:')
+ call assert_fails('norm zo', 'E490:')
+ call assert_fails('3fold', 'E16:')
+
+ set foldmethod=marker
+ set nomodifiable
+ call assert_fails('1,2fold', 'E21:')
+
+ set modifiable&
+ set foldmethod&
+ bw!
+endfunc
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 71fa6632c7..1e910b6aa7 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -766,6 +766,115 @@ describe('api', function()
end)
end)
+ describe('nvim_list_chans and nvim_get_chan_info', function()
+ before_each(function()
+ command('autocmd ChanOpen * let g:opened_event = copy(v:event)')
+ command('autocmd ChanInfo * let g:info_event = copy(v:event)')
+ end)
+ local testinfo = {
+ stream = 'stdio',
+ id = 1,
+ mode = 'rpc',
+ client = {},
+ }
+ local stderr = {
+ stream = 'stderr',
+ id = 2,
+ mode = 'bytes',
+ }
+
+ it('returns {} for invalid channel', function()
+ eq({}, meths.get_chan_info(0))
+ eq({}, meths.get_chan_info(-1))
+ -- more preallocated numbers might be added, try something high
+ eq({}, meths.get_chan_info(10))
+ end)
+
+ it('works for stdio channel', function()
+ eq({[1]=testinfo,[2]=stderr}, meths.list_chans())
+ eq(testinfo, meths.get_chan_info(1))
+ eq(stderr, meths.get_chan_info(2))
+
+ meths.set_client_info("functionaltests",
+ {major=0, minor=3, patch=17},
+ 'ui',
+ {do_stuff={n_args={2,3}}},
+ {license= 'Apache2'})
+ local info = {
+ stream = 'stdio',
+ id = 1,
+ mode = 'rpc',
+ client = {
+ name='functionaltests',
+ version={major=0, minor=3, patch=17},
+ type='ui',
+ methods={do_stuff={n_args={2,3}}},
+ attributes={license='Apache2'},
+ },
+ }
+ eq({info=info}, meths.get_var("info_event"))
+ eq({[1]=info, [2]=stderr}, meths.list_chans())
+ eq(info, meths.get_chan_info(1))
+ end)
+
+ it('works for job channel', function()
+ if iswin() and os.getenv('APPVEYOR') ~= nil then
+ pending("jobstart(['cat']) unreliable on appveyor")
+ return
+ end
+ eq(3, eval("jobstart(['cat'], {'rpc': v:true})"))
+ local info = {
+ stream='job',
+ id=3,
+ mode='rpc',
+ client={},
+ }
+ eq({info=info}, meths.get_var("opened_event"))
+ eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
+ eq(info, meths.get_chan_info(3))
+ eval('rpcrequest(3, "nvim_set_client_info", "cat", {}, "remote",'..
+ '{"nvim_command":{"n_args":1}},'.. -- and so on
+ '{"description":"The Amazing Cat"})')
+ info = {
+ stream='job',
+ id=3,
+ mode='rpc',
+ client = {
+ name='cat',
+ version={major=0},
+ type='remote',
+ methods={nvim_command={n_args=1}},
+ attributes={description="The Amazing Cat"},
+ },
+ }
+ eq({info=info}, meths.get_var("info_event"))
+ eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
+ end)
+
+ it('works for :terminal channel', function()
+ command(":terminal")
+ eq({id=1}, meths.get_current_buf())
+ eq(3, meths.buf_get_option(1, "channel"))
+
+ local info = {
+ stream='job',
+ id=3,
+ mode='terminal',
+ buffer = 1,
+ pty='?',
+ }
+ local event = meths.get_var("opened_event")
+ if not iswin() then
+ info.pty = event.info.pty
+ neq(nil, string.match(info.pty, "^/dev/"))
+ end
+ eq({info=info}, event)
+ info.buffer = {id=1}
+ eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans())
+ eq(info, meths.get_chan_info(3))
+ end)
+ end)
+
describe('nvim_call_atomic', function()
it('works', function()
meths.buf_set_lines(0, 0, -1, true, {'first'})
diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua
index bcf83698bb..3f54ff6f41 100644
--- a/test/functional/ex_cmds/write_spec.lua
+++ b/test/functional/ex_cmds/write_spec.lua
@@ -9,6 +9,7 @@ local command = helpers.command
local feed_command = helpers.feed_command
local funcs = helpers.funcs
local meths = helpers.meths
+local iswin = helpers.iswin
local fname = 'Xtest-functional-ex_cmds-write'
local fname_bak = fname .. '~'
@@ -34,11 +35,14 @@ describe(':write', function()
it('&backupcopy=auto preserves symlinks', function()
command('set backupcopy=auto')
write_file('test_bkc_file.txt', 'content0')
- if helpers.iswin() then
+ if iswin() then
command("silent !mklink test_bkc_link.txt test_bkc_file.txt")
else
command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
end
+ if eval('v:shell_error') ~= 0 then
+ pending('Cannot create symlink', function()end)
+ end
source([[
edit test_bkc_link.txt
call setline(1, ['content1'])
@@ -51,11 +55,14 @@ describe(':write', function()
it('&backupcopy=no replaces symlink with new file', function()
command('set backupcopy=no')
write_file('test_bkc_file.txt', 'content0')
- if helpers.iswin() then
+ if iswin() then
command("silent !mklink test_bkc_link.txt test_bkc_file.txt")
else
command("silent !ln -s test_bkc_file.txt test_bkc_link.txt")
end
+ if eval('v:shell_error') ~= 0 then
+ pending('Cannot create symlink', function()end)
+ end
source([[
edit test_bkc_link.txt
call setline(1, ['content1'])
@@ -66,7 +73,8 @@ describe(':write', function()
end)
it("appends FIFO file", function()
- if eval("executable('mkfifo')") == 0 then
+ -- mkfifo creates read-only .lnk files on Windows
+ if iswin() or eval("executable('mkfifo')") == 0 then
pending('missing "mkfifo" command', function()end)
return
end
@@ -88,7 +96,7 @@ describe(':write', function()
command('let $HOME=""')
eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~'))
-- Message from check_overwrite
- if not helpers.iswin() then
+ if not iswin() then
eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'),
redir_exec('write .'))
end
@@ -108,7 +116,7 @@ describe(':write', function()
funcs.setfperm(fname, 'r--------')
eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)',
exc_exec('write'))
- if helpers.iswin() then
+ if iswin() then
eq(0, os.execute('del /q/f ' .. fname))
eq(0, os.execute('rd /q/s ' .. fname_bak))
else
@@ -117,7 +125,7 @@ describe(':write', function()
end
write_file(fname_bak, 'TTYX')
-- FIXME: exc_exec('write!') outputs 0 in Windows
- if helpers.iswin() then return end
+ if iswin() then return end
lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true)
eq('Vim(write):E166: Can\'t open linked file for writing',
exc_exec('write!'))
diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua
index eebbd70f2b..df0b9df5dd 100644
--- a/test/functional/ex_cmds/wviminfo_spec.lua
+++ b/test/functional/ex_cmds/wviminfo_spec.lua
@@ -3,21 +3,21 @@ local lfs = require('lfs')
local command, eq, neq, spawn, nvim_prog, set_session, write_file =
helpers.command, helpers.eq, helpers.neq, helpers.spawn,
helpers.nvim_prog, helpers.set_session, helpers.write_file
+local iswin = helpers.iswin
+local read_file = helpers.read_file
describe(':wshada', function()
local shada_file = 'wshada_test'
local session
before_each(function()
- if session then
- session:close()
- end
-
-- Override the default session because we need 'swapfile' for these tests.
- session = spawn({nvim_prog, '-u', 'NONE', '-i', '/dev/null', '--embed',
+ session = spawn({nvim_prog, '-u', 'NONE', '-i', iswin() and 'nul' or '/dev/null', '--embed',
'--cmd', 'set swapfile'})
set_session(session)
-
+ end)
+ after_each(function ()
+ session:close()
os.remove(shada_file)
end)
@@ -36,7 +36,7 @@ describe(':wshada', function()
write_file(shada_file, text)
-- sanity check
- eq(text, io.open(shada_file):read())
+ eq(text, read_file(shada_file))
neq(nil, lfs.attributes(shada_file))
command('wsh! '..shada_file)
@@ -49,8 +49,4 @@ describe(':wshada', function()
assert(char1:byte() == 0x01,
shada_file..' should be a shada file')
end)
-
- teardown(function()
- os.remove(shada_file)
- end)
end)
diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua
index c2667d28d2..379646b2ba 100644
--- a/test/functional/legacy/011_autocommands_spec.lua
+++ b/test/functional/legacy/011_autocommands_spec.lua
@@ -17,9 +17,10 @@ local lfs = require('lfs')
local clear, feed_command, expect, eq, neq, dedent, write_file, feed =
helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq,
helpers.dedent, helpers.write_file, helpers.feed
+local iswin = helpers.iswin
local function has_gzip()
- local null = helpers.iswin() and 'nul' or '/dev/null'
+ local null = iswin() and 'nul' or '/dev/null'
return os.execute('gzip --help >' .. null .. ' 2>&1') == 0
end
@@ -59,7 +60,7 @@ describe('file reading, writing and bufnew and filter autocommands', function()
os.remove('test.out')
end)
- if not has_gzip() then
+ if iswin() or not has_gzip() then
pending('skipped (missing `gzip` utility)', function() end)
else
diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua
index 907f0665ae..ccd93fed60 100644
--- a/test/functional/legacy/097_glob_path_spec.lua
+++ b/test/functional/legacy/097_glob_path_spec.lua
@@ -74,7 +74,7 @@ describe('glob() and globpath()', function()
teardown(function()
if helpers.iswin() then
os.execute('del /q/f Xxx{ Xxx$')
- os.execute('rd /q sautest')
+ os.execute('rd /q /s sautest')
else
os.execute("rm -rf sautest Xxx{ Xxx$")
end
diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua
index 5ef456bfe3..9ea3269828 100644
--- a/test/functional/legacy/delete_spec.lua
+++ b/test/functional/legacy/delete_spec.lua
@@ -4,6 +4,9 @@ local eq, eval, command = helpers.eq, helpers.eval, helpers.command
describe('Test for delete()', function()
before_each(clear)
+ after_each(function()
+ os.remove('Xfile')
+ end)
it('file delete', function()
command('split Xfile')
@@ -52,6 +55,9 @@ describe('Test for delete()', function()
silent !ln -s Xfile Xlink
endif
]])
+ if eval('v:shell_error') ~= 0 then
+ pending('Cannot create symlink', function()end)
+ end
-- Delete the link, not the file
eq(0, eval("delete('Xlink')"))
eq(-1, eval("delete('Xlink')"))
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 1fc045e0f7..eaeffa2449 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -222,7 +222,8 @@ if(WIN32)
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${DEPS_INSTALL_DIR}/bin)
GetBinaryDep(TARGET wingui
- INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_INSTALL_DIR}/bin)
+ INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_INSTALL_DIR}/bin
+ COMMAND ${CMAKE_COMMAND} -E copy_directory share ${DEPS_INSTALL_DIR}/share)
include(TargetArch)
GetBinaryDep(TARGET "win32yank_${TARGET_ARCH}"