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

pynvim.api.nvim.NvimError: b'Index out of bounds' #1041

Closed
hackingcat opened this issue Dec 15, 2018 · 6 comments · May be fixed by #1562
Closed

pynvim.api.nvim.NvimError: b'Index out of bounds' #1041

hackingcat opened this issue Dec 15, 2018 · 6 comments · May be fixed by #1562

Comments

@hackingcat
Copy link

hackingcat commented Dec 15, 2018

Following is the full stack trace:

Traceback (most recent call last):
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/err_to_scratch_buffer.py", line 16, in wrapper
    return func(self, *args, **kwds)
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/snippet_manager.py", line 339, in _cursor_moved
    self._active_snippets[0].update_textobjects(_vim.buf)
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/text_objects/_snippet_instance.py", line 80, in update_textobjects
    if obj._update(done, buf):
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/text_objects/_mirror.py", line 19, in _update
    self.overwrite(buf, '')
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/text_objects/_base.py", line 126, in overwrite
    if self.current_text == gtext:
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/text_objects/_base.py", line 96, in current_text
    return _vim.buf[self._start.line][self._start.col:self._end.col]
  File "/home/pittcat/.vim/plugged/ultisnips/pythonx/UltiSnips/_vim.py", line 25, in __getitem__
    rv = vim.current.buffer[idx]
  File "/usr/lib/python3.7/site-packages/pynvim/api/buffer.py", line 45, in __getitem__
    return self.request('nvim_buf_get_lines', i, i + 1, True)[0]
  File "/usr/lib/python3.7/site-packages/pynvim/api/common.py", line 51, in request
    return self._session.request(name, self, *args, **kwargs)
  File "/usr/lib/python3.7/site-packages/pynvim/api/nvim.py", line 182, in request
    res = self._session.request(name, *args, **kwargs)
  File "/usr/lib/python3.7/site-packages/pynvim/msgpack_rpc/session.py", line 102, in request
    raise self.error_wrapper(err)
pynvim.api.nvim.NvimError: b'Index out of bounds'
@SirVer
Copy link
Owner

SirVer commented Oct 30, 2019

too little information to reproduce the problem. When does this happen? What is your vimrc? How do you trigger the traceback?

@SirVer SirVer closed this as completed Oct 30, 2019
@danihodovic
Copy link

danihodovic commented Jan 21, 2020

This happens because the vim object that's injected in the python context does not behave like the Neovim vim object does.

Iterating through the buffer in Ultisnips fails, while it succeeds using the Neovim API (outside of the Ultisnips conext).

This call fails

  for line in snip.buffer:
      vim.command(f"echom '{line}'")

This call succeeds:

  for line in snip.buffer[0:-1]:
      vim.command(f"echom '{line}'")

Snippet globals

global !p
import px.snippets

def iterate_buffer():
    for line in snip.buffer:
        pass
endglobal

Snippet

post_jump "iterate_buffer()"
snippet empty ""
endsnippet

Error log

An error occured. This is either a bug in UltiSnips or a bug in a
snippet definition. If you think this is a bug, please report it to
https://github.com/SirVer/ultisnips/issues/new
Please read and follow:
https://github.com/SirVer/ultisnips/blob/master/CONTRIBUTING.md#reproducing-bugs

Following is the full stack trace:
Traceback (most recent call last):
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/err_to_scratch_buffer.py", line 18, in wrapper
    return func(self, *args, **kwds)
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet_manager.py", line 171, in expand
    if not self._try_expand():
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet_manager.py", line 804, in _try_expand
    self._do_snippet(snippet, before)
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet_manager.py", line 776, in _do_snippet
    self._jump(JumpDirection.FORWARD)
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet_manager.py", line 614, in _jump
    snippet_for_action,
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet/definition/base.py", line 438, in do_post_jump
    self._actions["post_jump"], current_snippet.context, locals
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet/definition/base.py", line 200, in _execute_action
    snip = self._eval_code(action, locals)
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/snippet/definition/base.py", line 182, in _eval_code
    exec(code, {"snip": snip})
  File "<string>", line 21, in <module>
  File "<string>", line 19, in iterate_buffer
  File "/home/dani/.config/nvim/plugged/ultisnips/pythonx/UltiSnips/buffer_proxy.py", line 124, in __getitem__
    return self._buffer[key]
  File "/home/dani/.local/lib/python3.6/site-packages/pynvim/api/buffer.py", line 45, in __getitem__
    return self.request('nvim_buf_get_lines', i, i + 1, True)[0]
  File "/home/dani/.local/lib/python3.6/site-packages/pynvim/api/common.py", line 58, in request
    return self._session.request(name, self, *args, **kwargs)
  File "/home/dani/.local/lib/python3.6/site-packages/pynvim/api/nvim.py", line 182, in request
    res = self._session.request(name, *args, **kwargs)
  File "/home/dani/.local/lib/python3.6/site-packages/pynvim/msgpack_rpc/session.py", line 102, in request
    raise self.error_wrapper(err)
pynvim.api.common.NvimError: Index out of bounds

