mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
Merge pull request #9024 from bfredl/embed_ui2
always wait for UI with --embed, unless --headless is supplied
This commit is contained in:
commit
c236e80cf3
@ -348,30 +348,35 @@ argument.
|
||||
|
||||
*--embed*
|
||||
--embed Use stdin/stdout as a msgpack-RPC channel, so applications can
|
||||
embed and control Nvim via the |rpc-api|. Implies |--headless|.
|
||||
embed and control Nvim via the |rpc-api|.
|
||||
|
||||
Nvim will wait for a single request before sourcing startup
|
||||
files and reading buffers. This is mainly so that UIs can call
|
||||
`nvim_ui_attach` so that the UI can show startup messages
|
||||
and possible swap file dialog for the first loaded file. In
|
||||
addition, a `nvim_get_api_info` call before the `nvim_ui_attach`
|
||||
call is also allowed, so that UI features can be safely
|
||||
detected by the UI.
|
||||
By default nvim will wait for the embedding process to call
|
||||
`nvim_ui_attach` before sourcing startup files and reading
|
||||
buffers. This is so that UI can show startup messages and
|
||||
possible swap file dialog for the first loaded file. The
|
||||
process can do requests before the `nvim_ui_attach`, for
|
||||
instance a `nvim_get_api_info` call so that UI features can be
|
||||
safely detected by the UI before attaching.
|
||||
|
||||
To avoid this behavior, this alterative could be used instead: >
|
||||
To embed nvim without using the UI protocol, `--headless` should
|
||||
be supplied together with `--embed`. Then initialization is
|
||||
performed without waiting for an UI. This is also equivalent
|
||||
to the following alternative: >
|
||||
nvim --headless --cmd "call stdioopen({'rpc': v:true})"
|
||||
<
|
||||
See also |channel-stdio|.
|
||||
|
||||
*--headless*
|
||||
--headless Do not start the default UI, so stdio can be used as an
|
||||
arbitrary communication channel. |channel-stdio|
|
||||
--headless Start nvim without an UI. The TUI is not used, so stdio
|
||||
can be used as an arbitrary communication channel.
|
||||
|channel-stdio| When used together with `--embed`, do not wait
|
||||
for the embedder to attach an UI.
|
||||
|
||||
Also useful for scripting (tests) to see messages that would
|
||||
not be printed by |-es|.
|
||||
|
||||
To detect if a UI is available, check if |nvim_list_uis()| is
|
||||
empty after |VimEnter|.
|
||||
empty in or after |VimEnter|.
|
||||
|
||||
To read stdin as text, "-" must be given explicitly:
|
||||
--headless cannot assume that stdin is just text. >
|
||||
|
@ -58,6 +58,21 @@ void remote_ui_disconnect(uint64_t channel_id)
|
||||
xfree(ui);
|
||||
}
|
||||
|
||||
/// Wait until ui has connected on stdio channel.
|
||||
void remote_ui_wait_for_attach(void)
|
||||
FUNC_API_NOEXPORT
|
||||
{
|
||||
Channel *channel = find_channel(CHAN_STDIO);
|
||||
if (!channel) {
|
||||
// this function should only be called in --embed mode, stdio channel
|
||||
// can be assumed.
|
||||
abort();
|
||||
}
|
||||
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1,
|
||||
pmap_has(uint64_t)(connected_uis, CHAN_STDIO));
|
||||
}
|
||||
|
||||
void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
|
||||
Dictionary options, Error *err)
|
||||
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
|
||||
|
@ -432,7 +432,7 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
|
||||
const char **error)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
if (!headless_mode) {
|
||||
if (!headless_mode && !embedded_mode) {
|
||||
*error = _("can only be opened in headless mode");
|
||||
return 0;
|
||||
}
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
#include "nvim/msgpack_rpc/server.h"
|
||||
#include "nvim/msgpack_rpc/channel.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/handle.h"
|
||||
@ -304,6 +305,7 @@ int main(int argc, char **argv)
|
||||
// Read ex-commands if invoked with "-es".
|
||||
//
|
||||
bool reading_tty = !headless_mode
|
||||
&& !embedded_mode
|
||||
&& !silent_mode
|
||||
&& (params.input_isatty || params.output_isatty
|
||||
|| params.err_isatty);
|
||||
@ -348,18 +350,16 @@ int main(int argc, char **argv)
|
||||
// startup. This allows an external UI to show messages and prompts from
|
||||
// --cmd and buffer loading (e.g. swap files)
|
||||
bool early_ui = false;
|
||||
if (embedded_mode) {
|
||||
if (embedded_mode && !headless_mode) {
|
||||
TIME_MSG("waiting for embedder to make request");
|
||||
rpc_wait_for_request();
|
||||
remote_ui_wait_for_attach();
|
||||
TIME_MSG("done waiting for embedder");
|
||||
|
||||
if (ui_active()) {
|
||||
// prepare screen now, so external UIs can display messages
|
||||
starting = NO_BUFFERS;
|
||||
screenclear();
|
||||
early_ui = true;
|
||||
TIME_MSG("initialized screen early for embedder");
|
||||
}
|
||||
// prepare screen now, so external UIs can display messages
|
||||
starting = NO_BUFFERS;
|
||||
screenclear();
|
||||
early_ui = true;
|
||||
TIME_MSG("initialized screen early for embedder");
|
||||
}
|
||||
|
||||
// Execute --cmd arguments.
|
||||
@ -467,7 +467,7 @@ int main(int argc, char **argv)
|
||||
wait_return(true);
|
||||
}
|
||||
|
||||
if (!headless_mode && !silent_mode) {
|
||||
if (!headless_mode && !embedded_mode && !silent_mode) {
|
||||
input_stop(); // Stop reading input, let the UI take over.
|
||||
ui_builtin_start();
|
||||
}
|
||||
@ -848,7 +848,6 @@ static void command_line_scan(mparm_T *parmp)
|
||||
headless_mode = true;
|
||||
} else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
|
||||
embedded_mode = true;
|
||||
headless_mode = true;
|
||||
const char *err;
|
||||
if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
|
||||
abort();
|
||||
|
@ -40,8 +40,6 @@
|
||||
static PMap(cstr_t) *event_strings = NULL;
|
||||
static msgpack_sbuffer out_buffer;
|
||||
|
||||
static bool got_stdio_request = false;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "msgpack_rpc/channel.c.generated.h"
|
||||
#endif
|
||||
@ -331,9 +329,6 @@ static void handle_request(Channel *channel, msgpack_object *request)
|
||||
send_error(channel, request_id, error.msg);
|
||||
api_clear_error(&error);
|
||||
api_free_array(args);
|
||||
if (channel->id == CHAN_STDIO) {
|
||||
got_stdio_request = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -349,9 +344,6 @@ static void handle_request(Channel *channel, msgpack_object *request)
|
||||
if (is_get_mode && !input_blocking()) {
|
||||
// Defer the event to a special queue used by os/input.c. #6247
|
||||
multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
|
||||
if (channel->id == CHAN_STDIO) {
|
||||
got_stdio_request = true;
|
||||
}
|
||||
} else {
|
||||
// Invoke immediately.
|
||||
on_request_event((void **)&evdata);
|
||||
@ -387,11 +379,6 @@ static void on_request_event(void **argv)
|
||||
channel_decref(channel);
|
||||
xfree(e);
|
||||
api_clear_error(&error);
|
||||
bool is_api_info = handler.fn == handle_nvim_get_api_info;
|
||||
// api info is used to initiate client library, ignore it
|
||||
if (channel->id == CHAN_STDIO && !is_api_info) {
|
||||
got_stdio_request = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool channel_write(Channel *channel, WBuffer *buffer)
|
||||
@ -757,17 +744,3 @@ static void log_msg_close(FILE *f, msgpack_object msg)
|
||||
log_unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Wait until embedder has done a request
|
||||
void rpc_wait_for_request(void)
|
||||
{
|
||||
Channel *channel = find_rpc_channel(CHAN_STDIO);
|
||||
if (!channel) {
|
||||
// this function should only be called in --embed mode, stdio channel
|
||||
// can be assumed.
|
||||
abort();
|
||||
}
|
||||
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, got_stdio_request);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.fu
|
||||
local source, next_msg = helpers.source, helpers.next_msg
|
||||
local ok = helpers.ok
|
||||
local meths = helpers.meths
|
||||
local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv
|
||||
local spawn, merge_args = helpers.spawn, helpers.merge_args
|
||||
local set_session = helpers.set_session
|
||||
local expect_err = helpers.expect_err
|
||||
|
||||
@ -23,7 +23,7 @@ describe('server -> client', function()
|
||||
|
||||
it('handles unexpected closed stream while preparing RPC response', function()
|
||||
source([[
|
||||
let g:_nvim_args = [v:progpath, '--embed', '-n', '-u', 'NONE', '-i', 'NONE', ]
|
||||
let g:_nvim_args = [v:progpath, '--embed', '--headless', '-n', '-u', 'NONE', '-i', 'NONE', ]
|
||||
let ch1 = jobstart(g:_nvim_args, {'rpc': v:true})
|
||||
let child1_ch = rpcrequest(ch1, "nvim_get_api_info")[0]
|
||||
call rpcnotify(ch1, 'nvim_eval', 'rpcrequest('.child1_ch.', "nvim_get_api_info")')
|
||||
@ -189,7 +189,7 @@ describe('server -> client', function()
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
command("let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed'])")
|
||||
command("let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed', '--headless'])")
|
||||
neq(0, eval('vim'))
|
||||
end)
|
||||
|
||||
@ -268,6 +268,7 @@ describe('server -> client', function()
|
||||
end)
|
||||
|
||||
describe('connecting to another (peer) nvim', function()
|
||||
local nvim_argv = merge_args(helpers.nvim_argv, {'--headless'})
|
||||
local function connect_test(server, mode, address)
|
||||
local serverpid = funcs.getpid()
|
||||
local client = spawn(nvim_argv)
|
||||
|
@ -9,7 +9,7 @@ local eval = helpers.eval
|
||||
local shada_file = 'Xtest.shada'
|
||||
|
||||
local function _clear()
|
||||
set_session(spawn({nvim_prog, '--embed', '-u', 'NONE',
|
||||
set_session(spawn({nvim_prog, '--embed', '--headless', '-u', 'NONE',
|
||||
-- Need shada for these tests.
|
||||
'-i', shada_file,
|
||||
'--cmd', 'set noswapfile undodir=. directory=. viewdir=. backupdir=. belloff= noshowcmd noruler'}))
|
||||
|
@ -330,6 +330,7 @@ local function clear(...)
|
||||
local new_args
|
||||
local env = nil
|
||||
local opts = select(1, ...)
|
||||
local headless = true
|
||||
if type(opts) == 'table' then
|
||||
if opts.env then
|
||||
local env_tbl = {}
|
||||
@ -355,15 +356,19 @@ local function clear(...)
|
||||
end
|
||||
end
|
||||
new_args = opts.args or {}
|
||||
if opts.headless == false then
|
||||
headless = false
|
||||
end
|
||||
else
|
||||
new_args = {...}
|
||||
end
|
||||
if headless then
|
||||
table.insert(args, '--headless')
|
||||
end
|
||||
for _, arg in ipairs(new_args) do
|
||||
table.insert(args, arg)
|
||||
end
|
||||
set_session(spawn(args, nil, env))
|
||||
-- Dummy request so that --embed continues past UI initialization
|
||||
session:request('nvim_eval', "0")
|
||||
end
|
||||
|
||||
local function insert(...)
|
||||
|
@ -3,7 +3,6 @@ local paths = require('test.config.paths')
|
||||
local helpers = require('test.functional.helpers')(nil)
|
||||
local spawn, set_session, nvim_prog, merge_args =
|
||||
helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args
|
||||
local request = helpers.request
|
||||
|
||||
local additional_cmd = ''
|
||||
|
||||
@ -14,7 +13,7 @@ local function nvim_argv(shada_file)
|
||||
'--cmd', 'set shortmess+=I background=light noswapfile belloff= noshowcmd noruler',
|
||||
'--cmd', 'let &runtimepath=' .. rtp_value,
|
||||
'--cmd', additional_cmd,
|
||||
'--embed'}
|
||||
'--embed', '--headless'}
|
||||
if helpers.prepend_argv then
|
||||
return merge_args(helpers.prepend_argv, nvim_args)
|
||||
else
|
||||
@ -30,7 +29,6 @@ local function reset(...)
|
||||
end
|
||||
session = spawn(nvim_argv(...))
|
||||
set_session(session)
|
||||
request('nvim_eval', "0")
|
||||
end
|
||||
|
||||
local function set_additional_cmd(s)
|
||||
|
@ -9,9 +9,12 @@ local tmpname = helpers.tmpname()
|
||||
local append_argv = nil
|
||||
|
||||
local function nvim_argv(shada_file, embed)
|
||||
if embed == nil then
|
||||
embed = true
|
||||
end
|
||||
local argv = {nvim_prog, '-u', 'NONE', '-i', shada_file or tmpname, '-N',
|
||||
'--cmd', 'set shortmess+=I background=light noswapfile',
|
||||
embed or '--embed'}
|
||||
'--headless', embed and '--embed' or nil}
|
||||
if helpers.prepend_argv or append_argv then
|
||||
return merge_args(helpers.prepend_argv, argv, append_argv)
|
||||
else
|
||||
|
@ -224,7 +224,7 @@ describe('ShaDa support code', function()
|
||||
it('does not create incorrect file for non-existent buffers when writing from -c',
|
||||
function()
|
||||
add_argv('--cmd', 'silent edit ' .. non_existent_testfilename, '-c', 'qall')
|
||||
local argv = nvim_argv(nil, '--headless')
|
||||
local argv = nvim_argv(nil, false) -- no --embed
|
||||
eq('', funcs.system(argv))
|
||||
eq(0, exc_exec('rshada'))
|
||||
end)
|
||||
@ -233,7 +233,7 @@ describe('ShaDa support code', function()
|
||||
function()
|
||||
add_argv('-c', 'silent edit ' .. non_existent_testfilename,
|
||||
'-c', 'autocmd VimEnter * qall')
|
||||
local argv = nvim_argv(nil, '--headless')
|
||||
local argv = nvim_argv(nil, false) -- no --embed
|
||||
eq('', funcs.system(argv))
|
||||
eq(0, exc_exec('rshada'))
|
||||
end)
|
||||
|
@ -137,7 +137,7 @@ describe('ShaDa support code', function()
|
||||
|
||||
it('does not write NONE file', function()
|
||||
local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed',
|
||||
'--cmd', 'qall'}, true)
|
||||
'--headless', '--cmd', 'qall'}, true)
|
||||
session:close()
|
||||
eq(nil, lfs.attributes('NONE'))
|
||||
eq(nil, lfs.attributes('NONE.tmp.a'))
|
||||
@ -145,8 +145,8 @@ describe('ShaDa support code', function()
|
||||
|
||||
it('does not read NONE file', function()
|
||||
write_file('NONE', '\005\001\015\131\161na\162rX\194\162rc\145\196\001-')
|
||||
local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'},
|
||||
true)
|
||||
local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed',
|
||||
'--headless'}, true)
|
||||
set_session(session)
|
||||
eq('', funcs.getreg('a'))
|
||||
session:close()
|
||||
|
@ -3,18 +3,12 @@ local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local feed = helpers.feed
|
||||
local eq = helpers.eq
|
||||
local spawn, set_session = helpers.spawn, helpers.set_session
|
||||
local nvim_prog, nvim_set = helpers.nvim_prog, helpers.nvim_set
|
||||
local merge_args, prepend_argv = helpers.merge_args, helpers.prepend_argv
|
||||
local clear = helpers.clear
|
||||
|
||||
local function test_embed(ext_newgrid)
|
||||
local session, screen
|
||||
local screen
|
||||
local function startup(...)
|
||||
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE',
|
||||
'--cmd', nvim_set, '--embed'}
|
||||
nvim_argv = merge_args(prepend_argv, nvim_argv, {...})
|
||||
session = spawn(nvim_argv)
|
||||
set_session(session)
|
||||
clear{headless=false, args={...}}
|
||||
|
||||
-- attach immediately after startup, for early UI
|
||||
screen = Screen.new(60, 8)
|
||||
@ -26,10 +20,6 @@ local function test_embed(ext_newgrid)
|
||||
})
|
||||
end
|
||||
|
||||
after_each(function()
|
||||
session:close()
|
||||
end)
|
||||
|
||||
it('can display errors', function()
|
||||
startup('--cmd', 'echoerr invalid+')
|
||||
screen:expect([[
|
||||
|
Loading…
Reference in New Issue
Block a user