Coder Social home page Coder Social logo

luckasranarison / tailwind-tools.nvim Goto Github PK

View Code? Open in Web Editor NEW
288.0 2.0 11.0 117 KB

An unofficial Tailwind CSS integration and tooling for Neovim

Home Page: https://dotfyle.com/plugins/luckasRanarison/tailwind-tools.nvim

License: MIT License

Lua 70.33% Scheme 3.71% Astro 0.50% CSS 0.49% HTML 4.19% Hack 0.84% Svelte 0.45% TypeScript 1.56% Twig 0.39% Vue 0.63% Rust 1.37% JavaScript 15.29% templ 0.26%
lsp neovim neovim-plugin tailwindcss treesitter javascript lua

tailwind-tools.nvim's Introduction

Important

This plugin is a community project and is NOT officially supported by Tailwind Labs.

tailwind-tools.nvim

An unofficial Tailwind CSS integration and tooling for Neovim written in Lua and JavaScript, leveraging the built-in LSP client, Treesitter, and the NodeJS plugin host. It is inspired by the official Visual Studio Code extension.

preview

Contents

Features

The plugin works with all languages inheriting from html, css and tsx treesitter grammars (php, astro, vue, svelte, ...). Lua patterns can also be used as a fallback.

It currently provides the following features:

Note

Language services like autocompletion, diagnostics and hover are already provided by tailwindcss-language-server.

Prerequisites

Tip

If you are not familiar with neovim LSP ecosystem check out nvim-lspconfig to learn how to setup the LSP.

Installation

Using lazy.nvim:

-- tailwind-tools.lua
return {
  "luckasRanarison/tailwind-tools.nvim",
  name = "tailwind-tools",
  build = ":UpdateRemotePlugins",
  dependencies = {
    "nvim-treesitter/nvim-treesitter",
    "nvim-telescope/telescope.nvim", -- optional
  },
  opts = {} -- your configuration
}

If you are using other package managers, you need register the remote plugin by running the :UpdateRemotePlugins command, then call setup to enable the lua plugin:

require("tailwind-tools").setup({
  -- your configuration
})

Configuration

Important

Neovim v0.10 is required for vscode-like inline color hints.

Here is the default configuration:

---@type TailwindTools.Option
{
  document_color = {
    enabled = true, -- can be toggled by commands
    kind = "inline", -- "inline" | "foreground" | "background"
    inline_symbol = "󰝤 ", -- only used in inline mode
    debounce = 200, -- in milliseconds, only applied in insert mode
  },
  conceal = {
    enabled = false, -- can be toggled by commands
    min_length = nil, -- only conceal classes exceeding the provided length
    symbol = "󱏿", -- only a single character is allowed
    highlight = { -- extmark highlight options, see :h 'highlight'
      fg = "#38BDF8",
    },
  },
  telescope = {
    utilities = {
      -- the function used when selecting an utility class in telescope
      callback = function(name, class) end,
    },
  },
  -- see the extension section to learn more
  extension = {
    queries = {}, -- a list of filetypes having custom `class` queries
    patterns = { -- a map of filetypes to Lua pattern lists
      -- exmaple:
      -- rust = { "class=[\"']([^\"']+)[\"']" },
      -- javascript = { "clsx%(([^)]+)%)" },
    },
  },
}

Commands

Available commands:

  • TailwindConcealEnable: enables conceal for all buffers.
  • TailwindConcealDisable: disables conceal.
  • TailwindConcealToggle: toggles conceal.
  • TailwindColorEnable: enables color hints for all buffers.
  • TailwindColorDisable: disables color hints.
  • TailwindColorToggle: toggles color hints.
  • TailwindSort(Sync): sorts all classes in the current buffer.
  • TailwindSortSelection(Sync): sorts selected classes in visual mode.
  • TailwindNextClass: moves the cursor to the nearest next class node.
  • TailwindPrevClass: moves the cursor to the nearest previous class node.

