Merge pull request #6743 from bfredl/channel_info
Add methods to enumerate channels and clients to identify themselves
This commit is contained in:
commit
418abfc9d0
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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] "
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {{{
|
||||
|
||||
|
|
|
@ -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'})
|
||||
|
|
Loading…
Reference in New Issue