diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-05-02 23:24:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-02 23:24:37 +0200 |
commit | ddf7bb24f98b468d2bc6c16c6f300570fc6530f5 (patch) | |
tree | 8d2c78c2fa94ee7c7bfd40d7cc2f00bc54801eaf | |
parent | a1542b091dd7b919fa8524c1c909ef897dde9299 (diff) | |
parent | ad63b94b03c166f37bda477db6cbac2a9583d586 (diff) | |
download | rneovim-ddf7bb24f98b468d2bc6c16c6f300570fc6530f5.tar.gz rneovim-ddf7bb24f98b468d2bc6c16c6f300570fc6530f5.tar.bz2 rneovim-ddf7bb24f98b468d2bc6c16c6f300570fc6530f5.zip |
Merge pull request #18357 from bfredl/ui_stdin
feat(ui): allow embedder to emulate "cat data | nvim -" behaviour
-rw-r--r-- | runtime/doc/ui.txt | 18 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 16 | ||||
-rw-r--r-- | src/nvim/fileio.c | 20 | ||||
-rw-r--r-- | src/nvim/globals.h | 4 | ||||
-rw-r--r-- | src/nvim/main.c | 2 | ||||
-rw-r--r-- | test/functional/helpers.lua | 13 | ||||
-rw-r--r-- | test/functional/ui/embed_spec.lua | 48 | ||||
-rw-r--r-- | third-party/cmake/BuildLuarocks.cmake | 2 |
8 files changed, 106 insertions, 17 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/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 6443759a39..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); @@ -847,7 +850,6 @@ EXTERN linenr_T printer_page_num; EXTERN bool typebuf_was_filled INIT(= false); // received text from client // or from feedkeys() - #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 952064ab73..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 && !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) |