Skip to content

Nvim-cmp's keyword_pattern and Vimtex's Omnifunc #2786

@micangl

Description

@micangl

Description

Vimtex support in nvim-cmp is an issue which has been extensively discussed. These are just some of the related issues:

The problem explained in those issues dealt mainly with the Vimtex-provided neocomplete pattern (https://github.com/lervag/vimtex/blob/master/autoload/vimtex/re.vim#L35), why it didn't work, and why cite-completion wasn't triggered automatically (i.e. the completion menu appeared) as soon as a \cite{ statement was typed.

I may have found some workarounds and/or hints towards a possible solution. I'll explain them in order:

  1. Cite-completion isn't triggered automatically as soon as a \cite{ statement is issued. Differently from other engines, with nvim-cmp the user first has to type a character to have the menu pop up and show bilbiographic references. This is obviously annoying, as the user may not always remember, at the top of his head, exactly which reference to insert (or the key name). A workaround which I don't think was ever discussed is to use a trigger_character in nvim-cmp's configuration, as such:

    { name = "omni", trigger_characters = { "{" } },
  2. Correctness of the Vimtex-provided Neocomplete pattern. When the discussions in the previously linked issues were held, the validity of said pattern was questioned and, if I recall correctly, noone ever managed to make it work with nvim-cmp. The pattern, though, is indeed correct. This can be tested by opening the following file in Neovim and sourcing (:so):

    vim.g.regex =
             [=[\%(]=]
          .. [=[\v\\%(]=]
          .. [=[%(\a*cite|Cite)\a*\*?%(\s*\[[^]]*\]){0,2}\s*\{[^}]*]=]
          .. [=[|%(\a*cites|Cites)%(\s*\([^)]*\)){0,2}]=]
          ..    [=[%(%(\s*\[[^]]*\]){0,2}\s*\{[^}]*\})*]=]
          ..    [=[%(\s*\[[^]]*\]){0,2}\s*\{[^}]*]=]
          .. [=[|bibentry\s*\{[^}]*]=]
          .. [=[|%(text|block)cquote\*?%(\s*\[[^]]*\]){0,2}\s*\{[^}]*]=]
          .. [=[|%(for|hy)\w*cquote\*?\{[^}]*}%(\s*\[[^]]*\]){0,2}\s*\{[^}]*]=]
          .. [=[|defbibentryset\{[^}]*}\{[^}]*]=]
          .. [=[|\a*ref%(\s*\{[^}]*|range\s*\{[^,}]*%(}\{)?)]=]
          .. [=[|hyperref\s*\[[^]]*]=]
          .. [=[|includegraphics\*?%(\s*\[[^]]*\]){0,2}\s*\{[^}]*]=]
          .. [=[|%(include%(only)?|input|subfile)\s*\{[^}]*]=]
          .. [=[|([cpdr]?(gls|Gls|GLS)|acr|Acr|ACR)\a*\s*\{[^}]*]=]
          .. [=[|(ac|Ac|AC)\s*\{[^}]*]=]
          .. [=[|includepdf%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|includestandalone%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|%(usepackage|RequirePackage|PassOptionsToPackage)%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|documentclass%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|begin%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|end%(\s*\[[^]]*\])?\s*\{[^}]*]=]
          .. [=[|\a*]=]
          .. [=[)]=]
    local regex_1 = vim.regex(vim.g.regex)
    local match_str = [=[noise \cite{]=]
    
    vim.print(regex_1:match_str(match_str))
  3. And now the hardest issue: Making that pattern work with nvim-cmp. I've only partially achieved this goal, and I write this issue with the hope that someone more expert than me will be able to help.

    First thing, nvim-cmp modifies the keyword_pattern provided by the user:
    https://github.com/hrsh7th/nvim-cmp/blob/5dce1b778b85c717f6614e3f4da45e9f19f54435/lua/cmp/context.lua#L77

        return pattern.offset([[\%(]] .. keyword_pattern .. [[\)\m$]], self.cursor_before_line) or self.cursor.col

    This is necessary to ensure that only the last-typed text will be matched, as discussed in Custom completion source - completion popup not showing up hrsh7th/nvim-cmp#1273. The problem is that such a modification assumes to be in magic mode, whilst the keyword pattern we would be using set \v very-magic mode. This can be fixed by either adding a \m at the end of the pattern we pass to nvim-cmp, as such

     .. [=[|\a*]=]
     .. [=[)]=]
     .. [=[\m]=]

    or by correcting nvim-cmp's handling. I still haven't made a PR, but it should be like this:

        return pattern.offset([[\%(]] .. keyword_pattern .. [[\m\)$]], self.cursor_before_line) or self.cursor.col

    After making such an addition, nvim-cmp still doesn't match. It took me multiple hours of debugging but, by modifying cmp/source.lua

    +  local offset = ctx:get_offset(self:get_keyword_pattern()) + 1
    -  local offset = ctx:get_offset(self:get_keyword_pattern())

    nvim-cmp start finally to match correctly and show the completion menu. The issue I've found is that the completion menu, with such a modification, doesn't appear after typing a \cite{ statement. I've tried to troubleshoot this, but I'm not a programmer and the code in that nvim-cmp's function is just too complicated for me.

If someone who has the expertise would like to help, I'd be happy to contribute where I can. I honestly hope that we will, finally, be able to solve this stubborn issue.

Finally, sorry for bearing with me 'till the end of this message.

VimtexInfo

System info:
  OS: Linux 6.4.12-arch1-1
  Vim version: NVIM v0.9.1
  Has clientserver: true
  Servername: /run/user/1000/nvim.896581.0

VimTeX project: a
  base: a.tex
  root: /home/mike
  tex: /home/mike/a.tex
  main parser: current file verified
  document class: article
  compiler: latexmk
    engine: -pdf
    options:
      -verbose
      -file-line-error
      -synctex=1
      -interaction=nonstopmode
    callback: 1
    continuous: 1
    executable: latexmk
  viewer: Zathura
    xwin id: 0
  qf method: LaTeX logfile

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions