diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6849e11 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github:windwp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65e3ba2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfa8964 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# nvim-ts-closetag +Use treesitter to autoclose xml tag + +it work well with tsx + +## Usage + +``` text +Before Input After +------------------------------------ +
+------------------------------------ +``` + +## Setup +Neovim 0.5 with and nvim-treesitter to work + +``` lua +require('nvim-ts-closetag').setup() +``` + +## Default values + +``` lua +local filetypes = { + 'html', 'xml', 'javascript', 'javascriptreact', 'typescriptreact', 'svelte', 'vue' +} +local skip_tags = { + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'slot', + 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr','menuitem' +} + +``` + +### Override default values + +``` lua +require('nvim-ts-closetag').setup({ + filetypes = { "html" , "xml" }, +}) +``` + +# Ref +[vim-closetag](https://github.com/alvan/vim-closetag/edit/master/README.md) diff --git a/lua/nvim-ts-closetag.lua b/lua/nvim-ts-closetag.lua new file mode 100644 index 0000000..1f2b38b --- /dev/null +++ b/lua/nvim-ts-closetag.lua @@ -0,0 +1,80 @@ +local _, ts_utils = pcall(require, 'nvim-treesitter.ts_utils') + +local M= {} +M.tbl_filetypes = { + 'html', 'xml', 'javascript', 'javascriptreact', 'typescriptreact', 'svelte', 'vue' +} +M.tbl_skipTag = { + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'slot', + 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr','menuitem' +} + +M.setup = function (opts) + opts = opts or {} + M.tbl_filetypes = opts.filetypes or M.tbl_filetypes + M.tbl_skipTag = opts.skip_tag or M.tbl_skipTag + vim.cmd(string.format([[ + autocmd FileType %s inoremap > >:lua require('nvim-ts-closetag').closeTag() + ]],table.concat(M.tbl_filetypes,','))) +end + +local function is_in_table(tbl, val) + for _, value in pairs(tbl) do + if string.match(val, value) then return true end + end + return false +end + +local function find_child_match(target, pattern) + for node in target:iter_children() do + local node_type = node:type() + if node_type ~=nil and string.match(node_type,pattern) then + return node + end + end +end + +local function find_parent_match(target, pattern) + local cur_node = target + while cur_node ~= nil do + local node_type = cur_node:type() + if node_type ~= nil and string.match(node_type, pattern) then + return cur_node + else + cur_node = cur_node:parent() + end + end +end + +local function find_tag_name(start_tag_pattern, name_tag_pattern) + local cur_node = ts_utils.get_node_at_cursor() + local start_tag_node = find_parent_match(cur_node,start_tag_pattern) + if start_tag_node== nil then return nil end + local tag_name = nil + local tbl_name_pattern = vim.split(name_tag_pattern, '>') + local name_node = start_tag_node + for _, pattern in pairs(tbl_name_pattern) do + name_node = find_child_match(name_node, pattern) + end + if name_node ~=nil then + tag_name = ts_utils.get_node_text(name_node)[1] + end + return tag_name +end + +M.closeTag = function () + if is_in_table(M.tbl_filetypes,vim.bo.filetype) then + local start_tag_pattern = 'start_tag' + local name_tag_pattern = 'tag_name' + if is_in_table({'typescriptreact', 'javascriptreact'}, vim.bo.filetype) then + start_tag_pattern = 'jsx_element' + name_tag_pattern = 'jsx_opening_element>identifier' + end + local tag_name = find_tag_name(start_tag_pattern, name_tag_pattern) + if tag_name ~= nil and not is_in_table(M.tbl_skipTag,tag_name) then + vim.cmd(string.format([[normal! a]],tag_name)) + vim.cmd[[normal! T>]] + end + end +end +return M