blink.cmp icon indicating copy to clipboard operation
blink.cmp copied to clipboard

Lag and stuttering when using Tailwindcss LSP

Open Aasim-A opened this issue 7 months ago • 8 comments

Make sure you have done the following

  • [x] Updated to the latest version of blink.cmp
  • [x] Searched for existing issues and documentation (try <C-k> on https://cmp.saghen.dev)

Bug Description

The ui gets laggy when editing html classes when the taiwindcss lsp is active. This was a known issue in nvim-cmp but was resolved somewhat recently. I tried to enable the async option for the lsp source and it helped but there still is lag and stuttering.

The video shows that when typing, the editor lags and then shows the typed word all at once.

https://github.com/user-attachments/assets/9faff883-0856-4961-b93f-27abb3343130

Relevant configuration

sources = {
      default = {
        'lsp',
        'path',
        'snippets',
        'buffer',
        'markdown',
      },

      providers = {
        lsp = {
          name = 'LSP',
          module = 'blink.cmp.sources.lsp',
          async = true,
          score_offset = 5,
        },

        ripgrep = {
          module = 'blink-cmp-rg',
          name = 'Ripgrep',
          async = true,
          opts = {
            search_casing = '--smart-case',
          },
        },

        markdown = {
          name = 'RenderMarkdown',
          module = 'render-markdown.integ.blink',
          fallbacks = { 'lsp' },
        },
      },
    },

neovim version

v0.11.0

blink.cmp version

v1.2.0

Aasim-A avatar May 12 '25 01:05 Aasim-A

What's your system specs and what operating system are you on? Are you able to reproduce the lag in a repro.lua with just the LSP source enabled (if so, please send it over!)? It takes ~4ms to update the menu with tailwind on my system so it's quite unusual that it's locking up your whole editor.

saghen avatar May 13 '25 18:05 saghen

I'm running NixOS 24.11 and the version of tailwind lsp is 0.14.4. The version of tailwind used in the demo is ^4.

The issue is still present with the following config:

vim.env.LAZY_STDPATH = '.repro'
load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))()

---@diagnostic disable-next-line: missing-fields
require('lazy.minit').repro({
  spec = {
    {
      'saghen/blink.cmp',
      -- please test on `main` if possible
      -- otherwise, remove this line and set `version = '*'`
      -- build = 'cargo build --release',
      version = '*',
      opts = {
        sources = {
          default = {
            'lsp',
          },
        },
      },
    },
    {
      'neovim/nvim-lspconfig',
      config = function()
        require('lspconfig').tailwindcss.setup({
          capabilities = require('blink.cmp').get_lsp_capabilities(),
        })
      end,
    },
  },
})

Aasim-A avatar May 13 '25 19:05 Aasim-A

I have the same issue with the tailwindcss server

zrlf avatar May 18 '25 22:05 zrlf

Minimal repro:

vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

---@diagnostic disable-next-line: missing-fields
require("lazy.minit").repro({
	spec = {
		{
			"saghen/blink.cmp",
			-- please test on `main` if possible
			-- otherwise, remove this line and set `version = '*'`
			-- build = 'cargo build --release',
			version = "*",
			opts = {
				sources = {
					default = {
						"lsp",
					},
				},
			},
		},
		{
			"neovim/nvim-lspconfig",
			config = function()
				vim.lsp.config("tailwindcss", {
					root_dir = function(buf, cb)
						local name = vim.api.nvim_buf_get_name(buf)
						cb(vim.fs.dirname(name))
					end,
				})
				vim.lsp.enable("tailwindcss")
			end,
		},
		{
			"mason-org/mason.nvim",
			build = ":MasonUpdate",
			opts = {},
		},
		{
			"mason-org/mason-lspconfig.nvim",
			dependencies = { "mason.nvim", "nvim-lspconfig" },
			opts = {
				ensure_installed = {
					"tailwindcss",
				},
				automatic_enable = false,
			},
		},
	},
})
  1. git clone https://github.com/theleop/tailwind-minimal
  2. cd tailwind-minimal && npm i
  3. nvim --clean -u minimal.lua a.html "+12"
  4. ci"
  5. start typing bg-orange (or any other tailwind class name )

the blink.cmp UI will stutter

TheLeoP avatar May 24 '25 20:05 TheLeoP

A small profile using https://github.com/stevearc/profile.nvim with the following minimal configuration produces the following resutls

vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

---@diagnostic disable-next-line: missing-fields
require("lazy.minit").repro({
	spec = {
		{
			"saghen/blink.cmp",
			-- please test on `main` if possible
			-- otherwise, remove this line and set `version = '*'`
			-- build = 'cargo build --release',
			version = "*",
			opts = {
				sources = {
					default = {
						"lsp",
					},
				},
			},
		},
		{
			"neovim/nvim-lspconfig",
			config = function()
				vim.lsp.config("tailwindcss", {
					root_dir = function(buf, cb)
						local name = vim.api.nvim_buf_get_name(buf)
						cb(vim.fs.dirname(name))
					end,
				})
				vim.lsp.enable("tailwindcss")
			end,
		},
		{
			"mason-org/mason.nvim",
			build = ":MasonUpdate",
			opts = {},
		},
		{
			"mason-org/mason-lspconfig.nvim",
			dependencies = { "mason.nvim", "nvim-lspconfig" },
			opts = {
				ensure_installed = {
					"tailwindcss",
				},
				automatic_enable = false,
			},
		},
		{
			"stevearc/profile.nvim",
			config = function()
				local should_profile = vim.env.NVIM_PROFILE
				if not should_profile then
					return
				end

				require("profile").instrument_autocmds()
				if should_profile:lower():match("^start") then
					require("profile").start("blink*")
				else
					require("profile").instrument("blink*")
				end

				vim.keymap.set("n", "<f4>", function()
					local prof = require("profile")
					if prof.is_recording() then
						prof.stop()
						vim.ui.input(
							{ prompt = "Save profile to:", completion = "file", default = "profile.json" },
							function(filename)
								if filename then
									prof.export(filename)
									vim.notify(("Wrote %s"):format(filename))
								end
							end
						)
					else
						vim.notify("Starting recording")
						prof.start("blink*")
					end
				end)
			end,
		},
	},
})

