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. |   // Set the break level after the terminal is initialized. | ||||||
|   debug_break_level = params.use_debug_break_level; |   debug_break_level = params.use_debug_break_level; | ||||||
|  |  | ||||||
|   // |  | ||||||
|   // Read user-input if any TTY is connected. |  | ||||||
|   // Read ex-commands if invoked with "-es". |   // Read ex-commands if invoked with "-es". | ||||||
|   // |   if (!params.input_isatty && silent_mode && exmode_active == EXMODE_NORMAL) { | ||||||
|   bool reading_tty = !headless_mode |     input_start(STDIN_FILENO); | ||||||
|                      && !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); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // open terminals when opening files that start with term:// |   // 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 |   // startup. This allows an external UI to show messages and prompts from | ||||||
|   // --cmd and buffer loading (e.g. swap files) |   // --cmd and buffer loading (e.g. swap files) | ||||||
|   bool early_ui = false; |   bool early_ui = false; | ||||||
|   if (embedded_mode && !headless_mode) { |   bool use_remote_ui = (embedded_mode && !headless_mode); | ||||||
|     TIME_MSG("waiting for embedder to make request"); |   bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); | ||||||
|     remote_ui_wait_for_attach(); |   if (use_remote_ui || use_builtin_ui) { | ||||||
|     TIME_MSG("done waiting for embedder"); |     TIME_MSG("waiting for UI to make request"); | ||||||
|  |     if (use_remote_ui) { | ||||||
|  |       remote_ui_wait_for_attach(); | ||||||
|  |     } else { | ||||||
|  |       ui_builtin_start(); | ||||||
|  |     } | ||||||
|  |     TIME_MSG("done waiting for UI"); | ||||||
|  |  | ||||||
|     // prepare screen now, so external UIs can display messages |     // prepare screen now, so external UIs can display messages | ||||||
|     starting = NO_BUFFERS; |     starting = NO_BUFFERS; | ||||||
|     screenclear(); |     screenclear(); | ||||||
|     early_ui = true; |     early_ui = true; | ||||||
|     TIME_MSG("initialized screen early for embedder"); |     TIME_MSG("initialized screen early for UI"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Execute --cmd arguments. |   // Execute --cmd arguments. | ||||||
| @@ -469,25 +456,12 @@ int main(int argc, char **argv) | |||||||
|     read_stdin(); |     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 |   setmouse();  // may start using the mouse | ||||||
|  |  | ||||||
|   if (exmode_active || early_ui) { |   if (exmode_active || early_ui) { | ||||||
|     // Don't clear the screen when starting in Ex mode, or when an |     // Don't clear the screen when starting in Ex mode, or when a UI might have | ||||||
|     // embedding UI might have displayed messages |     // displayed messages. | ||||||
|     must_redraw = CLEAR; |     redraw_later(VALID); | ||||||
|   } else { |   } else { | ||||||
|     screenclear();  // clear screen |     screenclear();  // clear screen | ||||||
|     TIME_MSG("clearing screen"); |     TIME_MSG("clearing screen"); | ||||||
|   | |||||||
| @@ -48,6 +48,26 @@ void tinput_init(TermInput *input, Loop *loop) | |||||||
|  |  | ||||||
|   int curflags = termkey_get_canonflags(input->tk); |   int curflags = termkey_get_canonflags(input->tk); | ||||||
|   termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); |   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 |   // setup input handle | ||||||
|   rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); |   rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); | ||||||
|   // initialize a timer handle for handling ESC with libtermkey |   // 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; |   TermInput *input = data; | ||||||
|  |  | ||||||
|   if (eof) { |   if (eof) { | ||||||
|     if (input->in_fd == 0 && !os_isatty(0) && os_isatty(2)) { |     loop_schedule(&main_loop, event_create(tinput_done_event, 0)); | ||||||
|       // 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; |     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. |   // without wrap around, otherwise it could be misinterpreted. | ||||||
|   rbuffer_reset(input->read_stream.buffer); |   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) |   end) | ||||||
|   it('in a TTY: has("ttyin")==1 has("ttyout")==1', function() |   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() |     screen:attach() | ||||||
|     if iswin() then |     if iswin() then | ||||||
|       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) |       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) | ||||||
| @@ -58,6 +58,7 @@ describe('startup', function() | |||||||
|             ..[[, shellescape(v:progpath))]]) |             ..[[, shellescape(v:progpath))]]) | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
|       ^                         | |       ^                         | | ||||||
|  |       ~                        | | ||||||
|       1 1                      | |       1 1                      | | ||||||
|                                | |                                | | ||||||
|     ]]) |     ]]) | ||||||
| @@ -96,7 +97,7 @@ describe('startup', function() | |||||||
|     end) |     end) | ||||||
|   end) |   end) | ||||||
|   it('input from pipe (implicit) #7679', function() |   it('input from pipe (implicit) #7679', function() | ||||||
|     local screen = Screen.new(25, 3) |     local screen = Screen.new(25, 4) | ||||||
|     screen:attach() |     screen:attach() | ||||||
|     if iswin() then |     if iswin() then | ||||||
|       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) |       command([[set shellcmdflag=/s\ /c shellxquote=\"]]) | ||||||
| @@ -109,6 +110,7 @@ describe('startup', function() | |||||||
|             ..[[, shellescape(v:progpath))]]) |             ..[[, shellescape(v:progpath))]]) | ||||||
|     screen:expect([[ |     screen:expect([[ | ||||||
|       ^foo                      | |       ^foo                      | | ||||||
|  |       ~                        | | ||||||
|       0 1                      | |       0 1                      | | ||||||
|                                | |                                | | ||||||
|     ]]) |     ]]) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Justin M. Keyes
					Justin M. Keyes