Skip to content

Neovim completion is not working after version 0.14.11 #1323

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
willian opened this issue Apr 18, 2025 · 13 comments
Open

Neovim completion is not working after version 0.14.11 #1323

willian opened this issue Apr 18, 2025 · 13 comments
Labels
question Further information is requested

Comments

@willian
Copy link

willian commented Apr 18, 2025

What version of Tailwind CSS IntelliSense are you using?

v0.14.15

What version of Tailwind CSS are you using?

v4.1.4

What package manager are you using?

npm

What operating system are you using?

macOS

Tailwind config

@import 'tailwindcss';

My Neovim config
https://github.com/willian/dotfiles/tree/main/config/nvim

Reproduction URL
https://github.com/willian/my-app/blob/main/app/frontend/pages/InertiaExample.tsx#L20

Describe your issue

I created a Rails application from scratch:

rails new my-app -J

Then I installed Inertia.js like this:

bundle add inertia_rails
bin/rails generate inertia:install

Installing Inertia's Rails adapter
Could not find a package.json file to install Inertia to.
Would you like to install Vite Ruby? (y/n) y
         run  bundle add vite_rails from "."
Vite Rails gem successfully installed
         run  bundle exec vite install from "."
Vite Rails successfully installed
Would you like to use TypeScript? (y/n) y
Adding TypeScript support
What framework do you want to use with Inertia? [react, vue, svelte4, svelte] (react) react
         run  npm add @types/react @types/react-dom typescript --silent from "."
Copying adding scripts to package.json
         run  npm pkg set scripts.check="tsc -p tsconfig.app.json && tsc -p tsconfig.node.json" from "."
Would you like to install Tailwind CSS? (y/n) y
Installing Tailwind CSS
         run  npm add tailwindcss @tailwindcss/vite @tailwindcss/forms @tailwindcss/typography --silent from "."

[TL;DR]
I said Yes for all the questions, and I choose React with TypeScript and TailwindCSS

The autocompletion works when LSP version is 0.14.11:
Image

But it does not work if it is 0.14.12 or above:
Image

@thecrypticace
Copy link
Contributor

Do you have a way to check the logs produced by the language server in your Neovim setup? I feel like I run into issues with this every time I try to debug neovim-related problems.

If you can providing them would be very helpful.

@thecrypticace
Copy link
Contributor

Also what Node.js version are you on?

@willian
Copy link
Author

willian commented Apr 24, 2025

@thecrypticace
Node version: v22.14.0

LSP logs: https://gist.github.com/willian/c427579a7c731f396f15b652378a638e#file-lsp-log-L2915
Anything below line 2914 was generated after I attempted to use auto-completion in a className prop.

@thecrypticace
Copy link
Contributor

Ah fantastic thank you — I've downloaded the log. I'll comb through the log and see what I can find.

Also, aside, how did you get it to show these? Would love to know because It'd be super useful for me triaging issues in the future.

@thecrypticace
Copy link
Contributor

I'm not 100% sure if its our language server (possibly) but I see this:
Request textDocument/completion failed with message: Reduce of empty array with no initial value

