aboutsummaryrefslogtreecommitdiff
path: root/lua/colorizer/tailwind.lua
blob: 0c20b003730628a48c597ea134b38ec9167be3de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
---Helper functions to parse tailwind color variables
--@module colorizer.tailwind
local api = vim.api

local tailwind = {}

-- use a different namespace for tailwind as will be cleared if kept in Default namespace
local DEFAULT_NAMESPACE_TAILWIND = api.nvim_create_namespace "colorizer_tailwind"

local TAILWIND = {}

--- Cleanup tailwind variables and autocmd
---@param buf number
function tailwind.cleanup(buf)
  if TAILWIND[buf] and TAILWIND[buf].AU_ID and TAILWIND[buf].AU_ID[1] then
    pcall(api.nvim_del_autocmd, TAILWIND[buf].AU_ID[1])
    pcall(api.nvim_del_autocmd, TAILWIND[buf].AU_ID[2])
  end
  api.nvim_buf_clear_namespace(buf, DEFAULT_NAMESPACE_TAILWIND, 0, -1)
  TAILWIND[buf] = nil
end

local function highlight_tailwind(buf, ns, options, add_highlight)
  -- it can take some time to actually fetch the results
  -- on top of that, tailwindcss is quite slow in neovim
  vim.defer_fn(function()
    if not TAILWIND[buf] or not TAILWIND[buf].CLIENT or not TAILWIND[buf].CLIENT.request then
      return
    end

    local opts = { textDocument = vim.lsp.util.make_text_document_params() }
    --@local
    ---@diagnostic disable-next-line: param-type-mismatch
    TAILWIND[buf].CLIENT.request("textDocument/documentColor", opts, function(err, results, _, _)
      if err == nil and results ~= nil then
        local data, line_start, line_end = {}, nil, nil
        for _, color in pairs(results) do
          local cur_line = color.range.start.line
          if line_start then
            if cur_line < line_start then
              line_start = cur_line
            end
          else
            line_start = cur_line
          end

          local end_line = color.range["end"].line
          if line_end then
            if end_line > line_end then
              line_end = end_line
            end
          else
            line_end = end_line
          end

          local r, g, b, a = color.color.red or 0, color.color.green or 0, color.color.blue or 0, color.color.alpha or 0
          local rgb_hex = string.format("%02x%02x%02x", r * a * 255, g * a * 255, b * a * 255)
          local first_col = color.range.start.character
          local end_col = color.range["end"].character

          data[cur_line] = data[cur_line] or {}
          table.insert(data[cur_line], { rgb_hex = rgb_hex, range = { first_col, end_col } })
        end
        add_highlight(buf, ns, line_start or 0, line_end and (line_end + 2) or -1, data, options)
      end
    end)
  end, 10)
end

--- highlight buffer using values returned by tailwindcss
-- To see these table information, see |colorizer.buffer|
---@param buf number
---@param options table
---@param options_local table
---@param add_highlight function
function tailwind.setup_lsp_colors(buf, options, options_local, add_highlight)
  TAILWIND[buf] = TAILWIND[buf] or {}
  TAILWIND[buf].AU_ID = TAILWIND[buf].AU_ID or {}

  if not TAILWIND[buf].CLIENT or TAILWIND[buf].CLIENT.is_stopped() then
    if vim.version().minor >= 8 then
      -- create the autocmds so tailwind colours only activate when tailwindcss lsp is active
      if not TAILWIND[buf].AU_CREATED then
        api.nvim_buf_clear_namespace(buf, DEFAULT_NAMESPACE_TAILWIND, 0, -1)
        TAILWIND[buf].AU_ID[1] = api.nvim_create_autocmd("LspAttach", {
          group = options_local.__augroup_id,
          buffer = buf,
          callback = function(args)
            local ok, client = pcall(vim.lsp.get_client_by_id, args.data.client_id)
            if ok then
              if client.name == "tailwindcss" and client.supports_method "textDocument/documentColor" then
                -- wait 100 ms for the first request
                TAILWIND[buf].CLIENT = client
                vim.defer_fn(function()
                  highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
                end, 100)
              end
            end
          end,
        })
        -- make sure the autocmds are deleted after lsp server is closed
        TAILWIND[buf].AU_ID[2] = api.nvim_create_autocmd("LspDetach", {
          group = options_local.__augroup_id,
          buffer = buf,
          callback = function()
            tailwind.cleanup(buf)
          end,
        })
        TAILWIND[buf].AU_CREATED = true
      end
    end
    -- this will be triggered when no lsp is attached
    api.nvim_buf_clear_namespace(buf, DEFAULT_NAMESPACE_TAILWIND, 0, -1)

    TAILWIND[buf].CLIENT = nil

    local ok, tailwind_client = pcall(function()
      return vim.lsp.get_active_clients { bufnr = buf, name = "tailwindcss" }
    end)
    if not ok then
      return
    end

    ok = false
    for _, cl in pairs(tailwind_client) do
      if cl["name"] == "tailwindcss" then
        tailwind_client = cl
        ok = true
        break
      end
    end

    if
      not ok
      and (
        vim.tbl_isempty(tailwind_client or {})
        or not tailwind_client
        or not tailwind_client.supports_method
        or not tailwind_client.supports_method "textDocument/documentColor"
      )
    then
      return true
    end

    TAILWIND[buf].CLIENT = tailwind_client

    -- wait 500 ms for the first request
    vim.defer_fn(function()
      highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
    end, 1000)

    return true
  end

  -- only try to do tailwindcss highlight if lsp is attached
  if TAILWIND[buf].CLIENT then
    highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
  end
end

return tailwind