Skip to content

Neovim completion is not working in monorepos #1338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tomaszsympi opened this issue Apr 28, 2025 · 8 comments
Open

Neovim completion is not working in monorepos #1338

tomaszsympi opened this issue Apr 28, 2025 · 8 comments
Assignees

Comments

@tomaszsympi
Copy link

I’ve discovered that the TailwindCSS LSP picks its root_dir from the first package I open which contains tailwind.config.ts file —so when I jump into a different package in my monorepo, I lose all completions until I manually restart the server.

To work around this, I’ve hooked into BufEnter/InsertEnter and written a tiny utility that:

  1. Finds the nearest tailwind.config.ts for the current buffer
  2. Compares it to the active LSP client’s root_dir
  3. Stops & restarts tailwindcss LSP if the root_dir has changed
vim.api.nvim_create_autocmd({ "BufEnter", "InsertEnter" }, {
  pattern = "*.tsx",
  callback = require("utils.tailwind_lsp").restart,
})
-- utils/tailwind_lsp.lua
local M = {}

function M.restart()
  local buf = vim.api.nvim_get_current_buf()
  local clients = vim.lsp.get_clients({ bufnr = buf, name = "tailwindcss" })
  local lspconfig_tailwind = require("lspconfig.configs.tailwindcss")

  -- Get current file's path and detect new root
  local current_file = vim.api.nvim_buf_get_name(buf)
  local new_root = lspconfig_tailwind.default_config.root_dir(current_file)

  -- Check if tailwindcss is not attached to the buffer
  if #clients == 0 then
    vim.cmd("LspStart tailwindcss")
    return
  end

  local client = clients[1]

  if client.config.root_dir == new_root then
    return
  end

  client.stop()

  vim.defer_fn(function()
    vim.cmd("LspStart tailwindcss")
  end, 100)
end

return M

It works, but feels hacky. Is there a cleaner way to make the TailwindCSS LSP automatically pick up each package’s config in a monorepo (e.g. by customizing root_dir in lspconfig)? I can’t hoist the config to the root because the tailwind config differs between packages.

Additional info:

Tailwind LSP: v0.14.16
Tailwind: v3
package manager: pnpm

@thecrypticace
Copy link
Contributor

thecrypticace commented Apr 28, 2025

Can you provide a reproduction repository so I know what the general project structure is? Our Language Server generally supprts multiple configs and bases that off of the content: […] array. (It also supports multiple workspace folders but I don't think that's particularly relevant to your use case). Do you happen to know what the current working directory of the running server is?

@thecrypticace thecrypticace self-assigned this Apr 28, 2025
@thecrypticace thecrypticace added the question Further information is requested label Apr 28, 2025
@tomaszsympi
Copy link
Author

Here is a dummy repository that shows the issue: https://github.com./tomaszsympi/tw-nvim-mono

Steps:

  1. Open apps/web/app/page.tsx and check tailwind completion for classNames
  2. Open apps/app-2/app/page.tsx - you will notice that the class completion doesn't work anymore (different package in monorepo)

@thecrypticace
Copy link
Contributor

thecrypticace commented Apr 28, 2025

@tomaszsympi Looks like that repo isn't public (it 404s)

@tomaszsympi
Copy link
Author

@thecrypticace fixed! Sorry, lol.

@thecrypticace
Copy link
Contributor

All good! I just checked it the project and it seems to work for me but I'm testing in Zed which also uses our language server.

I haven't directly tested in Neovim (yet) but I suspect that this is a quirk of the Neovim LSP implementation. The neovim LSP configs for Tailwind CSS appears to look for a tailwind config file or a package.json relative to the opened file / buffer: https://github.com./neovim/nvim-lspconfig/blob/master/lua/lspconfig/configs/tailwindcss.lua#L113-L114

I assume what is happening is that in a monorepo a file like my-project/packages/web/app/page.tsx will walk up directories until it notices my-project/packages/web/package.json and then it treats this as the root.

Neovim then uses this information to start the language server and uses it as the root of the workspace folders (see https://neovim.io/doc/user/lsp.html#vim.lsp.Config)

This would mean the LSP is basically started as if it were inside my-project/packages/web and not my-project.

If that is what is happening then I don't think we can fix that — but I'll have to give it a test a bit later to verify.

@tomaszsympi
Copy link
Author

tomaszsympi commented Apr 28, 2025

@thecrypticace cursor/zed/vsc work fine, yeah. Thank you for the explanation! I think this is most likely what happens. I will try to move out of nvim-lspconfig to my own lsp config then. Thx again.

@tomaszsympi
Copy link
Author

Neovim then uses this information to start the language server and uses it as the root of the workspace folders (see https://neovim.io/doc/user/lsp.html#vim.lsp.Config)

So this root_dir is cached on Neovim level? Per session? Interesting!

@tomaszsympi
Copy link
Author

tomaszsympi commented Apr 28, 2025

So I tried to disable nvim-lspconfig and then do all of the LSP setup myself. I broke everything and couldn't get any completions, on top of that the :LspInfo command stopped working (I guess it comes from nvim-lspconfig).

Btw., I also noticed that the "completion issue" is fixed after adding this line:

-- options.lua
vim.lsp.enable({
  "tailwindcss",
})

Weird 🤔 .

Edit: I'm using LazyVim distro. I will open an issue there. Thx!

@thecrypticace thecrypticace removed the question Further information is requested label Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants