Make nvim-cmp show full function help again instead of signature only

For some time now nvim-cmp autocomplete only gives me a short signature popup instead of the full help for a function. I think this has changed recently.

I now get this:

I would prefer if it would show me the full help text (i.e. what I get from lua vim.lsp.buf.hover()):

It’s been some time since I configured all this. And as there are so many moving parts involved (Nvim LSP, nvim-lspconfig, nvim-cmp, cmp-nvim-lsp, intelephense, …) I feel a bit lost as to where I would configure this.

Any help would be much appreciated.

Here the relevant parts from my config:

" LSP (Intellisense / Autocomplete)
Plug 'neovim/nvim-lspconfig'        " Start LSP server on demand
Plug 'hrsh7th/nvim-cmp'             " Autocomplete
Plug 'ray-x/lsp_signature.nvim'     " Function signature popup
Plug 'hrsh7th/vim-vsnip'            " Snippets plugin
Plug 'hrsh7th/cmp-buffer'           " Buffer word completion source for nvim-cmp
Plug 'hrsh7th/cmp-nvim-lsp'         " LSP completion source for nvim-cmp
Plug 'hrsh7th/cmp-nvim-lua'         " LUA completion source for nvim-cmp
Plug 'hrsh7th/cmp-path'             " Path completion source for nvim-cmp
Plug 'hrsh7th/cmp-vsnip'            " vsnip completion source for nvim-cmp
Plug 'rafamadriz/friendly-snippets', { 'branch': 'main' } " Snippets collection

lua <<EOF
  local cmp = require'cmp'

  -- Check if there's a word before the cursor (used by <TAB> mapping)
  local has_words_before = function()
    local line, col = unpack(vim.api.nvim_win_get_cursor(0))
    return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
  end

  -- Send feed keys with special codes (used by <S-TAB> mapping)
  local feedkey = function(key, mode)
    vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
  end

  cmp.setup({
    snippet = {
      expand = function(args)
        vim.fn["vsnip#anonymous"](args.body)
      end,
    },
    mapping = {
      ['<C-d>'] = cmp.mapping.scroll_docs(-4),
      ['<C-f>'] = cmp.mapping.scroll_docs(4),
      ['<C-j>'] = cmp.mapping.select_next_item(),
      ['<C-k>'] = cmp.mapping.select_prev_item(),
      ['<C-Space>'] = cmp.mapping.complete(),
      ['<C-e>'] = cmp.mapping.abort(),
      ['<CR>'] = cmp.mapping.confirm({ select = false }),
      ["<Tab>"] = cmp.mapping(function(fallback)
        if cmp.visible() then
          cmp.select_next_item()
        elseif vim.fn["vsnip#available"]() == 1 then
          feedkey("<Plug>(vsnip-expand-or-jump)", "")
        elseif has_words_before() then
          cmp.complete()
        else
          fallback() -- The fallback function sends a already mapped key. In this case, it's probably `<Tab>`.
        end
      end, { "i", "s" }),
      ["<S-Tab>"] = cmp.mapping(function()
        if cmp.visible() then
          cmp.select_prev_item()
        elseif vim.fn["vsnip#jumpable"](-1) == 1 then
          feedkey("<Plug>(vsnip-jump-prev)", "")
        end
      end, { "i", "s" }),

    },
    sources = {
      { name = 'nvim_lsp' },
      { name = 'vsnip' },
      {
          name = 'buffer',
          option = {
            -- complete from all buffers
            get_bufnrs = function()
              return vim.api.nvim_list_bufs()
            end
          },
      },
      { name = 'nvim_lua' },
      { name = 'path' },
    },
    formatting = {
      format = require("lspkind").cmp_format({with_text = true, menu = ({
          buffer = "[Buffer]",
          nvim_lsp = "[LSP]",
          vsnip = "[Vsnip]",
          nvim_lua = "[Lua]",
          path = "[Path]",
        })}),
    },
  })
EOF

local lspconfig = require'lspconfig'
local signature = require'lsp_signature'
local cmp_nvim_lsp = require'cmp_nvim_lsp'