Utilities

nvim-cmp

Utility function for highlighting colors in nvim-cmp using lspkind.nvim:

-- nvim-cmp.lua
return {
  "hrsh7th/nvim-cmp",
  dependencies = {
    "luckasRanarison/tailwind-tools.nvim",
    "onsails/lspkind-nvim",
    -- ...
  },
  opts = function()
    return {
      -- ...
      formatting = {
        format = require("lspkind").cmp_format({
          before = require("tailwind-tools.cmp").lspkind_format
        },
      })
    }
  end
},

Tip

You can extend it by calling the function and get the returned vim_item, see the nvim-cmp wiki to learn more.

telescope.nvim

The plugins registers by default a telescope extension that you can call using :Telescope tailwind <subcommand>

Available subcommands:

  • classes: Lists all the classes in the current file and allows to jump to the selected location.

  • utilities: Lists all utility classes available in the current projects with a custom callback.

Extension

The plugin already supports many languages, but requests for additional language support and PRs are welcome. You can also extend the language support in your configuration by using Treesitter queries or Lua patterns (or both).

Treesitter queries

Treesitter queries are recommended because they can precisely capture the class values at the AST level, but they can be harder to write. If you are not familiar with Treesitter queries, check out the documentation from Neovim or Treesitter.

You can define custom queries for a filetype by adding the filetype to the queries list, like this:

{
  extension = {
    queries = { "myfiletype" },
  }
}

The plugin will search for a class.scm file (classexpr) associated with that filetype in your runtimepath. You can use your Neovim configuration folder to store queries in the following way:

~/.config/nvim
.
├── init.lua
├── lua
│   └── ...
└── queries
    └── myfiletype
        └── class.scm

The class.scm file should contain a query used to extract the class values for a given filetype. The class value should be captured using @tailwind, as shown in the follwing example:

