Skip to content

Instantly share code, notes, and snippets.

@mactep
Last active May 2, 2024 18:44
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mactep/430449fd4f6365474bfa15df5c02d27b to your computer and use it in GitHub Desktop.
Save mactep/430449fd4f6365474bfa15df5c02d27b to your computer and use it in GitHub Desktop.
Conceal html class attribute values using treesitter
-- THIS IS DEPRECATED, USE THE FILE BELOW
-- should get bufnr from autocmd or something
-- conceal only accepts one character
-- thanks to u/Rafat913 for many suggestions and tips
local namespace = vim.api.nvim_create_namespace("class_conceal")
local group = vim.api.nvim_create_augroup("class_conceal", { clear = true })
local conceal_html_class = function(bufnr)
local language_tree = vim.treesitter.get_parser(bufnr, "html")
local syntax_tree = language_tree:parse()
local root = syntax_tree[1]:root()
local query = vim.treesitter.parse_query(
"html",
[[
((attribute
(attribute_name) @att_name (#eq? @att_name "class")
(quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
]]
) -- using single character for conceal thanks to u/Rafat913
for _, captures, metadata in query:iter_matches(root, bufnr, root:start(), root:end_()) do
local start_row, start_col, end_row, end_col = captures[2]:range()
vim.api.nvim_buf_set_extmark(bufnr, namespace, start_row, start_col, {
end_line = end_row,
end_col = end_col,
conceal = metadata[2].conceal,
})
end
end
vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "TextChanged", "InsertLeave" }, {
group = group,
pattern = "*.html",
callback = function()
conceal_html_class(vim.api.nvim_get_current_buf())
end,
})
;; extends
((attribute
(attribute_name) @att_name (#eq? @att_name "class")
(quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
@olimorris
Copy link

This is amazing and thanks for sharing

For those who don't have a conceal level set, make sure vim.opt.conceallevel = 2.

@bastianhenneberg
Copy link

Where do you place this file in your config, it's not working for me?

@olimorris
Copy link

I have mine as a function which I call via an autocmd

@bastianhenneberg
Copy link

Ok thank you, i will test it and hope that i can get it to work in AstroVim.

@bastianhenneberg
Copy link

bastianhenneberg commented May 3, 2023

I have got it to work in my autocmds.lua File in the Astrovim directory if somebody needs the solution here it is. Thank you @olimorris


...
autocmd({ "BufEnter", "BufWritePost", "TextChanged", "InsertLeave" }, {
  group = group,
  pattern = { "*.html", "*.php", "*.blade" },
  callback = function()
    local bufnr = vim.api.nvim_get_current_buf()

    local conceal_ns = vim.api.nvim_create_namespace "class_conceal"

    ---Conceal HTML class attributes. Ideal for big TailwindCSS class lists
    ---Ref: https://gist.github.com/mactep/430449fd4f6365474bfa15df5c02d27b
    local language_tree = vim.treesitter.get_parser(bufnr, "html")
    local syntax_tree = language_tree:parse()
    local root = syntax_tree[1]:root()

    local query = vim.treesitter.query.parse(
      "html",
      [[
    ((attribute
        (attribute_name) @att_name (#eq? @att_name "class")
        (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
    ]]
    )

    for _, captures, metadata in query:iter_matches(root, bufnr, root:start(), root:end_()) do
      local start_row, start_col, end_row, end_col = captures[2]:range()
      vim.api.nvim_buf_set_extmark(bufnr, conceal_ns, start_row, start_col, {
        end_line = end_row,
        end_col = end_col,
        conceal = metadata[2].conceal,
      })
    end
  end,
}) 
...

@theIbraDev
Copy link

i want to thank you guys for this, i have to maintain websites using tailwindcss and the really long classes made it unreadable in nvim.

ps vim.treesitter.parse_query is getting deprecated, use vim.treesitter.query.parse() instead.

@olimorris
Copy link

olimorris commented Nov 28, 2023

Thought I'd share my updated version:

  {
    name = "ConcealAttributes",
    {
      { "BufEnter", "BufWritePost", "TextChanged", "InsertLeave" },
      function()
        vim.opt.conceallevel = 2 -- Concealed text is completely hidden

        local bufnr = vim.api.nvim_get_current_buf()

        ---Conceal HTML class attributes. Ideal for big TailwindCSS class lists
        ---Ref: https://gist.github.com/mactep/430449fd4f6365474bfa15df5c02d27b
        local language_tree = vim.treesitter.get_parser(bufnr, "html")
        local syntax_tree = language_tree:parse()
        local root = syntax_tree[1]:root()

        local query = [[
        ((attribute
          (attribute_name) @att_name (#eq? @att_name "class")
          (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
        ]]

        local ok, ts_query = pcall(vim.treesitter.query.parse, "html", query)
        if not ok then
          return
        end

        for _, captures, metadata in ts_query:iter_matches(root, bufnr, root:start(), root:end_(), {}) do
          local start_row, start_col, end_row, end_col = captures[2]:range()
          -- This conditional prevents conceal leakage if the class attribute is erroneously formed
          if (end_row - start_row) == 0 then
            vim.api.nvim_buf_set_extmark(bufnr, conceal_ns, start_row, start_col, {
              end_line = end_row,
              end_col = end_col,
              conceal = metadata[2].conceal,
            })
          end
        end
      end,
      opts = {
        pattern = { "*.html" },
      },
    },
  },

Note: I'm using legendary.nvim to handle all of my autocmds so that's why the structure is a little different

@Moanrisy
Copy link

Moanrisy commented Dec 21, 2023

I have mine as a function which I call via an autocmd

I try some config in this gist but not working.
Then realize I'm using .jsx type

(currently trying olimorris one) try using in .html (it work, I feel so dumb just copyng paste the config without reading it properly)

then update the config with className too for jsx file and add the pattern in autocmd

  local query = vim.treesitter.query.parse(
    "html",
    [[
    ((attribute
        (attribute_name) @att_name (#any-of? @att_name "class" "className")
        (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
    ]]
  )

@theIbraDev
Copy link

I have mine as a function which I call via an autocmd

I try some config in this gist but not working. Then realize I'm using .jsx type

(currently trying olimorris one) try using in .html (it work, I feel so dumb just copyng paste the config without reading it properly)

then update the config with className too for jsx file and add the pattern in autocmd

  local query = vim.treesitter.query.parse(
    "html",
    [[
    ((attribute
        (attribute_name) @att_name (#any-of? @att_name "class" "className")
        (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))
    ]]
  )

Thought id mention that you can have more patterns at once.

{ ".html", ".svelte", "*.jsx" }

@mactep
Copy link
Author

mactep commented Apr 3, 2024

For everyone using this gist, now you can drop the lua stuff and use only a query to set the conceal. Reference issue. PR that fixes the issue

How to:

  • Update neovim to use the fix (I'm using v0.10.0-dev-2424c3e)
  • Create the file after/queries/heex/highlights.scm
  • Paste the code below in it
;; extends

((attribute
        (attribute_name) @att_name (#eq? @att_name "class")
        (quoted_attribute_value (attribute_value) @class_value) (#set! @class_value conceal "…")))

Make sure you have the conceallevel option set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment