Emmet For Styled Components In Neovim

Emmet support for JSX is somewhat limited in Neovim as it is not able to easily distinguish between when it should provide JSX completions in render methods or CSS completions in styled components or similar CSS-in-JS libraries. If you only care about CSS completions and you are okay with a few config changes, you can make it work pretty easily with an entry_filter for your completions.

Before we add our filter, we first need to add some tree-sitter captures to identify styled component blocks. We can do this with the following code.

~/.config/nvim/queries/ecma/highlights.scm
; css`<css>`, keyframes`<css>`
(call_expression
  function: (identifier) @_name
    (#match? @_name "^(css|keyframes)")
  arguments: ((template_string) @styled
  (#offset! @styled 0 1 0 -1)))

; styled.div`<css>`
(call_expression
 function: (member_expression
   object: (identifier) @_name
     (#eq? @_name "styled"))
 arguments: ((template_string) @styled
  (#offset! @styled 0 1 0 -1)))

; styled(Component)`<css>`
(call_expression
 function: (call_expression
   function: (identifier) @_name
     (#eq? @_name "styled"))
 arguments: ((template_string) @styled
  (#offset! @styled 0 1 0 -1)))

With this in place, we can now update our nvim_lsp completion source to add an entry filter to only allow Emmet completions in CSS files or when inside of a styled tree-sitter capture group.

cmp.config.sources({
  {
    name = "nvim_lsp",
    entry_filter = function(entry)
      local client_name = entry.source.source.client.name

      -- Only return Emmet results in styled-component template strings
      return client_name ~= "emmet_language_server"
        or entry.context.filetype == "css"
        or context.in_treesitter_capture("styled")
    end,
  },
})