hrsh7th / nvim-cmp

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

TailwindCSS performance issues #1828

Open mortezadadgar opened 9 months ago

mortezadadgar commented 9 months ago

FAQ

Announcement

Minimal reproducible full config

if has('vim_starting')
  set encoding=utf-8
endif
scriptencoding utf-8

if &compatible
  set nocompatible
endif

let s:plug_dir = expand('/tmp/plugged/vim-plug')
if !filereadable(s:plug_dir .. '/plug.vim')
  execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)
end

execute 'set runtimepath+=' . s:plug_dir
call plug#begin(s:plug_dir)
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/vim-vsnip'
Plug 'neovim/nvim-lspconfig'
call plug#end()
PlugInstall | quit

" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
  snippet = {
    expand = function(args)
      vim.fn["vsnip#anonymous"](args.body)
    end,
  },

  mapping = {
    ['<CR>'] = cmp.mapping.confirm({ select = true })
  },

  sources = cmp.config.sources({
    { name = "nvim_lsp" },
    { name = "buffer" },
  }),
}
EOF

lua << EOF
local capabilities = require('cmp_nvim_lsp').default_capabilities()

require'lspconfig'.tailwindcss.setup {
  capabilities = capabilities,
}
EOF

Description

I know https://github.com/hrsh7th/nvim-cmp/pull/1583 and https://github.com/hrsh7th/nvim-cmp/pull/1574 has improved the performance of tailwindcss completions but I think performance still is not on pair with programs such as vscode.

the issue is apparent after inserting a new space after the bg-white class in recorded video
https://github.com/hrsh7th/nvim-cmp/assets/27911493/5aca7637-f5c6-4264-9bfb-99d9ee1008d6

recorded another video to better illustrate it https://github.com/hrsh7th/nvim-cmp/assets/27911493/5492540e-901a-43cf-9e27-0c0688443fc2

Steps to reproduce

initialize tailwindcss in any web development project and type in a html tag class.

Expected behavior

to be butter smooth.

Actual behavior

described in description section.

Additional context

No response

dan-cooke commented 9 months ago

Yep I am still experiencing major lag with tailwind LSP installed, I have resorted to removing it. LSP zero config

local lsp = require('lsp-zero').preset({
})

local lspconfig = require('lspconfig')

lsp.ensure_installed({
    'tsserver',
    'eslint',
    'rust_analyzer',
})

lsp.on_attach(function(client, bufnr)
    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities.textDocument.completion.completionItem.snippetSupport = true
    lsp.default_keymaps({ buffer = bufnr })
    lsp.buffer_autoformat()
end)

vim.keymap.set("n", "gr", ":lua vim.lsp.buf.rename() <cr> :wall <cr>")
vim.keymap.set("n", "<leader>g", ":lua vim.lsp.buf.hover() <cr>")
vim.keymap.set("n", "<leader>]", ":lua vim.diagnostic.goto_next() <cr>")
vim.keymap.set("n", "<leader>[", ":lua vim.diagnostic.goto_prev() <cr>")
vim.keymap.set("n", "gh", ":lua vim.diagnostic.open_float() <cr>")
vim.keymap.set("n", "gd", ":lua vim.lsp.buf.definition() <cr>")
vim.diagnostic.config({
    virtual_text = false,
    root_dir = require("lspconfig.util").root_pattern(".git")
})

lsp.format_on_save({
    format_opts = {
        async = false,
        timeout_ms = 10000,
    },
    servers = {
        ['rust_analyzer'] = { 'rust' },
        ['prettier'] = { 'css', 'html', 'javascript', 'javascriptreact', 'json', 'less', 'scss', 'typescript', 'typescriptreact', 'vue' },
    }
})

lsp.set_server_config({
    on_init = function(client)
        client.server_capabilities.semanticTokensProvider = nil
    end,
})

lsp.set_sign_icons({
    error = '✘',
    warn = '▲',
    hint = '⚑',
    info = '»'
})

lspconfig.tsserver.setup({
    root_dir = require("lspconfig.util").root_pattern(".git"),
    flags = {
        allow_incremental_sync = false,
        debounce_text_changes = 500,
    },
})

lsp.setup()

nvim-cmp config

local cmp = require('cmp')
local lspkind = require('lspkind')