lspconfig.util.default_config = vim.tbl_extend(
  "force",
  lspconfig.util.default_config,
  {
    -- Required by nvim-cmp
    capabilities = cmp_nvim_lsp.update_capabilities(vim.lsp.protocol.make_client_capabilities()),

    -- Init signature plugin on_attach
    on_attach = function ()
      signature.on_attach({
        bind = true,
        handler_opts = {
          border = "single"
        }
      })
    end
  }
)

lspconfig.bashls.setup{}
lspconfig.ccls.setup{}
lspconfig.cssls.setup{}
lspconfig.html.setup {}
lspconfig.intelephense.setup{}
lspconfig.jsonls.setup{}
lspconfig.pyright.setup{}
lspconfig.tsserver.setup{}
lspconfig.vimls.setup{
  init_options = {
    runtimepath = vim.o.runtimepath
  }
}


I did some research and think that the issue has to do with completionItem/resolve as this is the action performed when a suggested completion item is selected:

		/**
		 * Indicates which properties a client can resolve lazily on a
		 * completion item. Before version 3.16.0 only the predefined properties
		 * `documentation` and `detail` could be resolved lazily.
		 *
		 * @since 3.16.0
		 */
		resolveSupport?: {
			/**
			 * The properties that a client can resolve lazily.
			 */
			properties: string[];
		};

From what I understand it’s the documentation property that contains what I’m looking for.

So I enabled LSP debugging (vim.lsp.set_log_level("debug")) and checked what Nvim negotiates with intelephense.

Neovim seems to send the correct capabilites:

[DEBUG][2022-01-11 15:10:55] .../vim/lsp/rpc.lua:344	"rpc.send"
{
  id = 1,
  jsonrpc = "2.0",
  method = "initialize",
  params = {
    capabilities = {
       [...]
        completion = {
          completionItem = {
            [...]
            resolveSupport = {
              properties = { "documentation", "detail", "additionalTextEdits" }
            },

But the response for completionItem/resolve does not contain documentation only detail:

[DEBUG][2022-01-11 15:41:27] .../vim/lsp/rpc.lua:344	"rpc.send"	{
  id = 6,
  jsonrpc = "2.0",
  method = "completionItem/resolve",
  params = {
    command = {
      command = "editor.action.triggerParameterHints",
      title = "Trigger Parameter Hints"
    },
    data = 7.4524247489258e+15,
    detail = "array_map(?callable $callback, array $array, array ...$arrays): array",
    filterText = "array_map",
    insertTextFormat = 2,
    kind = 3,
    label = "array_map",
    sortText = "02221",
    textEdit = {
      newText = "array_map($0)",
      range = {
        end = {
          character = 7,
          line = 1
        },
        start = {
          character = 0,
          line = 1
        }
      }
    }
  }}
[DEBUG][2022-01-11 15:41:27] .../vim/lsp/rpc.lua:451	"rpc.receive"	{
  id = 6,
  jsonrpc = "2.0",
  result = {
    command = {
      command = "editor.action.triggerParameterHints",
      title = "Trigger Parameter Hints"
    },
    data = 7.4524247489258e+15,
    detail = "array_map(?callable $callback, array $array, array ...$arrays): array",
    filterText = "array_map",
    insertTextFormat = 2,
    kind = 3,
    label = "array_map",
    sortText = "02221",
    textEdit = {
      newText = "array_map($0)",
      range = {
        end = {
          character = 7,
          line = 1
        },
        start = {
          character = 0,
          line = 1
        }
      }
    }
  }}

So is this a bug with intelephense?

On the other hand I also tried with a javascript document and it also only shows me the signatures there, not the documentation. I’m not sure if this ever worked, though.

I’ve opened a bug report with intelephense now:

Any hints on what I could try to further debug this still appreciated.

Ok, still talking to myself here but whatever: The issue started with Neovim 0.6.0. I’ve now also opened an issue there (and will later probably close the intelephense issue after the author has taken notice of it).

Posting here for others: