Merge pull request #9024 from bfredl/embed_ui2

always wait for UI with --embed, unless --headless is supplied
This commit is contained in:
Björn Linse 2018-09-22 10:20:23 +02:00 committed by GitHub
commit c236e80cf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 68 additions and 79 deletions

View File

@ -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. >

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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)

View File

@ -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'}))

View File

@ -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(...)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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([[