I don't know what's causing that (I feel like it shouldn't actually be possible) but it's definitely a place for me to start

@thecrypticace
Copy link
Contributor

Ah okay that one isn't us. It's vtsls. There's several language servers running at one time so there's lots of the same RPC IDs. But assuming I've associated things correctly we're either:

  • Returning an empty object
  • Or a null result

Both of these things make me thing we're not detecting class lists correctly in the file. Will need to reconstruct it and go from there. Will report back anything I discover later (tomorrow probably)

@willian
Copy link
Author

willian commented Apr 24, 2025

Ah fantastic thank you — I've downloaded the log. I'll comb through the log and see what I can find.

Also, aside, how did you get it to show these? Would love to know because It'd be super useful for me triaging issues in the future.

No problem!

I've added this to my Neovim config:

vim.lsp.set_log_level("debug")
if vim.fn.has("nvim-0.5.1") == 1 then
  require("vim.lsp.log").set_format_func(vim.inspect)
end

I'm not 100% sure if its our language server (possibly) but I see this: Request textDocument/completion failed with message: Reduce of empty array with no initial value

I don't know what's causing that (I feel like it shouldn't actually be possible) but it's definitely a place for me to start

Yes, I saw this too. My assumption is that the LSP is returning an empty array of suggestions. It works fine when I downgrade it to 0.14.11 by running :MasonInstall [email protected]


Ah okay that one isn't us. It's vtsls. There's several language servers running at one time so there's lots of the same RPC IDs. But assuming I've associated things correctly we're either:

  • Returning an empty object
  • Or a null result

Both of these things make me thing we're not detecting class lists correctly in the file. Will need to reconstruct it and go from there. Will report back anything I discover later (tomorrow probably)

Yes, I think your assumptions are aligned with mine.
Thanks for the quick answer on this! Please, let me know if I can help somehow.

@willian
Copy link
Author

willian commented Apr 30, 2025

Just an update saying that the error still exists in v.0.14.16. I know the possible fix is still in WIP.

@weirdlooop
Copy link

This is happening to me as well, I get no completion in v4, but I do in v3 with the exact same config, this is my lsp log from nvim

[DEBUG][2025-05-11 07:14:55] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  jsonrpc = "2.0",
  method = "textDocument/didChange",
  params = {
    contentChanges = { {
        text = "@props([\n    'image' => false,\n    'title' => false,\n    'subTitle' => false,\n    'content' => false,\n    'buttons' => [],\n])\n@php\n    $class = 'flex flex-col py-5 relative min-h-[600px] justify-center bg-cover bg-center text-surface-content';\n    if ($image) {\n        $class .= ' !text-surface before:absolute before:inset-0 before:bg-black before:opacity-80';\n    }\n@endphp\n<div {{ $attributes->merge(['class' => $class]) }}\n    style=\"{{ $image ? 'background-image: url(' . $image['url'] . ')' : null }}\">\n    <div class=\"flex justify-start z-10 container\">\n        <div class=\"flex flex-col p-3 md:p-10 text-surface max-w-2xl gap-3\">\n            @if ($subTitle)\n                <p class=\"text-sub\">\n                    {!! $subTitle !!}\n                </p>\n            @endif\n            @if ($title)\n                <h1 class=\"text-primary\">{!! $title !!}</h1>\n            @endif\n\n            @if ($content)\n                <div class=\"\">\n                    {!! $content !!}\n                </div>\n            @endif\n            @if ($buttons instanceof Illuminate\\View\\ComponentSlot)\n                {!! $buttons !!}\n            @elseif(is_array($buttons))\n                <div class=\"flex gap-3 btn-group\">\n                    @foreach ($buttons as $button)\n                        <x-button.default :url=\"$button['button']['link']['url']\" :text=\"$button['button']['link']['title']\" :target=\"$button['button']['link']['target']\" />\n                    @endforeach\n                </div>\n            @endif\n        </div>\n    </div>\n</div>\n"
      } },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php",
      version = 13
    }
  }
}
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  jsonrpc = "2.0",
  method = "textDocument/didChange",
  params = {
    contentChanges = { {
        text = "@props([\n    'image' => false,\n    'title' => false,\n    'subTitle' => false,\n    'content' => false,\n    'buttons' => [],\n])\n@php\n    $class = 'flex flex-col py-5 relative min-h-[600px] justify-center bg-cover bg-center text-surface-content';\n    if ($image) {\n        $class .= ' !text-surface before:absolute before:inset-0 before:bg-black before:opacity-80';\n    }\n@endphp\n<div {{ $attributes->merge(['class' => $class]) }}\n    style=\"{{ $image ? 'background-image: url(' . $image['url'] . ')' : null }}\">\n    <div class=\"flex justify-start z-10 container\">\n        <div class=\"flex flex-col p-3 md:p-10 text-surface max-w-2xl gap-3\">\n            @if ($subTitle)\n                <p class=\"text-sub\">\n                    {!! $subTitle !!}\n                </p>\n            @endif\n            @if ($title)\n                <h1 class=\"text-primary\">{!! $title !!}</h1>\n            @endif\n\n            @if ($content)\n                <div class=\"f\">\n                    {!! $content !!}\n                </div>\n            @endif\n            @if ($buttons instanceof Illuminate\\View\\ComponentSlot)\n                {!! $buttons !!}\n            @elseif(is_array($buttons))\n                <div class=\"flex gap-3 btn-group\">\n                    @foreach ($buttons as $button)\n                        <x-button.default :url=\"$button['button']['link']['url']\" :text=\"$button['button']['link']['title']\" :target=\"$button['button']['link']['target']\" />\n                    @endforeach\n                </div>\n            @endif\n        </div>\n    </div>\n</div>\n"
      } },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php",
      version = 14
    }
  }
}
[DEBUG][2025-05-11 07:14:56] ...m/lsp/client.lua:680	"LSP[tailwindcss]"	"client.request"	1	"textDocument/completion"	{
  context = {
    triggerKind = 1
  },
  position = {
    character = 29,
    line = 27
  },
  textDocument = {
    uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
  }
}	<function 1>	11
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  id = 10,
  jsonrpc = "2.0",
  method = "textDocument/completion",
  params = {
    context = {
      triggerKind = 1
    },
    position = {
      character = 29,
      line = 27
    },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
    }
  }
}
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:391	"rpc.receive"	{
  id = 10,
  jsonrpc = "2.0"
}
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  jsonrpc = "2.0",
  method = "textDocument/didChange",
  params = {
    contentChanges = { {
        text = "@props([\n    'image' => false,\n    'title' => false,\n    'subTitle' => false,\n    'content' => false,\n    'buttons' => [],\n])\n@php\n    $class = 'flex flex-col py-5 relative min-h-[600px] justify-center bg-cover bg-center text-surface-content';\n    if ($image) {\n        $class .= ' !text-surface before:absolute before:inset-0 before:bg-black before:opacity-80';\n    }\n@endphp\n<div {{ $attributes->merge(['class' => $class]) }}\n    style=\"{{ $image ? 'background-image: url(' . $image['url'] . ')' : null }}\">\n    <div class=\"flex justify-start z-10 container\">\n        <div class=\"flex flex-col p-3 md:p-10 text-surface max-w-2xl gap-3\">\n            @if ($subTitle)\n                <p class=\"text-sub\">\n                    {!! $subTitle !!}\n                </p>\n            @endif\n            @if ($title)\n                <h1 class=\"text-primary\">{!! $title !!}</h1>\n            @endif\n\n            @if ($content)\n                <div class=\"fl\">\n                    {!! $content !!}\n                </div>\n            @endif\n            @if ($buttons instanceof Illuminate\\View\\ComponentSlot)\n                {!! $buttons !!}\n            @elseif(is_array($buttons))\n                <div class=\"flex gap-3 btn-group\">\n                    @foreach ($buttons as $button)\n                        <x-button.default :url=\"$button['button']['link']['url']\" :text=\"$button['button']['link']['title']\" :target=\"$button['button']['link']['target']\" />\n                    @endforeach\n                </div>\n            @endif\n        </div>\n    </div>\n</div>\n"
      } },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php",
      version = 15
    }
  }
}
[DEBUG][2025-05-11 07:14:56] ...m/lsp/client.lua:680	"LSP[tailwindcss]"	"client.request"	1	"textDocument/completion"	{
  context = {
    triggerKind = 1
  },
  position = {
    character = 30,
    line = 27
  },
  textDocument = {
    uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
  }
}	<function 1>	11
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  id = 11,
  jsonrpc = "2.0",
  method = "textDocument/completion",
  params = {
    context = {
      triggerKind = 1
    },
    position = {
      character = 30,
      line = 27
    },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
    }
  }
}
[DEBUG][2025-05-11 07:14:56] .../vim/lsp/rpc.lua:391	"rpc.receive"	{
  id = 11,
  jsonrpc = "2.0"
}
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  jsonrpc = "2.0",
  method = "textDocument/didChange",
  params = {
    contentChanges = { {
        text = "@props([\n    'image' => false,\n    'title' => false,\n    'subTitle' => false,\n    'content' => false,\n    'buttons' => [],\n])\n@php\n    $class = 'flex flex-col py-5 relative min-h-[600px] justify-center bg-cover bg-center text-surface-content';\n    if ($image) {\n        $class .= ' !text-surface before:absolute before:inset-0 before:bg-black before:opacity-80';\n    }\n@endphp\n<div {{ $attributes->merge(['class' => $class]) }}\n    style=\"{{ $image ? 'background-image: url(' . $image['url'] . ')' : null }}\">\n    <div class=\"flex justify-start z-10 container\">\n        <div class=\"flex flex-col p-3 md:p-10 text-surface max-w-2xl gap-3\">\n            @if ($subTitle)\n                <p class=\"text-sub\">\n                    {!! $subTitle !!}\n                </p>\n            @endif\n            @if ($title)\n                <h1 class=\"text-primary\">{!! $title !!}</h1>\n            @endif\n\n            @if ($content)\n                <div class=\"fle\">\n                    {!! $content !!}\n                </div>\n            @endif\n            @if ($buttons instanceof Illuminate\\View\\ComponentSlot)\n                {!! $buttons !!}\n            @elseif(is_array($buttons))\n                <div class=\"flex gap-3 btn-group\">\n                    @foreach ($buttons as $button)\n                        <x-button.default :url=\"$button['button']['link']['url']\" :text=\"$button['button']['link']['title']\" :target=\"$button['button']['link']['target']\" />\n                    @endforeach\n                </div>\n            @endif\n        </div>\n    </div>\n</div>\n"
      } },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php",
      version = 16
    }
  }
}
[DEBUG][2025-05-11 07:14:59] ...m/lsp/client.lua:680	"LSP[tailwindcss]"	"client.request"	1	"textDocument/completion"	{
  context = {
    triggerKind = 1
  },
  position = {
    character = 31,
    line = 27
  },
  textDocument = {
    uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
  }
}	<function 1>	11
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  id = 12,
  jsonrpc = "2.0",
  method = "textDocument/completion",
  params = {
    context = {
      triggerKind = 1
    },
    position = {
      character = 31,
      line = 27
    },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
    }
  }
}
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:391	"rpc.receive"	{
  id = 12,
  jsonrpc = "2.0"
}
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  jsonrpc = "2.0",
  method = "textDocument/didChange",
  params = {
    contentChanges = { {
        text = "@props([\n    'image' => false,\n    'title' => false,\n    'subTitle' => false,\n    'content' => false,\n    'buttons' => [],\n])\n@php\n    $class = 'flex flex-col py-5 relative min-h-[600px] justify-center bg-cover bg-center text-surface-content';\n    if ($image) {\n        $class .= ' !text-surface before:absolute before:inset-0 before:bg-black before:opacity-80';\n    }\n@endphp\n<div {{ $attributes->merge(['class' => $class]) }}\n    style=\"{{ $image ? 'background-image: url(' . $image['url'] . ')' : null }}\">\n    <div class=\"flex justify-start z-10 container\">\n        <div class=\"flex flex-col p-3 md:p-10 text-surface max-w-2xl gap-3\">\n            @if ($subTitle)\n                <p class=\"text-sub\">\n                    {!! $subTitle !!}\n                </p>\n            @endif\n            @if ($title)\n                <h1 class=\"text-primary\">{!! $title !!}</h1>\n            @endif\n\n            @if ($content)\n                <div class=\"flex\">\n                    {!! $content !!}\n                </div>\n            @endif\n            @if ($buttons instanceof Illuminate\\View\\ComponentSlot)\n                {!! $buttons !!}\n            @elseif(is_array($buttons))\n                <div class=\"flex gap-3 btn-group\">\n                    @foreach ($buttons as $button)\n                        <x-button.default :url=\"$button['button']['link']['url']\" :text=\"$button['button']['link']['title']\" :target=\"$button['button']['link']['target']\" />\n                    @endforeach\n                </div>\n            @endif\n        </div>\n    </div>\n</div>\n"
      } },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php",
      version = 17
    }
  }
}
[DEBUG][2025-05-11 07:14:59] ...m/lsp/client.lua:680	"LSP[tailwindcss]"	"client.request"	1	"textDocument/completion"	{
  context = {
    triggerKind = 1
  },
  position = {
    character = 32,
    line = 27
  },
  textDocument = {
    uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
  }
}	<function 1>	11
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:277	"rpc.send"	{
  id = 13,
  jsonrpc = "2.0",
  method = "textDocument/completion",
  params = {
    context = {
      triggerKind = 1
    },
    position = {
      character = 32,
      line = 27
    },
    textDocument = {
      uri = "file:///Users/someuser/Sites/gIboilerplate/resources/views/components/hero/left.blade.php"
    }
  }
}
[DEBUG][2025-05-11 07:14:59] .../vim/lsp/rpc.lua:391	"rpc.receive"	{
  id = 13,
  jsonrpc = "2.0"
}