; queries/myfiletype/class.scm
(attribute
  (attribute_name) @_attribute_name
  (#eq? @_attribute_name "class")
  (quoted_attribute_value
    (attribute_value) @tailwind))

Note that quantified captures (using + or ?) cannot be captured using @tailwind. Instead, you must capture the parent node using @tailwind.inner.

(arguments
  (_)+) @tailwind.inner

You can also define node offsets by using the #set! directive and assign the start or end variables to some offset values (defaults to 0).

((postcss_statement
   (at_keyword) @_keyword
   (#eq? @_keyword "@apply")
   (plain_value)+) @tailwind.inner
 (#set! @tailwind.inner "start" 1))

Lua patterns

Lua patterns are easier to write, but they have some limitations. Unlike Treesitter queries, Lua patterns cannot capture nested structures, they are limited to basic pattern matching.

You can define custom patterns by attaching a list of patterns to filetypes. Each pattern should have exactly one capture group representing the class value, as shown below:

{
  extension = {
    patterns = {
      javascript = { "clsx%(([^)]+)%)" },
    },
  }
}

Tip

Lua patterns can be combined with Treesitter queries. You can use both for a single filetype to get the combined results.

Related projects

Here are some related projects:

Contributing

Read the documentation carefully before submitting any issue.

Feature and pull requests are welcome.

tailwind-tools.nvim's People

Contributors

declanchidlow avatar gnarus-g avatar luckasranarison avatar ofseed avatar robog-11 avatar t1gu1 avatar topaxi avatar userisntavailable avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

tailwind-tools.nvim's Issues

Conceal is not working in elixir files (*.html.heex and ex files)

Describe the bug
within heex templates class attributes are still showing. The plugin is working wonderfully with html files.

this is a .html file:
image

this is a .html.heex file:
image

and this is in an .ex file:
image

To Reproduce
Steps to reproduce the behavior:

  1. Open a heex or ex file with some heex template markup

Expected behavior
should work the same as the html file :)

Screenshots
If applicable, add screenshots to help explain your problem.

Environment (please complete the following information):

  • Mac OS 14.5

  • NVIM
    v0.10.0
    Build type: Release
    LuaJIT 2.1.1716656478

  • Nvim-treesitter commit:
    branch master
    commit a6b2f4e

  • tailwind-language-server version:
    tailwindcss-language-server tailwindcss
    Language Server Protocol implementation for Tailwind CSS.
    installed version 0.0.21
    executables tailwindcss-language-server

Additional context
Add any other context about the problem here.

Bug on svelte files tw concealing inside script clause

Describe the bug
Concealign random texts in the script section on svelte

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.
image

Environment (please complete the following information):
Using LazyVim and adding your tool with this configuration

  {
    "luckasRanarison/tailwind-tools.nvim",
    dependencies = { "nvim-treesitter/nvim-treesitter" },
    opts = {
      document_color = {
        enabled = true, -- can be toggled by commands
        kind = "inline", -- "inline" | "foreground" | "background"
        inline_symbol = "󰝤 ", -- only used in inline mode
        debounce = 200, -- in milliseconds, only applied in insert mode
      },
      conceal = {
        enabled = true, -- can be toggled by commands
        min_length = 60, -- only conceal classes exceeding the provided length
        symbol = "󱏿", -- only a single character is allowed
        highlight = { -- extmark highlight options, see :h 'highlight'
          fg = "#38BDF8",
        },
      },
    }, -- your configuration
  },

Additional context
Add any other context about the problem here.

working fine at this commit

Show all the class content of the element concel when hover

Is your feature request related to a problem? Please describe.
In Tailwind you can have a concep of hover:, active:, etc.
A nice way to work with that is to split these "states" on different line.

My problem here is it's actually hard to work with it when it's conceal.

image

Describe the solution you'd like
I love the conceal, but I would love to see only all the style in the current block im working on.

Kinda like that: (But without but in vim normal mode)
image

Describe alternatives you've considered
I could toggle manually the conceal.. but It display ALLL the styles.
Also if I can skip an action, i will take it.

Additional context
Here is an example:

className="
        color-red-400 bg-black                  // Default
        hover:color-blue-400 bg-yellow    // Hover
        active:color-sky-100 
        focus:color-fuchsia-500
"

Got error in a .css file with some tailwind

Describe the bug
In a .css file, I got an error.

Here is what it looks like:
image

P.S. That's not my way of using tailwind.. This is just a project that I have to works with..

To Reproduce
Steps to reproduce the behavior:

  1. Go to a .css file using some tailwind.
  2. Add a space, delete a caracter or paste something (It trigger the error on each action everytime)
  3. See error trigger at every action of the step 2

Here is a small example of what a .css with tailwind can contain.

body {
    @apply flex min-h-[100svh] flex-col bg-skin-fill font-mono text-skin-base
    selection:bg-skin-accent selection:bg-opacity-70 selection:text-skin-inverted;
  }
  section,
  footer {
    @apply mx-auto max-w-3xl px-4;
  }
  a {
    @apply outline-2 outline-offset-1 outline-skin-fill
    focus-visible:no-underline focus-visible:outline-dashed bg-red-600;
  }
  svg {
    @apply inline-block h-6 w-6 fill-skin-base group-hover:fill-skin-accent;
  }

Expected behavior
Just doing nothing in a first time?
Maybe it should not try to conceal in that type of file? (At least in a first time if it's hard to fix)

More information

The color thing works in that type of file but it's weird with the actual conceal.

When cursor isn't on the line: (It conceal it?)
image

When cursor is on the line: (It disable the conceal)
image

Environment (please complete the following information):

  • Operating system: Mac OS Sonoma 14.2.1
  • Neovim version or commit: 0.10.0
  • Nvim-treesitter commit: 5e4f959 (Updated yesterday, using master)
  • tailwind-language-server version: 0.0.16

Add regex fallback

Add the ability to use regex when classes cannot be obtained through treesitter. (#25)

It's currently blocked due to the lack of APIs for searching pattern and retrieving both start and end positions.

Allow more flexible queries

Is your feature request related to a problem? Please describe.
I'd like to create a more complex query, but the requirement "The class value should be the second matched item in the query" means I can't really write the query how I want. In order to use a predicate, I have to capture a node. Therefore, I can only use one predicate before I capture the class node.

Describe the solution you'd like
Fetch the match using the a specified capture-id (which would have to be standardized). Say for example @_twtools_class.

Describe alternatives you've considered
You could also use treesitter-directives. This approach would be more complicated, but it would allow multiple class nodes per query. The built-in set! directive (:h treesitter-directive-set!) would be sufficient for this purpose, but there could be a unique directive for this plugin.

Additional context
This would be a breaking change. You could try to fallback to the original behavior if the specified capture-id is not found.

Example query based on html/class.scm, but restricting which elements it applies to:

(start_tag
  (tag_name) @_tag_name
  (#eq? @_tag_name "div")
  (attribute
    (attribute_name) @_attribute_name
    (#eq? @_attribute_name "class")
    (quoted_attribute_value
      (attribute_value) @_twtools_class))) ; Third capture, but name is as-specified

Leptos / Rust support

The plugin basically works with any language as long it has a treesitter parser and a class query. You can check the currently available queries and supported filetypes here, feel free to request other languages support.

For some reason I'm having a hard time getting this to work with Leptos which is Rust front end framework. I'm not sure if this is expected or if I'm just doing the setup wrong but wanted to raise that it'd be very cool if this supported leptos. view!{} macros might be what make this difficult to achieve so no worries if it's too much effort.

Thanks for sharing this btw, really neat plugin 😄

Add conceal support for .astro files

Is your feature request related to a problem? Please describe.
The plugin currently works in .tsx files but it doesn't do anything in .astro files.

Describe the solution you'd like
It would be nice to see the colors and the conceal working in that file type for astro.

TailwindSort fails on htmldjango file.

Describe the bug
TailwindSortSelection works just fine when highlighting the class areas within " ". However, TailwindSort fails with the following error message that I shared on Reddit.

Error executing Lua callback: ...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:180: attempt to call a nil value

stack traceback:

...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:180: in function <...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:169>

Error executing vim.schedule lua callback: ...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:164: Invalid 'end_col': out of range

stack traceback:

[C]: in function 'nvim_buf_set_text'

...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:164: in function 'handler'

...ightly/current/share/nvim/runtime/lua/vim/lsp/client.lua:680: in function 'fn'

vim/_editor.lua:350: in function <vim/_editor.lua:349>

Error executing Lua callback: ...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:180: attempt to call a nil value

stack traceback:

...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:180: in function <...data/lazy/tailwind-tools.nvim/lua/tailwind-tools/lsp.lua:169>

To Reproduce
Steps to reproduce the behavior:
Open File
Run :TailwindSort
Read error

Expected behavior
For the entire buffer to have its classes be sorted.

Environment (please complete the following information):

  • Windows 10
  • Neovim Nightly
  • Nvim-treesitter commit = 3686029
  • tailwind-language-server version= 0.0.16

Additional context
htmldjango TS grammar is installed. Still failed even after a reinstall.

Link to specific html document.

Sorting breaks syntax sometimes

Describe the bug
When I run the sort command, some classNames get place outside of the quotes.

To Reproduce
I prepared a repo you can clone: https://github.com/Gnarus-G/tailwind-tools-bug-repo
Steps to reproduce the behavior:

  1. Open src/app/page.tsx
  2. Run the :TailwindSort command
  3. See error

Expected behavior
Doesn't create any syntax errors.

Screenshots
image

Environment (please complete the following information):

  • Operating system: Linux rig 6.8.1-zen1-1-zen # 1 ZEN SMP PREEMPT_DYNAMIC Sat, 16 Mar 2024 17:15:23 +0000 x86_64 GNU/Linux
  • Neovim version: NVIM v0.10.0-dev-2657+g9765efb40f
    Build type: RelWithDebInfo
    LuaJIT 2.1.1702233742
  • Nvim-treesitter commit: Latest commit of master
  • tailwind-language-server version: 0.0.16

Add a possibility to set conceal to be enabled by default

Is your feature request related to a problem? Please describe.
Would be nice to have the conceal auto set with the config.
Right now, if I enabled the config, when I close the project and reopen it, I have to call TailwindConcealEnable each time

Describe the solution you'd like
Add an option in the config like enabled = true for the conceal exactly like the document_color.

Invalid buffer id when quitting a window

Describe the bug
When I quit some windows, tailwind-tools errors, and keeps erroring on BufEnter and TextChanged.

To Reproduce
You'll need to use the vim-fugitive plugin, https://github.com/tpope/vim-fugitive, like I do.
I prepared a repo you can clone: https://github.com/Gnarus-G/tailwind-tools-bug-repo
Steps to reproduce the behavior:

  1. Open src/app/page.tsx
  2. Edit it (doesn't matter how you edit it)
  3. Open fugitive and stage
  4. Open the commit prompt, :G commit
  5. :q quit it
  6. See error

Expected behavior
Doesn't error

Screenshots
image

Environment (please complete the following information):

  • Operating system: Linux rig 6.8.1-zen1-1-zen # 1 ZEN SMP PREEMPT_DYNAMIC Sat, 16 Mar 2024 17:15:23 +0000 x86_64 GNU/Linux
  • Neovim version: NVIM v0.10.0-dev-2657+g9765efb40f
    Build type: RelWithDebInfo
    LuaJIT 2.1.1702233742
  • Nvim-treesitter commit: Latest commit of master
  • tailwind-language-server version: 0.0.16

Additional context
I'm not a lua expert, but I think I know the exact issue. The callback closure in the create_autocmd options closes over bufnr. I don't understand enough to know how you should fix this. But I presume you can use the buf number from a argument.

M.on_attach = function(args)
  local bufnr = args.buf
  local client = get_tailwindcss()

  if not client then return end

  vim.api.nvim_create_autocmd(color_events, {
    group = vim.g.tailwind_tools.color_au,
    callback = function(a)
      if not state.color.enabled then return end
      if a.event == "TextChangedI" then
        debounced_color_request(bufnr)
      elseif vim.startswith(a.event, "Cursor") == state.conceal.enabled then
        M.color_request(bufnr)
      end
    end,
  })

  M.color_request(bufnr)
end

[error] no parser avaible for typescript react

Describe the bug
when i run the cmd "TailwindConcealEnable" returns me the error: [tailwind-tools] No parser avaible for typescriptreact

To Reproduce
Steps to reproduce the behavior:

  1. open a buffer with tailwind classes;
  2. run :TailwindConcealEnable
  3. See error

Expected behavior
Attach tailwind tools to all buffers

Screenshots
image

Environment (please complete the following information):

  • Arch linux
  • Neovim 0.10.0 - dev
  • Nvim-treesitter commit:latest
  • tailwind-language-server version:latest

Additional context:
lazy plugin:

  {
    "luckasRanarison/tailwind-tools.nvim",
    cmd = "TailwindConcealEnable",
    dependencies = { "nvim-treesitter/nvim-treesitter" },
    opts = {}, -- your configuration
  },

lspconfig:

local on_attach = require("nvchad.configs.lspconfig").on_attach
local on_init = require("nvchad.configs.lspconfig").on_init
local capabilities = require("nvchad.configs.lspconfig").capabilities

local lspconfig = require "lspconfig"
local servers = { "html", "cssls", "tailwindcss", "tsserver" }

for _, lsp in ipairs(servers) do
  lspconfig[lsp].setup {
    on_attach = on_attach,
    on_init = on_init,
    capabilities = capabilities,
  }
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.