mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	Merge pull request #8754 from bfredl/embed_ui
startup: make --embed wait for first request so embedding UI can display startup messages
This commit is contained in:
		@@ -349,7 +349,16 @@ argument.
 | 
				
			|||||||
							*--embed*
 | 
												*--embed*
 | 
				
			||||||
--embed		Use stdin/stdout as a msgpack-RPC channel, so applications can
 | 
					--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|.  Implies |--headless|.
 | 
				
			||||||
		Equivalent to: >
 | 
					
 | 
				
			||||||
 | 
							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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							To avoid this behavior, this alterative could be used instead: >
 | 
				
			||||||
			nvim --headless --cmd "call stdioopen({'rpc': v:true})"
 | 
								nvim --headless --cmd "call stdioopen({'rpc': v:true})"
 | 
				
			||||||
<
 | 
					<
 | 
				
			||||||
		See also |channel-stdio|.
 | 
							See also |channel-stdio|.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -344,6 +344,24 @@ int main(int argc, char **argv)
 | 
				
			|||||||
    p_lpl = false;
 | 
					    p_lpl = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // give embedders a chance to set up nvim, by processing a request before
 | 
				
			||||||
 | 
					  // 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) {
 | 
				
			||||||
 | 
					    TIME_MSG("waiting for embedder to make request");
 | 
				
			||||||
 | 
					    rpc_wait_for_request();
 | 
				
			||||||
 | 
					    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");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Execute --cmd arguments.
 | 
					  // Execute --cmd arguments.
 | 
				
			||||||
  exe_pre_commands(¶ms);
 | 
					  exe_pre_commands(¶ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -456,8 +474,10 @@ int main(int argc, char **argv)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  setmouse();  // may start using the mouse
 | 
					  setmouse();  // may start using the mouse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (exmode_active) {
 | 
					  if (exmode_active || early_ui) {
 | 
				
			||||||
    must_redraw = CLEAR;  // Don't clear the screen when starting in Ex mode.
 | 
					    // Don't clear the screen when starting in Ex mode, or when an
 | 
				
			||||||
 | 
					    // embedding UI might have displayed messages
 | 
				
			||||||
 | 
					    must_redraw = CLEAR;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    screenclear();  // clear screen
 | 
					    screenclear();  // clear screen
 | 
				
			||||||
    TIME_MSG("clearing screen");
 | 
					    TIME_MSG("clearing screen");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,8 @@
 | 
				
			|||||||
static PMap(cstr_t) *event_strings = NULL;
 | 
					static PMap(cstr_t) *event_strings = NULL;
 | 
				
			||||||
static msgpack_sbuffer out_buffer;
 | 
					static msgpack_sbuffer out_buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool got_stdio_request = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
					#ifdef INCLUDE_GENERATED_DECLARATIONS
 | 
				
			||||||
# include "msgpack_rpc/channel.c.generated.h"
 | 
					# include "msgpack_rpc/channel.c.generated.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -329,6 +331,9 @@ static void handle_request(Channel *channel, msgpack_object *request)
 | 
				
			|||||||
    send_error(channel, request_id, error.msg);
 | 
					    send_error(channel, request_id, error.msg);
 | 
				
			||||||
    api_clear_error(&error);
 | 
					    api_clear_error(&error);
 | 
				
			||||||
    api_free_array(args);
 | 
					    api_free_array(args);
 | 
				
			||||||
 | 
					    if (channel->id == CHAN_STDIO) {
 | 
				
			||||||
 | 
					      got_stdio_request = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -344,6 +349,9 @@ static void handle_request(Channel *channel, msgpack_object *request)
 | 
				
			|||||||
    if (is_get_mode && !input_blocking()) {
 | 
					    if (is_get_mode && !input_blocking()) {
 | 
				
			||||||
      // Defer the event to a special queue used by os/input.c. #6247
 | 
					      // Defer the event to a special queue used by os/input.c. #6247
 | 
				
			||||||
      multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
 | 
					      multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
 | 
				
			||||||
 | 
					      if (channel->id == CHAN_STDIO) {
 | 
				
			||||||
 | 
					        got_stdio_request = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // Invoke immediately.
 | 
					      // Invoke immediately.
 | 
				
			||||||
      on_request_event((void **)&evdata);
 | 
					      on_request_event((void **)&evdata);
 | 
				
			||||||
@@ -378,6 +386,11 @@ static void on_request_event(void **argv)
 | 
				
			|||||||
  channel_decref(channel);
 | 
					  channel_decref(channel);
 | 
				
			||||||
  xfree(e);
 | 
					  xfree(e);
 | 
				
			||||||
  api_clear_error(&error);
 | 
					  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)
 | 
					static bool channel_write(Channel *channel, WBuffer *buffer)
 | 
				
			||||||
@@ -744,3 +757,16 @@ static void log_msg_close(FILE *f, msgpack_object msg)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6332,7 +6332,6 @@ int load_colors(char_u *name)
 | 
				
			|||||||
  apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
 | 
					  apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  recursive = false;
 | 
					  recursive = false;
 | 
				
			||||||
  ui_refresh();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return retval;
 | 
					  return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6885,7 +6884,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
 | 
				
			|||||||
      // "fg", which have been changed now.
 | 
					      // "fg", which have been changed now.
 | 
				
			||||||
      highlight_attr_set_all();
 | 
					      highlight_attr_set_all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!ui_is_external(kUINewgrid)) {
 | 
					      if (!ui_is_external(kUINewgrid) && starting != NO_SCREEN) {
 | 
				
			||||||
        // Older UIs assume that we clear the screen after normal group is
 | 
					        // Older UIs assume that we clear the screen after normal group is
 | 
				
			||||||
        // changed
 | 
					        // changed
 | 
				
			||||||
        ui_refresh();
 | 
					        ui_refresh();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -362,6 +362,8 @@ local function clear(...)
 | 
				
			|||||||
    table.insert(args, arg)
 | 
					    table.insert(args, arg)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  set_session(spawn(args, nil, env))
 | 
					  set_session(spawn(args, nil, env))
 | 
				
			||||||
 | 
					  -- Dummy request so that --embed continues past UI initialization
 | 
				
			||||||
 | 
					  session:request('nvim_eval', "0")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function insert(...)
 | 
					local function insert(...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ local paths = require('test.config.paths')
 | 
				
			|||||||
local helpers = require('test.functional.helpers')(nil)
 | 
					local helpers = require('test.functional.helpers')(nil)
 | 
				
			||||||
local spawn, set_session, nvim_prog, merge_args =
 | 
					local spawn, set_session, nvim_prog, merge_args =
 | 
				
			||||||
  helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args
 | 
					  helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.merge_args
 | 
				
			||||||
 | 
					local request = helpers.request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local additional_cmd = ''
 | 
					local additional_cmd = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,6 +30,7 @@ local function reset(...)
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
  session = spawn(nvim_argv(...))
 | 
					  session = spawn(nvim_argv(...))
 | 
				
			||||||
  set_session(session)
 | 
					  set_session(session)
 | 
				
			||||||
 | 
					  request('nvim_eval', "0")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function set_additional_cmd(s)
 | 
					local function set_additional_cmd(s)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										71
									
								
								test/functional/ui/embed_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								test/functional/ui/embed_spec.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					local helpers = require('test.functional.helpers')(after_each)
 | 
				
			||||||
 | 
					local Screen = require('test.functional.ui.screen')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local feed = helpers.feed
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('--embed UI on startup', function()
 | 
				
			||||||
 | 
					  local session, 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- attach immediately after startup, for early UI
 | 
				
			||||||
 | 
					    screen = Screen.new(60, 8)
 | 
				
			||||||
 | 
					    screen:attach()
 | 
				
			||||||
 | 
					    screen:set_default_attr_ids({
 | 
				
			||||||
 | 
					      [1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
 | 
				
			||||||
 | 
					      [2] = {bold = true, foreground = Screen.colors.SeaGreen4},
 | 
				
			||||||
 | 
					      [3] = {bold = true, foreground = Screen.colors.Blue1},
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  after_each(function()
 | 
				
			||||||
 | 
					    session:close()
 | 
				
			||||||
 | 
					  end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('can display errors', function()
 | 
				
			||||||
 | 
					    startup('--cmd', 'echoerr invalid+')
 | 
				
			||||||
 | 
					    screen:expect([[
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					      Error detected while processing pre-vimrc command line:     |
 | 
				
			||||||
 | 
					      E121: Undefined variable: invalid                           |
 | 
				
			||||||
 | 
					      E15: Invalid expression: invalid+                           |
 | 
				
			||||||
 | 
					      Press ENTER or type command to continue^                     |
 | 
				
			||||||
 | 
					    ]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    feed('<cr>')
 | 
				
			||||||
 | 
					    screen:expect([[
 | 
				
			||||||
 | 
					      ^                                                            |
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					      {3:~                                                           }|
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					    ]])
 | 
				
			||||||
 | 
					  end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it("doesn't erase output when setting colors", function()
 | 
				
			||||||
 | 
					    startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"')
 | 
				
			||||||
 | 
					    screen:expect([[
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					                                                                  |
 | 
				
			||||||
 | 
					      Error detected while processing pre-vimrc command line:     |
 | 
				
			||||||
 | 
					      foo                                                         |
 | 
				
			||||||
 | 
					      {1:bar}                                                         |
 | 
				
			||||||
 | 
					      {2:Press ENTER or type command to continue}^                     |
 | 
				
			||||||
 | 
					    ]])
 | 
				
			||||||
 | 
					  end)
 | 
				
			||||||
 | 
					end)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user