@rollsrobby
Copy link

rollsrobby commented May 12, 2025

What works for me is setting the tailwindCSS.experimental.configFile in combination with a custom root_dir function.
NOTE: I'm working in a monorepo so I want to go to the root of the repo.

lspconfig.tailwindcss.setup({
  capabilities = capabilities,
  on_attach = on_attach,
  root_dir = function(fname)
    return vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1])
  end,
  settings = {
    tailwindCSS = {
      experimental = {
        configFile = "path/to/css/globals.css"
      }
    }
  }
})

currently on 0.14.4 since I'm using nix flakes.

edit: my bad, this is explicitly after 0.14.11.

@daviareias
Copy link

experimental = {
configFile = "path/to/css/globals.css"
}

This is working for me in svelte 5, the only problem now is find a way to set this dynamically

thecrypticace added a commit that referenced this issue May 14, 2025
Fixes #1302

Possibly helps #1322
Possibly helps #1323

This PR fixes a problem where files may fail to match against the
appropriate project in a given workspace — and in some cases this
behavior could be "fixed" by opening multiple files until all projects
in a workspace had their selectors recomputed. (A selector is a file
pattern/path paired with a "priority" that tells us how files match
different projects in a workspace)

The problem here is caused by a few things:
- We fixed a bug where auto source detection in v4 silently failed in
IntelliSense. After fixing this a file could get matched against one of
the globs or file paths detected by Oxide.
- A workspace with lots of CSS files may end up creating more than one
"project"
- Upon project initialization we would recompute these selectors **based
on the resolved JS config** (necessary for v3 projects because we
compile ESM or TS configs during intiialization and not discovery).

