mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat(nvim_open_term): support input callback in lua
This commit is contained in:
parent
9086938f7b
commit
9e41e82481
@ -1248,10 +1248,16 @@ fail:
|
|||||||
/// in a virtual terminal having the intended size.
|
/// in a virtual terminal having the intended size.
|
||||||
///
|
///
|
||||||
/// @param buffer the buffer to use (expected to be empty)
|
/// @param buffer the buffer to use (expected to be empty)
|
||||||
/// @param opts Optional parameters. Reserved for future use.
|
/// @param opts Optional parameters.
|
||||||
|
/// - on_input: lua callback for input sent, i e keypresses in terminal
|
||||||
|
/// mode. Note: keypresses are sent raw as they would be to the pty
|
||||||
|
/// master end. For instance, a carriage return is sent
|
||||||
|
/// as a "\r", not as a "\n". |textlock| applies. It is possible
|
||||||
|
/// to call |nvim_chan_send| directly in the callback however.
|
||||||
|
/// ["input", term, bufnr, data]
|
||||||
/// @param[out] err Error details, if any
|
/// @param[out] err Error details, if any
|
||||||
/// @return Channel id, or 0 on error
|
/// @return Channel id, or 0 on error
|
||||||
Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
|
Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
|
||||||
FUNC_API_SINCE(7)
|
FUNC_API_SINCE(7)
|
||||||
{
|
{
|
||||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
@ -1259,13 +1265,27 @@ Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.size > 0) {
|
LuaRef cb = LUA_NOREF;
|
||||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
for (size_t i = 0; i < opts.size; i++) {
|
||||||
return 0;
|
String k = opts.items[i].key;
|
||||||
|
Object *v = &opts.items[i].value;
|
||||||
|
if (strequal("on_input", k.data)) {
|
||||||
|
if (v->type != kObjectTypeLuaRef) {
|
||||||
|
api_set_error(err, kErrorTypeValidation,
|
||||||
|
"%s is not a function", "on_input");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
cb = v->data.luaref;
|
||||||
|
v->data.luaref = LUA_NOREF;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalOptions topts;
|
TerminalOptions topts;
|
||||||
Channel *chan = channel_alloc(kChannelStreamInternal);
|
Channel *chan = channel_alloc(kChannelStreamInternal);
|
||||||
|
chan->stream.internal.cb = cb;
|
||||||
topts.data = chan;
|
topts.data = chan;
|
||||||
// NB: overridden in terminal_check_size if a window is already
|
// NB: overridden in terminal_check_size if a window is already
|
||||||
// displaying the buffer
|
// displaying the buffer
|
||||||
@ -1283,7 +1303,18 @@ Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
|
|||||||
|
|
||||||
static void term_write(char *buf, size_t size, void *data)
|
static void term_write(char *buf, size_t size, void *data)
|
||||||
{
|
{
|
||||||
// TODO(bfredl): lua callback
|
Channel *chan = data;
|
||||||
|
LuaRef cb = chan->stream.internal.cb;
|
||||||
|
if (cb == LUA_NOREF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FIXED_TEMP_ARRAY(args, 3);
|
||||||
|
args.items[0] = INTEGER_OBJ((Integer)chan->id);
|
||||||
|
args.items[1] = BUFFER_OBJ(terminal_buf(chan->term));
|
||||||
|
args.items[2] = STRING_OBJ(((String){ .data = buf, .size = size }));
|
||||||
|
textlock++;
|
||||||
|
nlua_call_ref(cb, "input", args, false, NULL);
|
||||||
|
textlock--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void term_resize(uint16_t width, uint16_t height, void *data)
|
static void term_resize(uint16_t width, uint16_t height, void *data)
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "nvim/eval/encode.h"
|
#include "nvim/eval/encode.h"
|
||||||
#include "nvim/event/socket.h"
|
#include "nvim/event/socket.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
|
#include "nvim/lua/executor.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
#include "nvim/msgpack_rpc/server.h"
|
#include "nvim/msgpack_rpc/server.h"
|
||||||
#include "nvim/os/shell.h"
|
#include "nvim/os/shell.h"
|
||||||
@ -136,6 +137,8 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
|
|||||||
*error = (const char *)e_invstream;
|
*error = (const char *)e_invstream;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
api_free_luaref(chan->stream.internal.cb);
|
||||||
|
chan->stream.internal.cb = LUA_NOREF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -420,6 +423,7 @@ uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader
|
|||||||
// Create a loopback channel. This avoids deadlock if nvim connects to
|
// Create a loopback channel. This avoids deadlock if nvim connects to
|
||||||
// its own named pipe.
|
// its own named pipe.
|
||||||
channel = channel_alloc(kChannelStreamInternal);
|
channel = channel_alloc(kChannelStreamInternal);
|
||||||
|
channel->stream.internal.cb = LUA_NOREF;
|
||||||
rpc_start(channel);
|
rpc_start(channel);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,10 @@ typedef struct {
|
|||||||
bool closed;
|
bool closed;
|
||||||
} StderrState;
|
} StderrState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LuaRef cb;
|
||||||
|
} InternalState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Callback cb;
|
Callback cb;
|
||||||
dict_T *self;
|
dict_T *self;
|
||||||
@ -74,6 +78,7 @@ struct Channel {
|
|||||||
Stream socket;
|
Stream socket;
|
||||||
StdioPair stdio;
|
StdioPair stdio;
|
||||||
StderrState err;
|
StderrState err;
|
||||||
|
InternalState internal;
|
||||||
} stream;
|
} stream;
|
||||||
|
|
||||||
bool is_rpc;
|
bool is_rpc;
|
||||||
|
@ -22,6 +22,7 @@ local source = helpers.source
|
|||||||
local next_msg = helpers.next_msg
|
local next_msg = helpers.next_msg
|
||||||
local tmpname = helpers.tmpname
|
local tmpname = helpers.tmpname
|
||||||
local write_file = helpers.write_file
|
local write_file = helpers.write_file
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
|
||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
local format_string = helpers.format_string
|
local format_string = helpers.format_string
|
||||||
@ -2264,6 +2265,9 @@ describe('API', function()
|
|||||||
[2] = {background = tonumber('0xffff40'), bg_indexed = true};
|
[2] = {background = tonumber('0xffff40'), bg_indexed = true};
|
||||||
[3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
|
[3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
|
||||||
[4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
|
[4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
|
||||||
|
[5] = {foreground = Screen.colors.Blue, background = Screen.colors.LightMagenta, bold = true};
|
||||||
|
[6] = {bold = true};
|
||||||
|
[7] = {reverse = true, background = Screen.colors.LightMagenta};
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -2311,6 +2315,74 @@ describe('API', function()
|
|||||||
|
|
|
|
||||||
]]}
|
]]}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('can handle input', function()
|
||||||
|
screen:try_resize(50, 10)
|
||||||
|
eq({3, 2}, exec_lua [[
|
||||||
|
buf = vim.api.nvim_create_buf(1,1)
|
||||||
|
|
||||||
|
stream = ''
|
||||||
|
do_the_echo = false
|
||||||
|
function input(_,t1,b1,data)
|
||||||
|
stream = stream .. data
|
||||||
|
_G.vals = {t1, b1}
|
||||||
|
if do_the_echo then
|
||||||
|
vim.api.nvim_chan_send(t1, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
term = vim.api.nvim_open_term(buf, {on_input=input})
|
||||||
|
vim.api.nvim_open_win(buf, true, {width=40, height=5, row=1, col=1, relative='editor'})
|
||||||
|
return {term, buf}
|
||||||
|
]])
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{0:~}{1:^ }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
feed 'iba<c-x>bla'
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{0:~}{7: }{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
{6:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
eq('ba\024bla', exec_lua [[ return stream ]])
|
||||||
|
eq({3,2}, exec_lua [[ return vals ]])
|
||||||
|
|
||||||
|
exec_lua [[ do_the_echo = true ]]
|
||||||
|
feed 'herrejösses!'
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{0:~}{1:herrejösses!}{7: }{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
{6:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
eq('ba\024blaherrejösses!', exec_lua [[ return stream ]])
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('nvim_del_mark', function()
|
describe('nvim_del_mark', function()
|
||||||
|
Loading…
Reference in New Issue
Block a user