cmp.setup({
    mapping = {
        ['<CR>'] = cmp.mapping.confirm({ select = false }),
        ['<C-c>'] = cmp.mapping.complete(),
        ['<C-e>'] = cmp.mapping(function(fallback)
            if cmp.visible() then
                cmp.abort()
            else
                fallback()
            end
        end, { 'i', 'c' }),
        ["<Tab>"] = cmp.mapping(function(fallback)
            if cmp.visible() then
                cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
            else
                fallback()
            end
        end),
        ['<S-Tab>'] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
    },

    formatting = {
        format = lspkind.cmp_format({
            mode = "symbol_text",
            max_width = 50,
            symbol_map = { Copilot = "" }
        })
    },

    experimental = {
        ghost_text = true,
    },

    snippet = {
        -- REQUIRED - you must specify a snippet engine
        expand = function(args)
            require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
            -- require('snippy').expand_snippet(args.body) -- For `snippy` users.
            -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users.
        end,
    },

    sources = {
        -- Other Sources
        -- { name = "copilot",  group_index = 2 },
        { name = "nvim_lsp", group_index = 2, keyword_length = 6 },
        { name = "path",     group_index = 2 },
        { name = "luasnip",  group_index = 2 },
    },
})

Ram usage has got has high as 99% with tailwind LSP enabled, without it im always sitting around 30% with Firefox open

Shougo commented 9 months ago

Hm.... But this is tailscale LS problem.

mortezadadgar commented 9 months ago

@Shougo but mini.completion and vscode doesn't suffer from this issue

Shougo commented 9 months ago

I think mini.completion does not use fuzzy match. If the items are very big from LS, nvim-cmp fuzzy match is very slow.

Shougo commented 9 months ago

I have tested it. Hm. It seems slow.

Its slowness is from:

Shougo commented 9 months ago

You can change keyword_length and disable fuzzy match.

https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt#L493

https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt#L539

Shougo commented 9 months ago

It is better for me.

" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
  snippet = {
    expand = function(args)
      vim.fn["vsnip#anonymous"](args.body)
    end,
  },

  mapping = {
    ['<CR>'] = cmp.mapping.confirm({ select = true })
  },

  matching = {
    disallow_partial_matching = true,
    disallow_fuzzy_matching = true,
    disallow_prefix_unmatching = true,
  },

  sources = cmp.config.sources({
    { name = "nvim_lsp", keyword_length = 2 },
    { name = "buffer" },
  }),
}
EOF

lua << EOF
local capabilities = require('cmp_nvim_lsp').default_capabilities()

require'lspconfig'.tailwindcss.setup {
  capabilities = capabilities,
}
EOF
dan-cooke commented 9 months ago

@Shougo i will try disabling these settings and get back to you, my keyword length was 6 though and still very slow

dan-cooke commented 9 months ago

@Shougo yeah that's faster but now it just a really subpar experience in my opinion.

Shougo commented 9 months ago
source.complete = function(self, params, callback)
  local lsp_params = vim.lsp.util.make_position_params(0, self.client.offset_encoding)
  lsp_params.context = {}
  lsp_params.context.triggerKind = params.completion_context.triggerKind
  lsp_params.context.triggerCharacter = params.completion_context.triggerCharacter
  self:_request('textDocument/completion', lsp_params, function(_, response)
    vim.print(response)
    callback(response)
  end)
end

I have checked response. The response is not filtered by LS and the size is very huge.

    }, {
      kind = 21,
      label = "forced-color-adjust-none",
      sortText = "11282"
    } }

11282 items are returned. It is slow for Lua.

mortezadadgar commented 9 months ago

yeah nvim-cmp to filter and sort all those responses primary reason is that tailwindcss triggers completion on spaces which send the maximum number of completion items; removing space from triggerCharacters can significantly improves performance

vim.api.nvim_create_autocmd("LspAttach", {
    group = vim.api.nvim_create_augroup("UserLspConfig", {}),
    callback = function(ev)
        for _, client in pairs((vim.lsp.get_clients {})) do
            if client.name == "tailwindcss" then
                client.server_capabilities.completionProvider.triggerCharacters =
                    { '"', "'", "`", ".", "(", "[", "!", "/", ":" }
            end
        end
    end,
})