mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge #9829 'startup: remove TUI init special-case'
fixes #7967
fixes #9959
Historically Vim/Nvim does backflips to handle input and show messages
before a UI is available. This logical contradiction was already fixed
for remote UIs (#9024 c236e80cf3). Fixing it also for the TUI avoids
problems on Windows, simplifies the logic, and avoids races like #9959.
- Move ui_builtin_start() to the same position as embedded_mode
  remote_ui_wait_for_attach().
- If stdin is redirected, save the original `stdin` and replace fd
  0 with tty before calling `ui_builtin_start()`.
			
			
This commit is contained in:
		| @@ -316,28 +316,9 @@ int main(int argc, char **argv) | ||||
|   // Set the break level after the terminal is initialized. | ||||
|   debug_break_level = params.use_debug_break_level; | ||||
|  | ||||
|   // | ||||
|   // Read user-input if any TTY is connected. | ||||
|   // 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); | ||||
|   bool reading_excmds = !params.input_isatty | ||||
|                         && silent_mode | ||||
|                         && exmode_active == EXMODE_NORMAL; | ||||
|   if (reading_tty || reading_excmds) { | ||||
|     // One of the startup commands (arguments, sourced scripts or plugins) may | ||||
|     // prompt the user, so start reading from a tty now. | ||||
|     int fd = STDIN_FILENO; | ||||
|     if (!silent_mode | ||||
|         && (!params.input_isatty || params.edit_type == EDIT_STDIN)) { | ||||
|       // Use stderr or stdout since stdin is being used to read commands. | ||||
|       fd = params.err_isatty ? fileno(stderr) : fileno(stdout); | ||||
|     } | ||||
|     input_start(fd); | ||||
|   if (!params.input_isatty && silent_mode && exmode_active == EXMODE_NORMAL) { | ||||
|     input_start(STDIN_FILENO); | ||||
|   } | ||||
|  | ||||
|   // open terminals when opening files that start with term:// | ||||
| @@ -366,16 +347,22 @@ 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 && !headless_mode) { | ||||
|     TIME_MSG("waiting for embedder to make request"); | ||||
|   bool use_remote_ui = (embedded_mode && !headless_mode); | ||||
|   bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); | ||||
|   if (use_remote_ui || use_builtin_ui) { | ||||
|     TIME_MSG("waiting for UI to make request"); | ||||
|     if (use_remote_ui) { | ||||
|       remote_ui_wait_for_attach(); | ||||
|     TIME_MSG("done waiting for embedder"); | ||||
|     } else { | ||||
|       ui_builtin_start(); | ||||
|     } | ||||
|     TIME_MSG("done waiting for UI"); | ||||
|  | ||||
|     // prepare screen now, so external UIs can display messages | ||||
|     starting = NO_BUFFERS; | ||||
|     screenclear(); | ||||
|     early_ui = true; | ||||
|     TIME_MSG("initialized screen early for embedder"); | ||||
|     TIME_MSG("initialized screen early for UI"); | ||||
|   } | ||||
|  | ||||
|   // Execute --cmd arguments. | ||||
| @@ -469,25 +456,12 @@ int main(int argc, char **argv) | ||||
|     read_stdin(); | ||||
|   } | ||||
|  | ||||
|   if (reading_tty && (need_wait_return || msg_didany)) { | ||||
|     // Because there's no UI yet, error messages would have been printed to | ||||
|     // stdout.  Before starting we need confirmation that the user has seen the | ||||
|     // messages and that is done with a call to wait_return. | ||||
|     TIME_MSG("waiting for return"); | ||||
|     wait_return(true); | ||||
|   } | ||||
|  | ||||
|   if (!headless_mode && !embedded_mode && !silent_mode) { | ||||
|     input_stop();  // Stop reading input, let the UI take over. | ||||
|     ui_builtin_start(); | ||||
|   } | ||||
|  | ||||
|   setmouse();  // may start using the mouse | ||||
|  | ||||
|   if (exmode_active || early_ui) { | ||||
|     // Don't clear the screen when starting in Ex mode, or when an | ||||
|     // embedding UI might have displayed messages | ||||
|     must_redraw = CLEAR; | ||||
|     // Don't clear the screen when starting in Ex mode, or when a UI might have | ||||
|     // displayed messages. | ||||
|     redraw_later(VALID); | ||||
|   } else { | ||||
|     screenclear();  // clear screen | ||||
|     TIME_MSG("clearing screen"); | ||||
|   | ||||
| @@ -48,6 +48,26 @@ void tinput_init(TermInput *input, Loop *loop) | ||||
|  | ||||
|   int curflags = termkey_get_canonflags(input->tk); | ||||
|   termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); | ||||
|  | ||||
|   // If stdin is not a pty, switch to stderr. For cases like: | ||||
|   //    echo q | nvim -es | ||||
|   //    ls *.md | xargs nvim | ||||
| #ifdef WIN32 | ||||
|   if (!os_isatty(0)) { | ||||
|       const HANDLE conin_handle = CreateFile("CONIN$", | ||||
|                                              GENERIC_READ | GENERIC_WRITE, | ||||
|                                              FILE_SHARE_READ | FILE_SHARE_WRITE, | ||||
|                                              (LPSECURITY_ATTRIBUTES)NULL, | ||||
|                                              OPEN_EXISTING, 0, (HANDLE)NULL); | ||||
|       input->in_fd = _open_osfhandle(conin_handle, _O_RDONLY); | ||||
|       assert(input->in_fd != -1); | ||||
|   } | ||||
| #else | ||||
|   if (!os_isatty(0) && os_isatty(2)) { | ||||
|     input->in_fd = 2; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   // setup input handle | ||||
|   rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); | ||||
|   // initialize a timer handle for handling ESC with libtermkey | ||||
| @@ -435,24 +455,7 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, | ||||
|   TermInput *input = data; | ||||
|  | ||||
|   if (eof) { | ||||
|     if (input->in_fd == 0 && !os_isatty(0) && os_isatty(2)) { | ||||
|       // Started reading from stdin which is not a pty but failed. Switch to | ||||
|       // stderr since it is a pty. | ||||
|       // | ||||
|       // This is how we support commands like: | ||||
|       // | ||||
|       // echo q | nvim -es | ||||
|       // | ||||
|       // and | ||||
|       // | ||||
|       // ls *.md | xargs nvim | ||||
|       input->in_fd = 2; | ||||
|       stream_close(&input->read_stream, NULL, NULL); | ||||
|       multiqueue_put(input->loop->fast_events, tinput_restart_reading, 1, | ||||
|                      input); | ||||
|     } else { | ||||
|     loop_schedule(&main_loop, event_create(tinput_done_event, 0)); | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| @@ -496,10 +499,3 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_, | ||||
|   // without wrap around, otherwise it could be misinterpreted. | ||||
|   rbuffer_reset(input->read_stream.buffer); | ||||
| } | ||||
|  | ||||
| static void tinput_restart_reading(void **argv) | ||||
| { | ||||
|   TermInput *input = argv[0]; | ||||
|   rstream_init_fd(input->loop, &input->read_stream, input->in_fd, 0xfff); | ||||
|   rstream_start(&input->read_stream, tinput_read_cb, input); | ||||
| } | ||||
|   | ||||
| @@ -46,7 +46,7 @@ describe('startup', function() | ||||
|     ]]) | ||||
|   end) | ||||
|   it('in a TTY: has("ttyin")==1 has("ttyout")==1', function() | ||||
|     local screen = Screen.new(25, 3) | ||||
|     local screen = Screen.new(25, 4) | ||||
|     screen:attach() | ||||
|     if iswin() then | ||||
|       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) | ||||
| @@ -58,6 +58,7 @@ describe('startup', function() | ||||
|             ..[[, shellescape(v:progpath))]]) | ||||
|     screen:expect([[ | ||||
|       ^                         | | ||||
|       ~                        | | ||||
|       1 1                      | | ||||
|                                | | ||||
|     ]]) | ||||
| @@ -96,7 +97,7 @@ describe('startup', function() | ||||
|     end) | ||||
|   end) | ||||
|   it('input from pipe (implicit) #7679', function() | ||||
|     local screen = Screen.new(25, 3) | ||||
|     local screen = Screen.new(25, 4) | ||||
|     screen:attach() | ||||
|     if iswin() then | ||||
|       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) | ||||
| @@ -109,6 +110,7 @@ describe('startup', function() | ||||
|             ..[[, shellescape(v:progpath))]]) | ||||
|     screen:expect([[ | ||||
|       ^foo                      | | ||||
|       ~                        | | ||||
|       0 1                      | | ||||
|                                | | ||||
|     ]]) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes