neovim-qt icon indicating copy to clipboard operation
neovim-qt copied to clipboard

Open input from stdin

Open SevereOverfl0w opened this issue 7 years ago • 14 comments

echo hi | nvim - opens hi in nvim buffer.

echo hi | nvim-qt - errors. Also echo hi | nvim-qt --nofork - errors. Error with nofork is:

QSocketNotifier: Invalid socket 9 and type 'Write', disabling...
Request 2 timed out: 76
Neovim fatal error "Neovim is taking too long to respond"

I think nvim-qt needs to pass through stdin to nvim.

SevereOverfl0w avatar May 06 '17 09:05 SevereOverfl0w

The problem here is that (for the default case) nvim's stdin is already in use, because nvim-qt starts nvim as nvim --embed, the --embed option uses stdin/stdout as an rpc channel between nvim and nvim-qt.

So simply passing the stdin is not an option, unless we somehow change the startup sequence (like https://github.com/equalsraf/neovim-qt/issues/50 suggests) e.g. it is possible to write a vimscript to start the gui

" start-nvim-qt.vim
let job = rpcstart('./nvim-qt', ['--embed', '--nofork'])

and then, this should work

echo hello | nvim --cmd "source start-nvim-qt.vim" --headless -

Bear in mind that this example is lacking in that it does not ensure the GUI shim is loaded and so GuiFont/ginit.vim and other features will not work properly as is.

equalsraf avatar May 06 '17 12:05 equalsraf

Have been considered the case of having other sockets to RPC on?

Like, starting (for example nvim --embed --rpc-sockets=foo,bar, and then communicate with nvim through those sockets, not stdin/stdout.

Of course, nvim would need to grow support for this, but it shouldn't be too hard (at least in POSIX systems).

Thanks!

facundobatista avatar Feb 15 '20 22:02 facundobatista

@facundobatista Nvim already supports that, so I don't understand the question?

justinmk avatar Feb 16 '20 22:02 justinmk

@justinmk hola! So you say that nvim already supports RPC on different channels than stdin/stdout? If that's the case, we only need to improve neovim-qt to use other channels, leaving stdin free?

facundobatista avatar Feb 17 '20 01:02 facundobatista

:help serverstart()

justinmk avatar Feb 17 '20 01:02 justinmk

@equalsraf is it possible to make nvim-qt to use other socket, as @justinmk is indicating, so we release stdin to be used as text input for the buffer?

facundobatista avatar Feb 17 '20 02:02 facundobatista

@equalsraf is it possible to make nvim-qt to use other socket

Probably not anymore. Some things changed since this issue was opened.

nvim is called by nvim-qt using the --embed flag which changes how neovim starts up - in particular it delays startup until the ui connects to avoid other issues. If we switched to using a different socket and allowing nvim to take in stdin input directly then the --embed behaviour would no longer be used (which would bring back past issues about error handling and startup). Right now I dont think this can be resolved this way and i dont think that is the main issue.

There is also another problem here in how exactly stdin would be passed to nvim (lets ignore the fork problem for now)

echo hi | nvim-qt -
# nvim-qt calls nvim, which means stdin needs to passed to the child
# if **-** is one of the file arguments.

what i suggested earlier was to simply invert this. Calling nvim (where test.vim starts nvim-qt via vimscript)

dmesg | nvim --cmd "source test.vim" --headless -

In either of these cases there are side effects

  • nvim will continue running (possible blocking the shell) until the user quits
  • because nvim-qt does not start nvim then it also does not close it (maybe this is solvable in some cases)

I am leaning away from any of the proposed solutions so far. Worst case scenario nvim-qt will have to handle stdin reading before starting up nvim and find a way to load it into a buffer - i'm not sure what is the best way to do this though.

equalsraf avatar Feb 17 '20 20:02 equalsraf

Worst case scenario nvim-qt will have to handle stdin reading before starting up nvim and find a way to load it into a buffer - i'm not sure what is the best way to do this though.

Read from stdlin and write into a temp file? Not everything at once, of course, chunk by chunk. Shouldn't be too hard.

And then tell nvim to open that temp file, of course.

facundobatista avatar Feb 20 '20 21:02 facundobatista

And then tell nvim to open that temp file, of course.

This last bit is more like load all data into an empty buffer. Otherwise nvim will open the file which is not what you want.

equalsraf avatar Feb 21 '20 00:02 equalsraf

Yes, probably, this is too subtle for me.

I was just doing some tests with gVim: head -c 1223 /dev/urandom | gvim - ... when I do that, it creates an empty temp directory (e.g. /tmp/vuSVP4h), I'm not sure what for.

Also checking what file descriptors has the process...

23:36:52|facundo@blackfx:/proc/15351$ ll fd
total 0
lrwx------ 1 facundo facundo 64 feb 20 23:36 0 -> /dev/pts/13
lrwx------ 1 facundo facundo 64 feb 20 23:36 1 -> /dev/pts/13
lrwx------ 1 facundo facundo 64 feb 20 23:36 10 -> socket:[20391476]
lrwx------ 1 facundo facundo 64 feb 20 23:36 11 -> anon_inode:[eventfd]
lrwx------ 1 facundo facundo 64 feb 20 23:36 2 -> /dev/pts/13
lrwx------ 1 facundo facundo 64 feb 20 23:36 20 -> socket:[2482947]
lrwx------ 1 facundo facundo 64 feb 20 23:36 3 -> socket:[20399638]
lrwx------ 1 facundo facundo 64 feb 20 23:36 4 -> anon_inode:[eventfd]
lrwx------ 1 facundo facundo 64 feb 20 23:36 5 -> /home/facundo/.svz
lrwx------ 1 facundo facundo 64 feb 20 23:36 6 -> socket:[20400350]
lrwx------ 1 facundo facundo 64 feb 20 23:36 7 -> anon_inode:[eventfd]
lrwx------ 1 facundo facundo 64 feb 20 23:36 8 -> socket:[20397377]
lrwx------ 1 facundo facundo 64 feb 20 23:36 9 -> anon_inode:[eventfd]

I found that temp buffer one:

23:37:16|facundo@blackfx:~$ stat .svz
  Fichero: .svz
  Tamaño: 12288         Bloques: 24         Bloque E/S: 4096   fichero regular
Dispositivo: 803h/2051d Nodo-i: 70214387    Enlaces: 1
Acceso: (0600/-rw-------)  Uid: ( 1000/ facundo)   Gid: ( 1000/ facundo)
Acceso: 2020-02-20 23:36:13.129432736 -0300
Modificación: 2020-02-20 23:36:22.217320163 -0300
      Cambio: 2020-02-20 23:36:22.217320163 -0300
    Creación: -

But I don't really understand what's going on here...

facundobatista avatar Feb 21 '20 02:02 facundobatista

This issue has been a major hurdle for me, coming from gvim (which is able to read stdin just fine). I have multiple things that depend on this:

  • using VIM as a PAGER
  • taking the clipboard text, formatting as JSON (with jq) and showing that in VIM, with syntax highlighting
  • etc.

Please fix this.

In the mean time, there is a workaround, at least on Linux. The idea is this: if nvim-qt cannot read from stdin/pipes, and can read from files, why not give it a file, which is a pipe - a named pipe?

Create an nv- file (make it executable & put in your $PATH), with the following content:

#!/usr/bin/env bash

function mk_tmp_fifo() {
    local temp_path
    temp_path="$(mktemp --dry-run)"
    local temp_full_path
    temp_full_path="$(readlink -f "${temp_path}")"

    mkfifo --mode=600 "${temp_full_path}"

    printf '%s' "${temp_full_path}"
}


function nv_from_stdin() {
    local nvim_pipe
    nvim_pipe="$(mk_tmp_fifo)"

    local cleanup_wait_pipe
    cleanup_wait_pipe="$(mk_tmp_fifo)"

    (
        nvim-qt \
            --spawn \
            -- \
            nvim \
                --cmd "let old_undolevels = &undolevels" \
                --cmd "set undolevels=-1" \
                --cmd "0read ${nvim_pipe}" \
                --cmd "let &undolevels = old_undolevels" \
                --cmd "unlet old_undolevels" \
                --cmd 'set nomodified' \
                "${@}"

        # signal cleanup process that it's ok to start cleaning up
        printf '' > "${cleanup_wait_pipe}"
    )&

    # pass stdin to the nvim_pipe
    cat /dev/stdin > "${nvim_pipe}"

    (
        cat "${cleanup_wait_pipe}" # wait for nvim to read the stdin

        rm "${nvim_pipe}"
        rm "${cleanup_wait_pipe}"
    )&
}

nv_from_stdin "${@}"

You can use it like this:

</path/to/my/file nv-

Or like this:

echo '<root></root>' | nv- --cmd 'set ft=xml'

cvmocanu avatar Aug 18 '22 07:08 cvmocanu

To use this as a PAGER, add export PAGER='/path/to/nv-' to your shell's startup file.

Unfortunately this can't be used as a pager for man, since neovim displays unescaped BACKSPACE (^H) characters.

To fix this, create a nv-manpager file (make it executable), with the following content:

#!/usr/bin/env bash

col -bx | NO_AT_BRIDGE=1 /home/cvmocanu/Dropbox/Configuration/bin/nv- --cmd "set ft=man"

To explain the script:

  • the col -b interprets the backspaces, and the -x replaces tabs with spaces
  • the NO_AT_BRIDGE env var prevents QT from printing some error message about not being able to register with the accessibility bus

Then add this to your shell startup script: export MANPAGER='/path/to/nv-manpager'.

cvmocanu avatar Aug 18 '22 08:08 cvmocanu

Improving the above shell script by people more knowledgeable with bash than me is appreciated.

cvmocanu avatar Aug 18 '22 08:08 cvmocanu

Is there any Neovim GUI variants which supports stdin without external scripts?

hrw avatar Dec 21 '23 12:12 hrw