Snippet, caused error:

  Defined in: /home/dani/.config/nvim/plugged/vim-snippets/python/python.django_drf.snippets:98
  Trigger: empty
  Description: ""
  Context: <none>
  Pre-expand: <none>
  Post-expand: <none>

Executed snippet code:
  1   import re, os, vim, string, random
  2   import px.snippets
  3
  4   # def add_line_if_not_exist(stmt):
  5       # buffer = snip.buffer
  6       # print(snip.buffer[0])
  7       # buffer.append(dir(buffer))
  8       # for line in buffer[-1]:
  9           # pass
 10           # vim.command(f"echom '{line}'")
 11           # pass
 12       # exists = next((line for line in buffer if stmt in line), None)
 13       # if not exists:
 14       #     buffer.append(stmt)
 15
 16   def iterate_buffer():
 17     # vim.command(f"echom '{len(snip.buffer)}'")
 18     # snip.buffer
 19     for line in snip.buffer:
 20         vim.command(f"echom '{line}'")
 21   iterate_buffer()

@danihodovic
Copy link

I worked around this by using the vim buffer directly and tricking the Ultisnips wrapper that throws errors when you touch the vim buffer

Add the following code at the end of your python snippet helper

snip.buffer._change_tick = int(vim.eval("b:changedtick"))

@SirVer
Copy link
Owner

SirVer commented Jan 22, 2020

@danihodovic So this is Neovim only then? I cannot help then.

This issue was closed due to it only affecting Neovim and not core Vim.

Why is Neovim only best-effort?

UltiSnips maintenance is a lot of work. Reproducing bug reports is tedious, slow and error prone. The current maintainers have to limit their scope to provide a reasonable level of service to the community.

Neovim should work with any plugin that core vim works with. But in the past, Neovim support has been difficult for UltiSnips: It regularly had bugs that did not affect the core Vim distributions, i.e. Vim, gVim, MacVim, and Vim for Windows and its testing approach required a completely separate code path from core as well. Therefore, currently Neovim bugs are considered on a best effort basis.

UltiSnips is looking for an additional maintainer that is interested in bringing the Neovim level of service on par with core Vim. If you are interested in helping out, please reach out to SirVer.

@danihodovic
Copy link

danihodovic commented Jan 22, 2020

Yeah and that's understandable.

For future reference the workaround I use is to modify vim buffer object directly.

Using a decorator to bypass the error Ultisnips throws when modifying the buffer directly

def bypass_ultisnips_error(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        snip.buffer._change_tick = int(vim.eval("b:changedtick"))
        return result

    return wrapper

Usage

@bypass_ultisnips_error
def clean_imports():
    buffer = vim.current.buffer
    # modify buffer

An example snippet with Python post_jump

post_jump "clean_imports()"
snippet mysnippet "class S(serializers.Serializer)"
endsnippet

@idbrii
Copy link
Contributor

idbrii commented Jan 6, 2025

This is easy to reproduce. Edit ~/.vim/pythonx/test.py and paste:

import vim
import UltiSnips.vim_helper as helper

def dump(b):
    for line in b:
        print(line.upper())

# This works fine in both nvim and vim.
dump(vim.current.buffer)

# In nvim, the iteration fails with "pynvim.api.common.NvimError: Index out of bounds"
dump(helper.VimBuffer())

Run :pyxfile %. In vim both are fine, but in nvim it crashes. Top of the error callstack:

Traceback (most recent call last):
  File "~\vim\pythonx\test.py", line 14, in <module>
    dump(helper.VimBuffer())
  File "~\vim\pythonx\test.py", line 7, in dump
    for line in b:
  File "~\vim\bundle\ultisnips\pythonx\UltiSnips\vim_helper.py", line 23, in __getitem__
    return vim.current.buffer[idx]
           ~~~~~~~~~~~~~~~~~~^^^^^

Seems like VimBuffer implements iteration just with __getitem__ and nvim's api doesn't correctly return IndexError? (I'm not familiar with this pattern.)

Ah, seems this was reported long ago but not fixed portably: neovim/pynvim#128

idbrii added a commit to idbrii/ultisnips that referenced this issue Jan 6, 2025
Fix SirVer#1041: iterating over snip.buffer fails in neovim.

Use __iter__() to defer iteration of a buffer to the underlying object.

Test
* Run this code in neovim and vim:
    import vim
    import UltiSnips.vim_helper as helper

    def dump(b):
        for line in b:
            print(line.upper())

    # This works fine in both nvim and vim.
    dump(vim.current.buffer)

    # In nvim, the iteration previously failed with "pynvim.api.common.NvimError: Index out of bounds"
    dump(helper.VimBuffer())
idbrii added a commit to idbrii/ultisnips that referenced this issue Jan 8, 2025
Fix SirVer#1041: iterating over snip.buffer fails in neovim.

Use __iter__() to defer iteration of a buffer to the underlying object.

Test
* Run this code in neovim and vim:
    import vim
    import UltiSnips.vim_helper as helper

    def dump(b):
        for line in b:
            print(line.upper())

    # This works fine in both nvim and vim.
    dump(vim.current.buffer)

    # In nvim, the iteration previously failed with "pynvim.api.common.NvimError: Index out of bounds"
    dump(helper.VimBuffer())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants