aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2022-04-22 20:56:31 +0200
committerbfredl <bjorn.linse@gmail.com>2022-05-02 22:19:41 +0200
commitad63b94b03c166f37bda477db6cbac2a9583d586 (patch)
tree49ee0ca3d359e52630d7e30e5dbb5a53275640b1
parent619c8f4b9143e4dea0fb967ccdce594e14956ed3 (diff)
downloadrneovim-ad63b94b03c166f37bda477db6cbac2a9583d586.tar.gz
rneovim-ad63b94b03c166f37bda477db6cbac2a9583d586.tar.bz2
rneovim-ad63b94b03c166f37bda477db6cbac2a9583d586.zip
refactor(ui): simplify stdin handling
-rw-r--r--runtime/doc/ui.txt18
-rw-r--r--src/nvim/api/ui.c16
-rw-r--r--src/nvim/api/vim.c32
-rw-r--r--src/nvim/fileio.c20
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/main.c2
-rw-r--r--test/functional/helpers.lua13
-rw-r--r--test/functional/ui/embed_spec.lua48
-rw-r--r--third-party/cmake/BuildLuarocks.cmake2
9 files changed, 106 insertions, 53 deletions
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index f9110cd59b..8014199dc5 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -52,6 +52,11 @@ with these (optional) keys:
`term_name` Sets the name of the terminal 'term'.
`term_colors` Sets the number of supported colors 't_Co'.
`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|
`ui_options` key for supported options.
@@ -140,6 +145,19 @@ procedure:
Inside this request handler, the UI can safely do any initialization before
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*
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 997f0c218a..f449ccc3c7 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -283,6 +283,22 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
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.
bool is_popupmenu = strequal(name.data, "popupmenu_external");
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 9c34c912c0..061653c5af 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2661,35 +2661,3 @@ end:
xfree(cmdline);
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;
-}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 7ca7abccdc..95c373ec5c 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -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,
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 check_readonly;
int filtering = (flags & READ_FILTER);
@@ -1722,17 +1722,19 @@ failed:
xfree(buffer);
if (read_stdin) {
- close(0);
+ close(fd);
+ if (stdin_fd < 0) {
#ifndef WIN32
- // On Unix, use stderr for stdin, makes shell commands work.
- vim_ignored = dup(2);
+ // On Unix, use stderr for stdin, makes shell commands work.
+ vim_ignored = dup(2);
#else
- // On Windows, use the console input handle for stdin.
- HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
- OPEN_EXISTING, 0, (HANDLE)NULL);
- vim_ignored = _open_osfhandle(conin, _O_RDONLY);
+ // On Windows, use the console input handle for stdin.
+ HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
+ OPEN_EXISTING, 0, (HANDLE)NULL);
+ vim_ignored = _open_osfhandle(conin, _O_RDONLY);
#endif
+ }
}
if (tmpname != NULL) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 8d03bdf000..3ae0d32d8f 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -507,6 +507,9 @@ EXTERN int v_dying INIT(= 0);
EXTERN int stdin_isatty INIT(= true);
// is stdout a terminal?
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.
// volatile because it is used in a signal handler.
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 recoverymode INIT(= false); // Set to true for "-r" option
-EXTERN int stdin_filedesc INIT(= -1); // stdin filedesc set by embedder
// typeahead buffer
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
// 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
EXTERN char psepc INIT(= '\\'); // normal path separator character
EXTERN char psepcN INIT(= '/'); // abnormal path separator character
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 253a89467e..5a496a10b4 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -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
// are the same terminal: "cat | vim -".
// 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();
}
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 173fc54af5..b0b2dac9fd 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -361,14 +361,15 @@ local function remove_args(args, args_rm)
return new_args
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
session:close()
end
local child_stream = ChildProcessStream.spawn(
merge and module.merge_args(prepend_argv, argv) or argv,
- env)
+ env, io_extra)
return Session.new(child_stream)
end
@@ -415,8 +416,8 @@ end
-- clear('-e')
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
function module.clear(...)
- local argv, env = module.new_argv(...)
- module.set_session(module.spawn(argv, nil, env))
+ local argv, env, io_extra = module.new_argv(...)
+ module.set_session(module.spawn(argv, nil, env, nil, io_extra))
end
-- Builds an argument list for use in clear().
@@ -426,6 +427,7 @@ function module.new_argv(...)
local args = {unpack(module.nvim_argv)}
table.insert(args, '--headless')
local new_args
+ local io_extra
local env = nil
local opts = select(1, ...)
if type(opts) == 'table' then
@@ -461,13 +463,14 @@ function module.new_argv(...)
end
end
new_args = opts.args or {}
+ io_extra = opts.io_extra
else
new_args = {...}
end
for _, arg in ipairs(new_args) do
table.insert(args, arg)
end
- return args, env
+ return args, env, io_extra
end
function module.insert(...)
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
index 8218c8e12d..92f5beebf5 100644
--- a/test/functional/ui/embed_spec.lua
+++ b/test/functional/ui/embed_spec.lua
@@ -1,3 +1,5 @@
+local uv = require'luv'
+
local helpers = require('test.functional.helpers')(after_each)
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=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)
diff --git a/third-party/cmake/BuildLuarocks.cmake b/third-party/cmake/BuildLuarocks.cmake
index 0cef37736d..244d1d9fb8 100644
--- a/third-party/cmake/BuildLuarocks.cmake
+++ b/third-party/cmake/BuildLuarocks.cmake
@@ -225,7 +225,7 @@ if(USE_BUNDLED_BUSTED)
# nvim-client: https://github.com/neovim/lua-client
add_custom_command(OUTPUT ${ROCKS_DIR}/nvim-client
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)
add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client)