aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglacambre <code@lacamb.re>2021-10-03 16:19:25 +0200
committerglacambre <code@lacamb.re>2022-01-24 13:59:16 +0100
commita4069a3eed65f14b1149c6cda8638dcb49ab5027 (patch)
treedc0f0c1af6f8a9070340745c8f939b8a7d875d82
parent326e74571be43823ded9fa805a3173bdabda6bec (diff)
downloadrneovim-a4069a3eed65f14b1149c6cda8638dcb49ab5027.tar.gz
rneovim-a4069a3eed65f14b1149c6cda8638dcb49ab5027.tar.bz2
rneovim-a4069a3eed65f14b1149c6cda8638dcb49ab5027.zip
feat(--headless): add on_print callback to stdioopen
This commit adds an on_print callback to stdioopen's dictionary argument which lets the caller specify a function called each time neovim will try to output something to stdout (e.g. on "echo" or "echoerr" in --headless mode).
-rw-r--r--src/nvim/channel.h2
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.h3
-rw-r--r--src/nvim/message.c11
-rw-r--r--test/functional/core/channels_spec.lua32
5 files changed, 51 insertions, 1 deletions
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index 81b75e2d31..50d6b3600a 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -96,6 +96,8 @@ struct Channel {
EXTERN PMap(uint64_t) channels INIT(= MAP_INIT);
+EXTERN Callback on_print INIT(= CALLBACK_INIT);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "channel.h.generated.h"
#endif
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 5252c940f7..6d43097ddd 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -10213,6 +10213,10 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) {
return;
}
+ if (!tv_dict_get_callback(opts, S_LEN("on_print"), &on_print)) {
+ return;
+ }
+
on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered");
if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) {
on_stdin.self = opts;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index d1275d6512..ad01c01499 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -81,7 +81,8 @@ typedef struct {
} data;
CallbackType type;
} Callback;
-#define CALLBACK_NONE ((Callback){ .type = kCallbackNone })
+#define CALLBACK_INIT { .type = kCallbackNone }
+#define CALLBACK_NONE ((Callback)CALLBACK_INIT)
/// Structure holding dictionary watcher
typedef struct dict_watcher {
diff --git a/src/nvim/message.c b/src/nvim/message.c
index befca8c76b..76aa863bde 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2647,6 +2647,17 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
char buf[7];
char *p;
+ if (on_print.type != kCallbackNone) {
+ typval_T argv[1];
+ argv[0].v_type = VAR_STRING;
+ argv[0].v_lock = VAR_UNLOCKED;
+ argv[0].vval.v_string = (char_u *)str;
+ typval_T rettv = TV_INITIAL_VALUE;
+ callback_call(&on_print, 1, argv, &rettv);
+ tv_clear(&rettv);
+ return;
+ }
+
while ((maxlen < 0 || s - str < maxlen) && *s != NUL) {
int len = utf_ptr2len((const char_u *)s);
if (!(silent_mode && p_verbose == 0)) {
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index 93dec9fb35..c28300f0f4 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -100,6 +100,38 @@ describe('channels', function()
eq({"notification", "exit", {3,0}}, next_msg())
end)
+ it('can use stdio channel and on_print callback', function()
+ source([[
+ let g:job_opts = {
+ \ 'on_stdout': function('OnEvent'),
+ \ 'on_stderr': function('OnEvent'),
+ \ 'on_exit': function('OnEvent'),
+ \ }
+ ]])
+ meths.set_var("nvim_prog", nvim_prog)
+ meths.set_var("code", [[
+ function! OnStdin(id, data, event) dict
+ echo string([a:id, a:data, a:event])
+ if a:data == ['']
+ quit
+ endif
+ endfunction
+ function! OnPrint(text) dict
+ call chansend(g:x, ['OnPrint:' .. a:text])
+ endfunction
+ let g:x = stdioopen({'on_stdin': funcref('OnStdin'), 'on_print':'OnPrint'})
+ call chansend(x, "hello")
+ ]])
+ command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)")
+ local id = eval("g:id")
+ ok(id > 0)
+
+ eq({ "notification", "stdout", {id, { "hello" } } }, next_msg())
+
+ command("call chansend(id, 'howdy')")
+ eq({"notification", "stdout", {id, {"OnPrint:[1, ['howdy'], 'stdin']"}}}, next_msg())
+ end)
+
local function expect_twoline(id, stream, line1, line2, nobr)
local msg = next_msg()
local joined = nobr and {line1..line2} or {line1, line2}