mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
refactor(ui): simplify stdin handling
This commit is contained in:
parent
619c8f4b91
commit
ad63b94b03
@ -52,6 +52,11 @@ with these (optional) keys:
|
|||||||
`term_name` Sets the name of the terminal 'term'.
|
`term_name` Sets the name of the terminal 'term'.
|
||||||
`term_colors` Sets the number of supported colors 't_Co'.
|
`term_colors` Sets the number of supported colors 't_Co'.
|
||||||
`term_background` Sets the default value of 'background'.
|
`term_background` Sets the default value of 'background'.
|
||||||
|
`stdin_fd` Read buffer from `fd` as if it was a stdin pipe
|
||||||
|
This option can only used by |--embed| ui,
|
||||||
|
see |ui-startup-stdin|.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Specifying an unknown option is an error; UIs can check the |api-metadata|
|
Specifying an unknown option is an error; UIs can check the |api-metadata|
|
||||||
`ui_options` key for supported options.
|
`ui_options` key for supported options.
|
||||||
@ -140,6 +145,19 @@ procedure:
|
|||||||
Inside this request handler, the UI can safely do any initialization before
|
Inside this request handler, the UI can safely do any initialization before
|
||||||
entering normal mode, for example reading variables set by init.vim.
|
entering normal mode, for example reading variables set by init.vim.
|
||||||
|
|
||||||
|
*ui-startup-stdin*
|
||||||
|
An UI can support the native read from stdin feature as invoked with
|
||||||
|
`command | nvim -` for the builtin TUI. |--|
|
||||||
|
The embedding process can detect that its stdin is open to a file which
|
||||||
|
not is a terminal, just like nvim does. It then needs to forward this fd
|
||||||
|
to Nvim. As fd=0 is already is used to send rpc data from the embedder to
|
||||||
|
Nvim, it needs to use some other file descriptor, like fd=3 or higher.
|
||||||
|
|
||||||
|
Then, `stdin_fd` option should be passed to `nvim_ui_attach` and nvim will
|
||||||
|
implicitly read it as a buffer. This option can only be used when Nvim is
|
||||||
|
launched with `--embed` option, as described above.
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Global Events *ui-global*
|
Global Events *ui-global*
|
||||||
|
|
||||||
|
@ -283,6 +283,22 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strequal(name.data, "stdin_fd")) {
|
||||||
|
if (value.type != kObjectTypeInteger || value.data.integer < 0) {
|
||||||
|
api_set_error(error, kErrorTypeValidation, "stdin_fd must be a non-negative Integer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starting != NO_SCREEN) {
|
||||||
|
api_set_error(error, kErrorTypeValidation,
|
||||||
|
"stdin_fd can only be used with first attached ui");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdin_fd = (int)value.data.integer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// LEGACY: Deprecated option, use `ext_cmdline` instead.
|
// LEGACY: Deprecated option, use `ext_cmdline` instead.
|
||||||
bool is_popupmenu = strequal(name.data, "popupmenu_external");
|
bool is_popupmenu = strequal(name.data, "popupmenu_external");
|
||||||
|
|
||||||
|
@ -2661,35 +2661,3 @@ end:
|
|||||||
xfree(cmdline);
|
xfree(cmdline);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invokes the nvim server to read from stdin when it is not a tty
|
|
||||||
///
|
|
||||||
/// It enables functionalities like:
|
|
||||||
/// - echo "1f u c4n r34d th1s u r34lly n33d t0 g37 r357"| nvim -
|
|
||||||
/// - cat path/to/a/file | nvim -
|
|
||||||
/// It has to be called before |nvim_ui_attach()| is called in order
|
|
||||||
/// to ensure proper functioning.
|
|
||||||
///
|
|
||||||
/// @param channel_id: The channel id of the GUI-client
|
|
||||||
/// @param filedesc: The file descriptor of the GUI-client process' stdin
|
|
||||||
/// @param implicit: Tells if read_stdin call is implicit.
|
|
||||||
/// i.e for cases like `echo xxx | nvim`
|
|
||||||
/// @param[out] err Error details, if any
|
|
||||||
void nvim_read_stdin(uint64_t channel_id, Integer filedesc, Error *err)
|
|
||||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY
|
|
||||||
{
|
|
||||||
if (starting != NO_SCREEN) {
|
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"nvim_read_stdin must be called before nvim_ui_attach");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (filedesc < 0) {
|
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"file descriptor must be non-negative");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stdin_filedesc = (int)filedesc;
|
|
||||||
implicit_readstdin = implicit;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
@ -176,7 +176,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
|
|||||||
int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip,
|
int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip,
|
||||||
linenr_T lines_to_read, exarg_T *eap, int flags, bool silent)
|
linenr_T lines_to_read, exarg_T *eap, int flags, bool silent)
|
||||||
{
|
{
|
||||||
int fd = 0;
|
int fd = stdin_fd >= 0 ? stdin_fd : 0;
|
||||||
int newfile = (flags & READ_NEW);
|
int newfile = (flags & READ_NEW);
|
||||||
int check_readonly;
|
int check_readonly;
|
||||||
int filtering = (flags & READ_FILTER);
|
int filtering = (flags & READ_FILTER);
|
||||||
@ -1722,17 +1722,19 @@ failed:
|
|||||||
xfree(buffer);
|
xfree(buffer);
|
||||||
|
|
||||||
if (read_stdin) {
|
if (read_stdin) {
|
||||||
close(0);
|
close(fd);
|
||||||
|
if (stdin_fd < 0) {
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
// On Unix, use stderr for stdin, makes shell commands work.
|
// On Unix, use stderr for stdin, makes shell commands work.
|
||||||
vim_ignored = dup(2);
|
vim_ignored = dup(2);
|
||||||
#else
|
#else
|
||||||
// On Windows, use the console input handle for stdin.
|
// On Windows, use the console input handle for stdin.
|
||||||
HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||||
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
|
FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
|
||||||
OPEN_EXISTING, 0, (HANDLE)NULL);
|
OPEN_EXISTING, 0, (HANDLE)NULL);
|
||||||
vim_ignored = _open_osfhandle(conin, _O_RDONLY);
|
vim_ignored = _open_osfhandle(conin, _O_RDONLY);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmpname != NULL) {
|
if (tmpname != NULL) {
|
||||||
|
@ -507,6 +507,9 @@ EXTERN int v_dying INIT(= 0);
|
|||||||
EXTERN int stdin_isatty INIT(= true);
|
EXTERN int stdin_isatty INIT(= true);
|
||||||
// is stdout a terminal?
|
// is stdout a terminal?
|
||||||
EXTERN int stdout_isatty INIT(= true);
|
EXTERN int stdout_isatty INIT(= true);
|
||||||
|
/// filedesc set by embedder for reading first buffer like `cmd | nvim -`
|
||||||
|
EXTERN int stdin_fd INIT(= -1);
|
||||||
|
|
||||||
// true when doing full-screen output, otherwise only writing some messages.
|
// true when doing full-screen output, otherwise only writing some messages.
|
||||||
// volatile because it is used in a signal handler.
|
// volatile because it is used in a signal handler.
|
||||||
EXTERN volatile int full_screen INIT(= false);
|
EXTERN volatile int full_screen INIT(= false);
|
||||||
@ -704,7 +707,6 @@ EXTERN int RedrawingDisabled INIT(= 0);
|
|||||||
|
|
||||||
EXTERN int readonlymode INIT(= false); // Set to true for "view"
|
EXTERN int readonlymode INIT(= false); // Set to true for "view"
|
||||||
EXTERN int recoverymode INIT(= false); // Set to true for "-r" option
|
EXTERN int recoverymode INIT(= false); // Set to true for "-r" option
|
||||||
EXTERN int stdin_filedesc INIT(= -1); // stdin filedesc set by embedder
|
|
||||||
|
|
||||||
// typeahead buffer
|
// typeahead buffer
|
||||||
EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 });
|
EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 });
|
||||||
@ -848,10 +850,6 @@ EXTERN linenr_T printer_page_num;
|
|||||||
EXTERN bool typebuf_was_filled INIT(= false); // received text from client
|
EXTERN bool typebuf_was_filled INIT(= false); // received text from client
|
||||||
// or from feedkeys()
|
// or from feedkeys()
|
||||||
|
|
||||||
EXTERN bool implicit_readstdin INIT(= false); // Used in embed job created
|
|
||||||
// by TUI process only in
|
|
||||||
// builtin tui
|
|
||||||
|
|
||||||
#ifdef BACKSLASH_IN_FILENAME
|
#ifdef BACKSLASH_IN_FILENAME
|
||||||
EXTERN char psepc INIT(= '\\'); // normal path separator character
|
EXTERN char psepc INIT(= '\\'); // normal path separator character
|
||||||
EXTERN char psepcN INIT(= '/'); // abnormal path separator character
|
EXTERN char psepcN INIT(= '/'); // abnormal path separator character
|
||||||
|
@ -454,7 +454,7 @@ int main(int argc, char **argv)
|
|||||||
// writing end of the pipe doesn't like, e.g., in case stdin and stderr
|
// writing end of the pipe doesn't like, e.g., in case stdin and stderr
|
||||||
// are the same terminal: "cat | vim -".
|
// are the same terminal: "cat | vim -".
|
||||||
// Using autocommands here may cause trouble...
|
// Using autocommands here may cause trouble...
|
||||||
if ((params.edit_type == EDIT_STDIN || implicit_readstdin) && !recoverymode) {
|
if ((params.edit_type == EDIT_STDIN || stdin_fd >= 0) && !recoverymode) {
|
||||||
read_stdin();
|
read_stdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,14 +361,15 @@ local function remove_args(args, args_rm)
|
|||||||
return new_args
|
return new_args
|
||||||
end
|
end
|
||||||
|
|
||||||
function module.spawn(argv, merge, env, keep)
|
--- @param io_extra used for stdin_fd, see :help ui-option
|
||||||
|
function module.spawn(argv, merge, env, keep, io_extra)
|
||||||
if session and not keep then
|
if session and not keep then
|
||||||
session:close()
|
session:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local child_stream = ChildProcessStream.spawn(
|
local child_stream = ChildProcessStream.spawn(
|
||||||
merge and module.merge_args(prepend_argv, argv) or argv,
|
merge and module.merge_args(prepend_argv, argv) or argv,
|
||||||
env)
|
env, io_extra)
|
||||||
return Session.new(child_stream)
|
return Session.new(child_stream)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -415,8 +416,8 @@ end
|
|||||||
-- clear('-e')
|
-- clear('-e')
|
||||||
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
|
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
|
||||||
function module.clear(...)
|
function module.clear(...)
|
||||||
local argv, env = module.new_argv(...)
|
local argv, env, io_extra = module.new_argv(...)
|
||||||
module.set_session(module.spawn(argv, nil, env))
|
module.set_session(module.spawn(argv, nil, env, nil, io_extra))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Builds an argument list for use in clear().
|
-- Builds an argument list for use in clear().
|
||||||
@ -426,6 +427,7 @@ function module.new_argv(...)
|
|||||||
local args = {unpack(module.nvim_argv)}
|
local args = {unpack(module.nvim_argv)}
|
||||||
table.insert(args, '--headless')
|
table.insert(args, '--headless')
|
||||||
local new_args
|
local new_args
|
||||||
|
local io_extra
|
||||||
local env = nil
|
local env = nil
|
||||||
local opts = select(1, ...)
|
local opts = select(1, ...)
|
||||||
if type(opts) == 'table' then
|
if type(opts) == 'table' then
|
||||||
@ -461,13 +463,14 @@ function module.new_argv(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
new_args = opts.args or {}
|
new_args = opts.args or {}
|
||||||
|
io_extra = opts.io_extra
|
||||||
else
|
else
|
||||||
new_args = {...}
|
new_args = {...}
|
||||||
end
|
end
|
||||||
for _, arg in ipairs(new_args) do
|
for _, arg in ipairs(new_args) do
|
||||||
table.insert(args, arg)
|
table.insert(args, arg)
|
||||||
end
|
end
|
||||||
return args, env
|
return args, env, io_extra
|
||||||
end
|
end
|
||||||
|
|
||||||
function module.insert(...)
|
function module.insert(...)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local uv = require'luv'
|
||||||
|
|
||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local Screen = require('test.functional.ui.screen')
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
@ -98,3 +100,49 @@ end
|
|||||||
|
|
||||||
describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
|
describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
|
||||||
describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end)
|
describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end)
|
||||||
|
|
||||||
|
describe('--embed UI', function()
|
||||||
|
it('can pass stdin', function()
|
||||||
|
local pipe = assert(uv.pipe())
|
||||||
|
|
||||||
|
local writer = assert(uv.new_pipe(false))
|
||||||
|
writer:open(pipe.write)
|
||||||
|
|
||||||
|
clear {args_rm={'--headless'}, io_extra=pipe.read}
|
||||||
|
|
||||||
|
-- attach immediately after startup, for early UI
|
||||||
|
local screen = Screen.new(40, 8)
|
||||||
|
screen:attach {stdin_fd=3}
|
||||||
|
screen:set_default_attr_ids {
|
||||||
|
[1] = {bold = true, foreground = Screen.colors.Blue1};
|
||||||
|
[2] = {bold = true};
|
||||||
|
}
|
||||||
|
|
||||||
|
writer:write "hello nvim\nfrom external input\n"
|
||||||
|
writer:shutdown(function() writer:close() end)
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^hello nvim |
|
||||||
|
from external input |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- stdin (rpc input) still works
|
||||||
|
feed 'o'
|
||||||
|
screen:expect{grid=[[
|
||||||
|
hello nvim |
|
||||||
|
^ |
|
||||||
|
from external input |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
2
third-party/cmake/BuildLuarocks.cmake
vendored
2
third-party/cmake/BuildLuarocks.cmake
vendored
@ -225,7 +225,7 @@ if(USE_BUNDLED_BUSTED)
|
|||||||
# nvim-client: https://github.com/neovim/lua-client
|
# nvim-client: https://github.com/neovim/lua-client
|
||||||
add_custom_command(OUTPUT ${ROCKS_DIR}/nvim-client
|
add_custom_command(OUTPUT ${ROCKS_DIR}/nvim-client
|
||||||
COMMAND ${LUAROCKS_BINARY}
|
COMMAND ${LUAROCKS_BINARY}
|
||||||
ARGS build nvim-client 0.2.2-1 ${LUAROCKS_BUILDARGS}
|
ARGS build nvim-client 0.2.3-1 ${LUAROCKS_BUILDARGS}
|
||||||
DEPENDS luv)
|
DEPENDS luv)
|
||||||
add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client)
|
add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user