From de5bd2a1de21ef9ad1eb757b81d47a83f23ac039 Mon Sep 17 00:00:00 2001 From: Luckas Date: Thu, 1 Aug 2024 07:11:11 +0300 Subject: [PATCH] wip: lua pattern support --- lua/tailwind-tools/classes.lua | 32 ++++++++++++++++ lua/tailwind-tools/conceal.lua | 12 +++--- lua/tailwind-tools/config.lua | 3 +- lua/tailwind-tools/filetypes.lua | 1 + lua/tailwind-tools/lsp.lua | 11 +++--- lua/tailwind-tools/motions.lua | 2 +- lua/tailwind-tools/patterns.lua | 64 +++++++++++++++++++++++++++++++ lua/tailwind-tools/treesitter.lua | 33 ++++++++++------ 8 files changed, 132 insertions(+), 26 deletions(-) create mode 100644 lua/tailwind-tools/classes.lua create mode 100644 lua/tailwind-tools/patterns.lua diff --git a/lua/tailwind-tools/classes.lua b/lua/tailwind-tools/classes.lua new file mode 100644 index 0000000..f2d12d2 --- /dev/null +++ b/lua/tailwind-tools/classes.lua @@ -0,0 +1,32 @@ +local M = {} + +local config = require("tailwind-tools.config") +local patterns = require("tailwind-tools.patterns") +local filetypes = require("tailwind-tools.filetypes") +local tresitter = require("tailwind-tools.treesitter") + +---@param bufnr number +M.get_ranges = function(bufnr) + local ft = vim.bo[bufnr].ft + local custom_patterns = config.options.custom_patterns + local pattern_ft = vim.tbl_keys(custom_patterns) + local query_ft = vim.tbl_keys(config.options.custom_queries) + + vim.list_extend(filetypes, query_ft) + vim.list_extend(filetypes, pattern_ft) + + if not vim.tbl_contains(filetypes, ft) then return end + + local class_ranges + local pattern = patterns.builtin_patterns[ft] or custom_patterns[ft] + + if pattern then + class_ranges = patterns.find_class_ranges(bufnr, pattern[1], pattern[2]) + else + class_ranges = tresitter.find_class_ranges(bufnr, ft) + end + + return class_ranges +end + +return M diff --git a/lua/tailwind-tools/conceal.lua b/lua/tailwind-tools/conceal.lua index ad69a5a..91ec4d9 100644 --- a/lua/tailwind-tools/conceal.lua +++ b/lua/tailwind-tools/conceal.lua @@ -3,13 +3,13 @@ local M = {} local lsp = require("tailwind-tools.lsp") local state = require("tailwind-tools.state") local config = require("tailwind-tools.config") -local treesitter = require("tailwind-tools.treesitter") +local classes = require("tailwind-tools.classes") ---@param bufnr number local function set_conceal(bufnr) - local class_nodes = treesitter.get_class_nodes(bufnr) + local class_ranges = classes.get_ranges(bufnr) - if not class_nodes then return end + if not class_ranges then return end vim.wo.conceallevel = 2 vim.api.nvim_buf_clear_namespace(bufnr, vim.g.tailwind_tools.conceal_ns, 0, -1) @@ -18,10 +18,10 @@ local function set_conceal(bufnr) local opts = config.options.conceal - for _, node in pairs(class_nodes) do - local start_row, start_col, end_row, end_col = treesitter.get_class_range(node, bufnr) + for _, range in pairs(class_ranges) do + local start_row, start_col, end_row, end_col = unpack(range) - if not opts.min_length or node:byte_length() >= opts.min_length then + if not opts.min_length or end_row ~= start_row or end_col - start_col >= opts.min_length then vim.api.nvim_buf_set_extmark(bufnr, vim.g.tailwind_tools.conceal_ns, start_row, start_col, { end_line = end_row, end_col = end_col, diff --git a/lua/tailwind-tools/config.lua b/lua/tailwind-tools/config.lua index 0b594e4..e291d64 100644 --- a/lua/tailwind-tools/config.lua +++ b/lua/tailwind-tools/config.lua @@ -19,7 +19,8 @@ M.options = { fg = "#38BDF8", }, }, - custom_filetypes = {}, + custom_patterns = {}, + custom_queries = {}, } return M diff --git a/lua/tailwind-tools/filetypes.lua b/lua/tailwind-tools/filetypes.lua index 86af8ac..c3fef42 100644 --- a/lua/tailwind-tools/filetypes.lua +++ b/lua/tailwind-tools/filetypes.lua @@ -6,6 +6,7 @@ return { "vue", "svelte", "astro", + "rust", "heex", "elixir", "htmldjango", diff --git a/lua/tailwind-tools/lsp.lua b/lua/tailwind-tools/lsp.lua index ab21545..549b24b 100644 --- a/lua/tailwind-tools/lsp.lua +++ b/lua/tailwind-tools/lsp.lua @@ -4,7 +4,7 @@ local log = require("tailwind-tools.log") local utils = require("tailwind-tools.utils") local state = require("tailwind-tools.state") local config = require("tailwind-tools.config") -local treesitter = require("tailwind-tools.treesitter") +local classes = require("tailwind-tools.classes") local color_events = { "BufEnter", @@ -176,15 +176,14 @@ M.sort_classes = function() local bufnr = vim.api.nvim_get_current_buf() local params = vim.lsp.util.make_text_document_params(bufnr) - local class_nodes = treesitter.get_class_nodes(bufnr, true) + local class_ranges = classes.get_ranges(bufnr) - if not class_nodes then return end + if not class_ranges then return end local class_text = {} - local class_ranges = {} - for _, node in pairs(class_nodes) do - local start_row, start_col, end_row, end_col = treesitter.get_class_range(node, bufnr) + 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, {}) class_text[#class_text + 1] = table.concat(text, "\n") diff --git a/lua/tailwind-tools/motions.lua b/lua/tailwind-tools/motions.lua index 9d870f2..8488de9 100644 --- a/lua/tailwind-tools/motions.lua +++ b/lua/tailwind-tools/motions.lua @@ -5,7 +5,7 @@ local treesitter = require("tailwind-tools.treesitter") ---@param comp fun(a: number, b: number): boolean local move_to_class = function(comp) - local nodes = treesitter.get_class_nodes(0, true) + local nodes = treesitter.find_class_nodes(0, true) if not nodes then return end if #nodes == 0 then return log.info("No classes") end diff --git a/lua/tailwind-tools/patterns.lua b/lua/tailwind-tools/patterns.lua new file mode 100644 index 0000000..2958e76 --- /dev/null +++ b/lua/tailwind-tools/patterns.lua @@ -0,0 +1,64 @@ +local M = {} + +---@param b_start number +---@param b_end number +---@param bufnr number +local function byte_range_to_pos(b_start, b_end, bufnr) + local line_count = vim.api.nvim_buf_line_count(bufnr) + local line_offsets = {} + + for i = 1, line_count do + line_offsets[i] = vim.api.nvim_buf_get_offset(bufnr, i - 1) + end + + local start_row, start_col, end_row, end_col + + for line, offset in pairs(line_offsets) do + local next_offset = line_offsets[line + 1] + + if not next_offset or b_start >= offset and b_start < next_offset then + start_row = line - 1 + start_col = b_start - offset + end + + if not next_offset or b_end >= offset and b_end < next_offset then + end_row = line - 1 + end_col = b_end - offset + break + end + end + + return start_row, start_col, end_row, end_col +end + +---@param bufnr number +---@param pattern string +---@param delimiter string +M.find_class_ranges = function(bufnr, pattern, delimiter) + local results = {} + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) + local s = table.concat(lines, "\n") + local offset = 1 + + while true do + local substr = s:sub(offset) + local b_start, b_end, class = substr:find(pattern) + + if b_start == nil then break end + + local class_start = substr:find(delimiter) + offset + local class_end = class_start + #class + local pos = table.pack(byte_range_to_pos(class_start - 1, class_end - 1, bufnr)) + + results[#results + 1] = pos + offset = offset + b_end + end + + return results +end + +M.builtin_patterns = { + rust = { "class=[\"']([^\"']+)[\"']", "[\"']" }, +} + +return M diff --git a/lua/tailwind-tools/treesitter.lua b/lua/tailwind-tools/treesitter.lua index 0183de7..727bfde 100644 --- a/lua/tailwind-tools/treesitter.lua +++ b/lua/tailwind-tools/treesitter.lua @@ -1,22 +1,15 @@ local M = {} local log = require("tailwind-tools.log") -local config = require("tailwind-tools.config") -local filetypes = require("tailwind-tools.filetypes") ---@param bufnr number ----@param all boolean? -M.get_class_nodes = function(bufnr, all) - local ft = vim.bo[bufnr].ft - local supported_filetypes = vim.tbl_extend("keep", filetypes, config.options.custom_filetypes) - - if not vim.tbl_contains(supported_filetypes, ft) then return end - +---@param ft string +local function find_class_nodes(bufnr, ft) local results = {} local parser = vim.treesitter.get_parser(bufnr) - if not parser then return log.warn("No parser available for " .. ft) end - if all and vim.version().minor >= 10 then parser:parse(true) end + if not parser then return log.error("No parser available for " .. ft) end + if vim.version().minor >= 10 then parser:parse(true) end parser:for_each_tree(function(tree, lang_tree) local root = tree:root() @@ -36,7 +29,7 @@ end ---@param node TSNode ---@param bufnr number -M.get_class_range = function(node, bufnr) +local function get_class_range(node, bufnr) local start_row, start_col, end_row, end_col = node:range() local children = node:named_children() @@ -49,4 +42,20 @@ M.get_class_range = function(node, bufnr) return start_row, start_col, end_row, end_col end +---@param bufnr number +---@param ft string +M.find_class_ranges = function(bufnr, ft) + local nodes = find_class_nodes(bufnr, ft) + + if not nodes then return end + + local results = {} + + for _, node in pairs(nodes) do + results[#results + 1] = table.pack(get_class_range(node, bufnr)) + end + + return results +end + return M