Obviously, v4 projects do not have JS configs (even if you're using
`@config` or `@plugin` it's not actually the config file we care about.
It's the design system created from the CSS file that matters.) so we
were then throwing away these document selectors.

In a workspace with multiple detected projects (could be because of
multiple CSS "roots", some v3 and some v4 files, etc…) we would check
the file against the selectors of each project, pick out the most
specific match, then initialize the project. The problem is that, when
we re-computed these selectors during initialization they changed. This
has the side effect of dropping the patterns that we picked up from
Oxide for a v4 project.

This would then cause any subsequent requests for a file to match a
*different* project. So for example, a request to compute the document
colors would cause a project to be matched then initialized. Then a
hover in the same file would end up matching a completely different
project.

This PR addresses this by doing two things:
1. Using the same codepath for computing a projects document selectors
during discovery and initalization
2. Normalize Windows drive letters in source paths picked up by Oxide.
This would have the effect of some content paths not matching a project
when it otherwise should on Windows.

In the future it'd probably be a good idea to make documents "sticky"
while they are open such that an open document picks a project and
"sticks" to it. We'd still want to recompute this stickiness if the
config a file is attached to changed but this is a future task as there
might be side effects from doing this.
@willian
Copy link
Author

willian commented May 16, 2025

What works for me is setting the tailwindCSS.experimental.configFile in combination with a custom root_dir function. NOTE: I'm working in a monorepo so I want to go to the root of the repo.

lspconfig.tailwindcss.setup({
capabilities = capabilities,
on_attach = on_attach,
root_dir = function(fname)
return vim.fs.dirname(vim.fs.find('.git', { path = fname, upward = true })[1])
end,
settings = {
tailwindCSS = {
experimental = {
configFile = "path/to/css/globals.css"
}
}
}
})
currently on 0.14.4 since I'm using nix flakes.

edit: my bad, this is explicitly after 0.14.11.

Yes, 0.14.11 works for me. I saw they launched 0.14.17, I will try the new version and post here if that works.

@ashlite
Copy link

ashlite commented May 18, 2025

still not fix in v0.14.17
So will use v0.14.11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

6 participants