mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06: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:
commit
58f505dc74
@ -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 |
|
||||||
|
|
|
|
||||||
]])
|
]])
|
||||||
|
Loading…
Reference in New Issue
Block a user