mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +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
	 Björn Linse
					Björn Linse