Image

After hiding everything under blink.cmp.lib.utils.schedule_if_needed (which I suspect are simply callback waiting and not actual blocking calls), the graph looks like

Image

The first big chunk is blink.cmp.completion.list.fuzzy taking 464 ms. Another big chunk of time is the 240 ms taken by blink.cmp.sources.lsp.hacks.tailwind.process_response

TheLeoP avatar May 24 '25 20:05 TheLeoP

Here's a video where the stutter can be seen. It's more noticeable when autocompleting classes that have a color related do them

https://github.com/user-attachments/assets/c7544683-4d95-4d4c-a2fa-d62349d6248e

TheLeoP avatar May 24 '25 20:05 TheLeoP

I have the same issue. I use latest blink.cmp (1.3.1), latest nvim (0.11.1) and it's running on MacBook Pro M3 Max with latest Macos 15.5.

I believe that I didn't have this issue with nvim-cmp.

artem-simutin avatar May 24 '25 21:05 artem-simutin

Same here, I just migrated to blink.cmp and I'm facing the same issue

rmenai avatar May 25 '25 21:05 rmenai

Make sure you have done the following

* [x]  Updated to the latest version of `blink.cmp`[x]  Searched for existing issues and documentation (try `<C-k>` on https://cmp.saghen.dev)

Bug Description

The ui gets laggy when editing html classes when the taiwindcss lsp is active. This was a known issue in nvim-cmp but was resolved somewhat recently. I tried to enable the async option for the lsp source and it helped but there still is lag and stuttering.

The video shows that when typing, the editor lags and then shows the typed word all at once. output.mp4

Relevant configuration

sources = { default = { 'lsp', 'path', 'snippets', 'buffer', 'markdown', },

  providers = {
    lsp = {
      name = 'LSP',
      module = 'blink.cmp.sources.lsp',
      async = true,
      score_offset = 5,
    },

    ripgrep = {
      module = 'blink-cmp-rg',
      name = 'Ripgrep',
      async = true,
      opts = {
        search_casing = '--smart-case',
      },
    },

    markdown = {
      name = 'RenderMarkdown',
      module = 'render-markdown.integ.blink',
      fallbacks = { 'lsp' },
    },
  },
},

neovim version

v0.11.0

blink.cmp version

v1.2.0

How did you make tailwind render correctly the colors?

Mine renders all as red

Image Image

hrqmonteirodev avatar Jun 01 '25 01:06 hrqmonteirodev

@TheLeoP The schedule_if_needed calls taking a long time appears to have been real, as we were passing the items field in the event data. The last remaining bit to optimize is this process_response it seems.

saghen avatar Jun 20 '25 13:06 saghen

@Saghen OMG, yes, that was it. I just tested the latest main version (with a minimal.lua similar to the one on https://github.com/Saghen/blink.cmp/pull/1830#issuecomment-2923900014 , changing the branch to main) and there is no stuttering at all. Thank you so much for everything :D.

For the sake of completeness, this is what a flamegraph of two completions (as in typing the start of a word, going through the list on completions and selecting one of them, twice) using the same minimal setup I described in previous comments (but with the latest version of main) seems like

Image

Yes, it does look like process_response could be improved. But, at least on my computer, it feels instant to use tailwind-lsp completion now. Once again, thanks.

From my point of view, this issue could be closed.

TheLeoP avatar Jun 20 '25 14:06 TheLeoP

Sweet, thanks for testing!

saghen avatar Jun 20 '25 14:06 saghen

Can confirm that it's fixed. Thanks a lot @Saghen for taking the time to look into this :)

Aasim-A avatar Jun 20 '25 15:06 Aasim-A

Sorry to be reviving this thread, I don't really understand in what version this was fixed. At least I am still experiencing this issue. The blink-cmp version in nixpkgs seems to be 1.4.1.

@Aasim-A are you adding it any differently from this to the plugins?

      {
        plugin = blink-cmp;
        type = "lua";
        config = "require('blink.cmp').setup()";
      }

Elias-Graf avatar Jul 07 '25 18:07 Elias-Graf

@Elias-Graf It'll be released in v1.5.0

saghen avatar Jul 07 '25 19:07 saghen

@Elias-Graf Since you're using nix, if you want to use the patched version right now, you can add this to your lazy config to build from source:

build = 'nix run .#build-plugin',

Aasim-A avatar Jul 07 '25 21:07 Aasim-A