Skip to content
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

feat: NodeJS rplugin #33

Merged
merged 8 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# tailwind-tools.nvim

Unofficial [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) integration and tooling for [Neovim](https://github.com/neovim/neovim) using the built-in LSP client and Treesitter, inspired by the official Visual Studio Code [extension](https://github.com/tailwindlabs/tailwindcss-intellisense).
An unofficial [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) integration and tooling for [Neovim](https://github.com/neovim/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](https://github.com/tailwindlabs/tailwindcss-intellisense).

![preview](https://github.com/luckasRanarison/tailwind-tools.nvim/assets/101930730/cb1c0508-8375-474f-9078-2842fb62e0b7)

Expand All @@ -27,9 +27,10 @@ It currently provides the following features:

- Class color hints
- Class concealing
- Class motions
- Class sorting (without [prettier-plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss))
- Completion utilities (using [nvim-cmp](https://github.com/hrsh7th/nvim-cmp))
- Class motions
- Class previewer (using [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim))

> [!NOTE]
> Language services like autocompletion, diagnostics and hover are already provided by [tailwindcss-language-server](https://github.com/tailwindlabs/tailwindcss-intellisense/tree/master/packages/tailwindcss-language-server).
Expand All @@ -39,6 +40,7 @@ It currently provides the following features:
- Neovim v0.9 or higher (v0.10 is recommended)
- [tailwindcss-language-server](https://github.com/tailwindlabs/tailwindcss-intellisense/tree/master/packages/tailwindcss-language-server) >= `v0.0.14` (can be installed using [Mason](https://github.com/williamboman/mason.nvim))
- `html`, `css`, `tsx` and other language Treesitter grammars (using [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter))
- Neovim [node-client](https://www.npmjs.com/package/neovim) (using npm)

> [!TIP]
> If you are not familiar with neovim LSP ecosystem check out [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) to learn how to setup the LSP.
Expand All @@ -51,12 +53,17 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim):
-- tailwind-tools.lua
return {
"luckasRanarison/tailwind-tools.nvim",
dependencies = { "nvim-treesitter/nvim-treesitter" },
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 to call `setup`:
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:

```lua
require("tailwind-tools").setup({
Expand Down Expand Up @@ -88,6 +95,12 @@ Here is the default configuration:
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
Expand Down Expand Up @@ -117,6 +130,8 @@ Available commands:

## Utilities

### nvim-cmp

Utility function for highlighting colors in [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) using [lspkind.nvim](https://github.com/onsails/lspkind.nvim):

```lua
Expand Down Expand Up @@ -144,6 +159,16 @@ return {
> [!TIP]
> You can extend it by calling the function and get the returned `vim_item`, see the nvim-cmp [wiki](https://github.com/hrsh7th/nvim-cmp/wiki/Menu-Appearance) 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).
Expand Down Expand Up @@ -186,7 +211,7 @@ The `class.scm` file should contain a query used to extract the class values for
(attribute_value) @tailwind))
```

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

```scheme
(arguments
Expand Down
11 changes: 2 additions & 9 deletions lua/tailwind-tools/cmp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,9 @@ M.lspkind_format = function(entry, vim_item)

if vim_item.kind == "Color" and doc then
local content = type(doc) == "string" and doc or doc.value
local base, _, _, _r, _g, _b = 10, content:find("rgba?%((%d+), (%d+), (%d+)")
local r, g, b = utils.extract_color(content)

if not _r then
base, _, _, _r, _g, _b = 16, content:find("#(%x%x)(%x%x)(%x%x)")
end

if _r then
local r, g, b = tonumber(_r, base), tonumber(_g, base), tonumber(_b, base)
vim_item.kind_hl_group = utils.set_hl_from(r, g, b, "foreground")
end
if r then vim_item.kind_hl_group = utils.set_hl_from(r, g, b, "foreground") end
end

return vim_item
Expand Down
7 changes: 7 additions & 0 deletions lua/tailwind-tools/config.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
---@diagnostic disable: unused-local

local M = {}

---@alias TailwindTools.ColorHint "foreground" | "background" | "inline"
Expand All @@ -19,6 +21,11 @@ M.options = {
fg = "#38BDF8",
},
},
telescope = {
utilities = {
callback = function(_name, _css) end,
},
},
extension = {
queries = {},
patterns = {},
Expand Down
3 changes: 3 additions & 0 deletions lua/tailwind-tools/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ M.setup = function(options)
if state.conceal.enabled then conceal.enable() end
end,
})

local has_telescope, telescope = pcall(require, "telescope")
if has_telescope then telescope.load_extension("tailwind") end
end

return M
11 changes: 11 additions & 0 deletions lua/tailwind-tools/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,15 @@ M.set_hl_from = function(red, green, blue, style)
return group
end

---@param s string
M.extract_color = function(s)
local base, _, _, _r, _g, _b = 10, s:find("rgba?%((%d+).%s*(%d+).%s*(%d+)")

if not _r then
base, _, _, _r, _g, _b = 16, s:find("#(%x%x)(%x%x)(%x%x)")
end

if _r then return tonumber(_r, base), tonumber(_g, base), tonumber(_b, base) end
end

return M
147 changes: 147 additions & 0 deletions lua/telescope/_extensions/tailwind.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
local log = require("tailwind-tools.log")
local utils = require("tailwind-tools.utils")
local classes = require("tailwind-tools.classes")
local plugin_config = require("tailwind-tools.config")

local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local finders = require("telescope.finders")
local pickers = require("telescope.pickers")
local previewers = require("telescope.previewers")
local entry_display = require("telescope.pickers.entry_display")
local config = require("telescope.config").values

---@class TailwindTools.ClassEntry
---@field name string
---@field value any

local fg_prefixes = { "text", "border", "outline" }

local function get_hl_kind(class_name)
for _, prefix in pairs(fg_prefixes) do
if vim.startswith(class_name, prefix) then return "foreground" end
end

return "background"
end

local function utility_picker()
local utilities = vim.fn.TailwindGetUtilities() --[[@as TailwindTools.ClassEntry[] | nil]]

if utilities == vim.NIL then return log.error("No project found") end

local displayer = entry_display.create({
separator = "",
items = { { remaining = true } },
})

local finder = finders.new_table({
results = utilities,
---@param entry TailwindTools.ClassEntry
entry_maker = function(entry)
local highlight = "Normal"

for _, value in pairs(entry.value) do
if type(value) == "string" then
local r, g, b = utils.extract_color(value)

if r then
local kind = get_hl_kind(entry.name)
highlight = utils.set_hl_from(r, g, b, kind)
end
end
end

return {
value = entry,
ordinal = entry.name,
display = function() return displayer({ { entry.name, highlight } }) end,
}
end,
})

local previewer = previewers.new_buffer_previewer({
title = "CSS Output",
define_preview = function(self, entry)
local bufnr = self.state.bufnr
local css = vim.fn.TailwindExpandUtilities({ entry.value.name })

if type(css) == "string" then
vim.bo[bufnr].ft = "css"
vim.api.nvim_buf_set_text(bufnr, 0, -1, 0, -1, vim.split(css, "\n"))
entry.value.css = css
end
end,
})

local attach_mappings = function()
actions.select_default:replace(function(prompt_bufnr)
local selection = action_state.get_selected_entry()
local name = selection.value.name
local css = selection.value.css or ""

actions.close(prompt_bufnr)

plugin_config.options.telescope.utilities.callback(name, css)
end)

return true
end

pickers
.new({}, {
prompt_title = "Tailwind utilities",
finder = finder,
sorter = config.generic_sorter(),
previewer = previewer,
attach_mappings = attach_mappings,
})
:find()
end

local function class_picker()
local bufnr = vim.api.nvim_get_current_buf()
local class_ranges = classes.get_ranges(bufnr)

if #class_ranges == 0 then return log.info("No classes") end

local filename = vim.api.nvim_buf_get_name(bufnr)
local entries = {}

for _, range in pairs(class_ranges) do
local start_row, start_col, end_row, end_col = unpack(range)
local text = vim.api.nvim_buf_get_text(bufnr, start_row, start_col, end_row, end_col, {})

entries[#entries + 1] = { range = range, text = table.concat(text, "\n") }
end

local finder = finders.new_table({
results = entries,
entry_maker = function(entry)
return {
value = entry,
ordinal = entry.text,
display = entry.text,
path = filename,
lnum = entry.range[1] + 1,
}
end,
})

pickers
.new({}, {
prompt_title = "Tailwind classes",
finder = finder,
sorter = config.generic_sorter(),
previewer = previewers.vim_buffer_vimgrep.new({}),
})
:find()
end

return require("telescope").register_extension({
setup = function() end,
exports = {
classes = class_picker,
utilities = utility_picker,
},
})
Loading