hrsh7th / nvim-cmp

A completion plugin for neovim coded in Lua.
MIT License
7.78k stars 385 forks source link

Is it possible to have context-aware sources? #806

Open norseghost opened 2 years ago

norseghost commented 2 years ago

With the context support that was added recently, it is now possible to disable completion in say, comments. Awesome!

I want to have some sources in comments; and some outside. I tried creating a function that checked current context and set sources appropriately; and thought it worked - but this didn’t update on context change while editing:


-- cmp setup lua file
local context = require("cmp.config.context")
local set_sources = function()
    local s = {}
    if context.in_syntax_group('Comment') or context.in_treesitter_capture('comment') then
        s = {
            -- comment sources
        }
    else
        s = {
            -- other sources
        }
    end
    return s
end

cmp.setup({
    -- other setup
    sources = set_sources(),
)}

ideally; I’d like to pass a function directly to cmp.setup.sources. Could I hack around this with an InsertEnter autocommand and cmp.setup.buffer maybe?

st3fan commented 2 years ago

This would be really nice. I generally do not care about most sources in comments, but some may be relevant.

bennypowers commented 2 years ago

x-post from https://github.com/hrsh7th/cmp-nvim-lsp/issues/20#issuecomment-1175254858

Take this javascript source:

import { html, render } from 'lit-html';

render(html`
  <p>I'm a little teapot</p>

`, document.body);

With the cursor on the line below the <p> tag, I'd like to receive emmet completions from emmet_ls, but when the cursor is on the line beneath the import statement, I certainly would not like to receive emmet completions.

I would like the ability to filter lsp snippets by treesitter context.

Could SourceConfig also take an enabled predicate?

kkharji commented 2 years ago

x-post from hrsh7th/cmp-nvim-lsp#20 (comment)

Take this javascript source:

import { html, render } from 'lit-html';

render(html`
  <p>I'm a little teapot</p>

`, document.body);

With the cursor on the line below the <p> tag, I'd like to receive emmet completions from emmet_ls, but when the cursor is on the line beneath the import statement, I certainly would not like to receive emmet completions.

I would like the ability to filter lsp snippets by treesitter context.

Could SourceConfig also take an enabled predicate?

with #1067 you can do

function(entry, _)
      local kinds = require("cmp.types").lsp.CompletionItemKind
      local in_capture = require("cmp.config.context").in_treesitter_capture
      if kinds[entry:get_kind()] == "Snippet" then
        local name = vim.split(entry.source:get_debug_name(), ":")[2]
        if name == "emmet_ls" then
          return not in_capture "jsx_text"
        end
      end
    end

took sometime but was bothering me as well. This works for jsx/tsx, but I'm sure there would be something you can check to know you are inhtml

bennypowers commented 2 years ago

With this query in ~/.config/nvim/after/queries/typescript/lit_html.scm

(call_expression
 function: ((identifier) @_name
   (#eq? @_name "html"))
 arguments: ((template_string) @lit_html))

and this filter function:

local JS_CONFIG = {
  sources = cmp.config.sources({
    { name = 'nvim_lsp', filter = function(entry, _)
      local kinds = require 'cmp.types'.lsp.CompletionItemKind
      if (kinds[entry:get_kind()] == 'Snippet'
          and entry.source:get_debug_name() == 'nvim_lsp:emmet_ls') then
        local parser = vim.treesitter.get_parser(0, 'typescript')
        local query = vim.treesitter.query.get_query('typescript', 'lit_html')
        local tree = parser:parse()[1]

        local row = unpack(vim.api.nvim_win_get_cursor(0))
        local caps = {}
        for id, node, meta in query:iter_captures(tree:root(), 0, row - 1, row) do
          local name = query.captures[id]
          table.insert(caps, name)
        end
        if vim.tbl_contains(caps, 'lit_html') then
          return true
        end
      else
        return false
      end
    end },
    { name = 'nvim_lsp_signature_help' },
  }, {
    { name = 'luasnip', option = { use_show_condition = false } },
  }, {
    { name = 'treesitter' },
    { name = 'buffer', keyword_length = 3 },
  }, {
    { name = 'calc' },
    { name = 'emoji' },
  })
}
cmp.setup.filetype('javascript', JS_CONFIG)
cmp.setup.filetype('typescript', JS_CONFIG)

I've achieved my goal