Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC call (K_EVENT) completes pending op (d, y, ...) #6166

Closed
lunixbochs opened this issue Feb 23, 2017 · 13 comments
Closed

RPC call (K_EVENT) completes pending op (d, y, ...) #6166

lunixbochs opened this issue Feb 23, 2017 · 13 comments
Labels
api libnvim, Nvim RPC API bug issues reporting wrong behavior has:repro issue contains minimal reproducing steps rpc
Milestone

Comments

@lunixbochs
Copy link

lunixbochs commented Feb 23, 2017

  • nvim --version: v0.2.0-624-gddab466, v0.1.6
  • Operating system/version: macOS
  • Terminal name/version: Python 2/3

Actual behaviour

input('d'), nvim_buf_get_lines(), input('d') will either not delete a line, or will delete one line per input('d') depending on context. Yank via input('y') has the same behavior.

Expected behaviour

input('d'), nvim_buf_get_lines(), input('d') should act the same as input('dd'), or the API should expose some way of indicating a single input of d isn't finished so I shouldn't get the buffer yet.

Steps to reproduce using nvim -u NORC

import neovim

def setup(p):
    global n
    n = neovim.attach('child', argv=['/usr/local/bin/nvim', '--embed'])
    n.command('enew!')
    n.current.buffer[:] = ['line 1', 'line 2', 'line 3']
    print('')
    print('<new instance p={}>'.format(p))

def pbuf():
    for i, line in enumerate(n.current.buffer):
        i += 1
        if i == n.eval('line(".")'):
            i = '*'
        print('{} | {:s}'.format(i, line))

def run(*args, **kwargs):
    p = kwargs.get('p', True)
    setup(p)
    fn = n.input
    for arg in args:
        print('cmd: {:s}({:s})'.format(fn.__name__, repr(arg)))
        fn(arg)
        if p: pbuf()

# case 1
print('-' * 20)
print("case 1: input('d') deletes line without waiting for more input")
run('<esc>G', 'd', 'd', 'd', 'd')

# case 2
print('')
print('-' * 20)
print("case 2: input('d'), input('d') is a noop, but input('G') or input('dd') converts into case 1")
run('d', 'd')
run(':3\n', 'd', 'd')
run(':3\n', 'dd', 'd', 'd')
# works
run('d', 'd', p=False)
pbuf()

Here's my output: https://gist.github.com/lunixbochs/582b2435c6c2ba7e8f12fd2ee7df2f7f

Note the last case with p=False that doesn't query buf[:] will exhibit the correct output.

@justinmk justinmk added api libnvim, Nvim RPC API bug issues reporting wrong behavior rpc labels Feb 26, 2017
@justinmk justinmk added this to the 0.2 milestone Feb 26, 2017
@justinmk justinmk added the has:repro issue contains minimal reproducing steps label Feb 26, 2017
@justinmk justinmk changed the title calling buf_get_lines() interrupts dd yy RPC call (K_EVENT input) completes pending operator (d, y, ...) Mar 10, 2017
@justinmk justinmk changed the title RPC call (K_EVENT input) completes pending operator (d, y, ...) RPC call (K_EVENT) completes pending operator (d, y, ...) Mar 10, 2017
@justinmk justinmk changed the title RPC call (K_EVENT) completes pending operator (d, y, ...) RPC call (K_EVENT) completes pending op (d, y, ...) Mar 10, 2017
justinmk added a commit to justinmk/neovim that referenced this issue Mar 14, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
justinmk added a commit to justinmk/neovim that referenced this issue Mar 14, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
justinmk added a commit to justinmk/neovim that referenced this issue Mar 15, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
@justinmk justinmk modified the milestones: 0.2.1, 0.2 Apr 22, 2017
justinmk added a commit to justinmk/neovim that referenced this issue Apr 26, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
justinmk added a commit to justinmk/neovim that referenced this issue Apr 27, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
justinmk added a commit to justinmk/neovim that referenced this issue Apr 28, 2017
Until now, asynchronous API functions never flush the input queue.
`nvim_get_mode()` is different:

- if RPCs are known to be blocked, it responds immediately (without
  flushing the input/event queue)
- else it flushes the input/event queue in order to determine whether
  pending input would cause RPCs to block, then responds with the result

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
`nvim_get_mode()` is just another asynchronous function.

In all cases `nvim_get_mode()` never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note that this doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166
if e.g. `d` is operator-pending.

Closes neovim#6159
justinmk added a commit to justinmk/neovim that referenced this issue Apr 28, 2017
Asynchronous API functions are served immediately, which means pending
input could change the state of Nvim shortly after an async API function
result is returned.

nvim_get_mode() is different:
  - If RPCs are known to be blocked, it responds immediately (without
    flushing the input/event queue)
  - else it is handled just-in-time before waiting for input, after
    pending input was processed. This makes the result more reliable
    (but not perfect).

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
nvim_get_mode() is just another asynchronous API function.

In all cases nvim_get_mode() never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note: This doesn't address neovim#6166; nvim_get_mode() will provoke neovim#6166 if
e.g. `d` is operator-pending.

Closes neovim#6159
@Shougo
Copy link
Contributor

Shougo commented May 16, 2017

Related problem: Shougo/deoplete.nvim#477

ale uses timer feature.

@phmarek
Copy link

phmarek commented Jun 6, 2017

Is this possibly also the reason why some plugin doesn't get invoked repeatedly?

Ie. one plugin does

    if mode() == 'i' || mode() == 'I' || mode() == 'r' || mode() == 'R'
        if bufname('%') != g:slimv_sldb_name && bufname('%') != g:slimv_inspect_name && bufname('%') != g:slimv_threads_name
            " Put '<Insert>' twice into the typeahead buffer, which should not do anything
            " just switch to replace/insert mode then back to insert/replace mode
            " But don't do this for readonly buffers
            call feedkeys("\<insert>\<insert>")
        endif
    else
        " Put an incomplete 'f' command and an Esc into the typeahead buffer
        call feedkeys("f\e", 'n')
    endif

but this doesn't work any more; I can call the function registered for CursorHold myself, but this automatic re-triggering doesn't run.
Might that be related?

@lunixbochs
Copy link
Author

lunixbochs commented Jul 9, 2017 via email

@justinmk
Copy link
Member

justinmk commented Jul 9, 2017

@lunixbochs @Chillee @phmarek I spoke too soon, :set timeout only helps the similar case where :term output provokes this symptom. It doesn't help for external RPC calls. I'm working on a fix.

@benley
Copy link

benley commented Jul 24, 2017

Searchable comment to maybe save someone 20 minutes of digging: YouCompleteMe seems to trigger this issue pretty reliably.

@tbodt
Copy link

tbodt commented Jul 28, 2017

This appears to also be triggered by a focus lost event, e.g. d followed by losing focus will cause a line to be deleted.

@gsf
Copy link

gsf commented Jul 28, 2017

@tbodt Thanks for that bit of info about losing focus, made me realize why I kept hitting this bug.

With neovim terminal running something like "top" in another window or tab, the current window will lose focus every time that buffer is updated, triggering this issue.

Update: Also happens even if the terminal buffer is hidden, not in another window or tab.

hardenedapple added a commit to hardenedapple/neovim that referenced this issue Sep 2, 2017
hardenedapple added a commit to hardenedapple/neovim that referenced this issue Sep 2, 2017
hardenedapple added a commit to hardenedapple/neovim that referenced this issue Sep 2, 2017
hardenedapple added a commit to hardenedapple/neovim that referenced this issue Sep 3, 2017
@justinmk justinmk reopened this Sep 4, 2017
@justinmk
Copy link
Member

justinmk commented Sep 4, 2017

#7230 resolves this issue for operator-pending mode. Note also that RPCs are not blocked during operator-pending mode. We've decided that's it better to allow that and fix any resulting problems. If you notice any crashes during operator-pending mode, please report it.

#7230 does not fix the problem for unfinished mappings (this is a different state than operator-pending mode). (Update: that's wrong; map-pending is also fine. However, RPCs will block during map-pending, currently. see ce852ba)

@justinmk
Copy link
Member

justinmk commented Sep 4, 2017

cc @lunixbochs @Chillee

@Chillee
Copy link

Chillee commented Sep 4, 2017

Nice! What's the easiest way of getting the most up to date neovim version?

@justinmk
Copy link
Member

justinmk commented Sep 4, 2017

The releases page: https://github.com/neovim/neovim/releases

Or build from source.

nateozem pushed a commit to nateozem/neovim that referenced this issue Oct 5, 2017
normal_finish_command() and normal_prepare() assume that any pending
operator needs to be finished after any subsequent key.

Set `finish_op = false` in nv_event() to indicate that the pending
operator shouldn't be finished in normal_execute().

This is how nv_visual() indicates that 'v' or 'V' in operator-pending
mode should not finish the current pending operator.

fixes neovim#5398
fixes neovim#6166 (partially; mappings are still interrupted)
nateozem pushed a commit to nateozem/neovim that referenced this issue Oct 5, 2017
The "mapping" tests added in 541dde3 were flawed:
- Unlike op-pending mode, RPCs are _blocked_ during map-pending. So
  a synchronous RPC like nvim_get_current_buf() waits until
  'timeoutlen', then the mapping is canceled.
- helpers.expect() also performs a blocking RPC, so again that must not
  intervene the two nvim_input() calls.

closes neovim#6166
@carleryd
Copy link

carleryd commented Nov 8, 2017

I'm still getting this double delete behaviour after installing NeoVIM 0.2.1 which was just released.
The package that is causing problems for me is w0rp/ale but the over at dense-analysis/ale#967 wOrp says it's a bug with NeoVIM that should have been fixed.

$ nvim
NVIM v0.2.2-5-ge98bcf0
Build type: Release
LuaJIT 2.0.5

Am I not using the correct version? Is anyone else having these problems still?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api libnvim, Nvim RPC API bug issues reporting wrong behavior has:repro issue contains minimal reproducing steps rpc
Projects
None yet
Development

No branches or pull requests

9 participants