mirror of
https://github.com/windwp/nvim-ts-autotag.git
synced 2025-01-24 13:03:54 -06:00
Compare commits
39 Commits
1aff7cb8ed
...
e4f9252120
Author | SHA1 | Date | |
---|---|---|---|
e4f9252120 | |||
6d690fc131 | |||
209162fde6 | |||
262a48b242 | |||
f655badbb1 | |||
c43a896ebe | |||
43669cf7b9 | |||
ca9ba06e83 | |||
dcbae6df1e | |||
e8ae6e8df3 | |||
57e7c6c27b | |||
de4f720e00 | |||
a14fc79546 | |||
8e3a7aa96f | |||
ea02d622c3 | |||
6c8f07b102 | |||
a4d19476c8 | |||
ef6f6e0d48 | |||
7f91215f15 | |||
3925ec3f34 | |||
9d9d0a812e | |||
03cc1b52ff | |||
9191009088 | |||
1299cbc639 | |||
8c1895066c | |||
fe3dbcc277 | |||
c16ff16926 | |||
20491dc57e | |||
|
99af6de7cd | ||
a33dc74d47 | |||
ee56a21770 | |||
a6728af239 | |||
4466bdcfeb | |||
ae27db72e4 | |||
|
42c89e163b | ||
0121209f76 | |||
9b90325db1 | |||
8ff2824168 | |||
|
aeb7090098 |
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -5,15 +5,15 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
x64-ubuntu:
|
||||
name: X64-ubuntu
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
url: https://github.com/neovim/neovim/releases/download/v0.7.0/nvim-linux64.tar.gz
|
||||
- os: ubuntu-24.04
|
||||
url: https://github.com/neovim/neovim/releases/download/v0.10.0/nvim-linux64.tar.gz
|
||||
manager: sudo apt-get
|
||||
packages: -y fd-find
|
||||
packages: -y fd-find tree-sitter-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: date +%F > todays-date
|
||||
@ -31,17 +31,9 @@ jobs:
|
||||
mkdir -p _neovim
|
||||
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
|
||||
}
|
||||
mkdir -p ~/.local/share/nvim/site/pack/vendor/start
|
||||
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
|
||||
git clone --depth 1 https://github.com/nvim-lua/popup.nvim ~/.local/share/nvim/site/pack/vendor/start/popup.nvim
|
||||
git clone --depth 1 --branch v0.7.2 https://github.com/nvim-treesitter/nvim-treesitter ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter
|
||||
git clone --depth 1 https://github.com/nvim-treesitter/playground ~/.local/share/nvim/site/pack/vendor/start/playground
|
||||
git clone --depth 1 https://github.com/nkrkv/nvim-treesitter-rescript ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter-rescript
|
||||
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
export PATH="${PWD}/_neovim/bin:${PATH}"
|
||||
export VIM="${PWD}/_neovim/share/nvim/runtime"
|
||||
nvim --headless -u tests/minimal.vim -c "TSInstallSync html javascript typescript svelte vue tsx php glimmer rescript" -c "q"
|
||||
export VIMRUNTIME="${PWD}/_neovim/share/nvim/runtime"
|
||||
make test
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -0,0 +1 @@
|
||||
tests/.deps
|
6
.luarc.json
Normal file
6
.luarc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"diagnostics.globals": [
|
||||
"it",
|
||||
"describe"
|
||||
]
|
||||
}
|
8
Makefile
8
Makefile
@ -1,2 +1,8 @@
|
||||
clean:
|
||||
nvim --headless --clean -n -c "lua vim.fn.delete('./tests/.deps', 'rf')" +q
|
||||
test:
|
||||
nvim --headless --noplugin -u tests/minimal.vim -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal.vim'}"
|
||||
nvim --headless --clean -u tests/test.lua "$(FILE)"
|
||||
lint:
|
||||
stylua --check lua/ tests/
|
||||
format:
|
||||
stylua lua/ tests/
|
||||
|
107
README.md
107
README.md
@ -15,13 +15,16 @@ It works with:
|
||||
- rescript
|
||||
- svelte
|
||||
- tsx
|
||||
- twig
|
||||
- typescript
|
||||
- vue
|
||||
- xml
|
||||
|
||||
and more
|
||||
|
||||
## Usage
|
||||
|
||||
``` text
|
||||
```text
|
||||
Before Input After
|
||||
------------------------------------
|
||||
<div > <div></div>
|
||||
@ -29,26 +32,71 @@ Before Input After
|
||||
------------------------------------
|
||||
```
|
||||
|
||||
|
||||
## Setup
|
||||
Neovim 0.7 and nvim-treesitter to work
|
||||
|
||||
User treesitter setup
|
||||
Requires `Nvim 0.9.5` and up.
|
||||
|
||||
```lua
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
autotag = {
|
||||
enable = true,
|
||||
require('nvim-ts-autotag').setup({
|
||||
opts = {
|
||||
-- Defaults
|
||||
enable_close = true, -- Auto close tags
|
||||
enable_rename = true, -- Auto rename pairs of tags
|
||||
enable_close_on_slash = false -- Auto close on trailing </
|
||||
},
|
||||
-- Also override individual filetype configs, these take priority.
|
||||
-- Empty by default, useful if one of the "opts" global settings
|
||||
-- doesn't work well in a specific filetype
|
||||
per_filetype = {
|
||||
["html"] = {
|
||||
enable_close = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
```
|
||||
or you can use a set up function
|
||||
|
||||
``` lua
|
||||
require('nvim-ts-autotag').setup()
|
||||
> [!CAUTION]
|
||||
> If you are setting up via `nvim-treesitter.configs` it has been deprecated! Please migrate to the
|
||||
> new way. It will be removed in `1.0.0`.
|
||||
|
||||
### Extending the default config
|
||||
|
||||
Let's say that there's a language that `nvim-ts-autotag` doesn't currently support and you'd like to support it in your
|
||||
config. While it would be the preference of the author that you upstream your changes, perhaps you would rather not 😢.
|
||||
|
||||
For example, if you have a language that has a very similar layout in its Treesitter Queries as `html`, you could add an
|
||||
alias like so:
|
||||
|
||||
```lua
|
||||
require('nvim-ts-autotag').setup({
|
||||
aliases = {
|
||||
["your language here"] = "html",
|
||||
}
|
||||
})
|
||||
|
||||
-- or
|
||||
local TagConfigs = require("nvim-ts-autotag.config.init")
|
||||
TagConfigs:add_alias("your language here", "html")
|
||||
```
|
||||
|
||||
That will make `nvim-ts-autotag` close tags according to the rules of the `html` config in the given language.
|
||||
|
||||
But what if a parser breaks for whatever reason, for example the upstream Treesitter tree changes its node names and now
|
||||
the default queries that `nvim-ts-autotag` provides no longer work.
|
||||
|
||||
Fear not! You can directly extend and override the existing configs. For example, let's say the start and end tag
|
||||
patterns have changed for `xml`. We can directly override the `xml` config:
|
||||
|
||||
```lua
|
||||
local TagConfigs = require("nvim-ts-autotag.config.init")
|
||||
TagConfigs:update(TagConfigs:get("xml"):override("xml", {
|
||||
start_tag_pattern = { "STag" },
|
||||
end_tag_pattern = { "ETag" },
|
||||
}))
|
||||
```
|
||||
|
||||
In fact, this very nearly what we do during our own internal initialization phase for `nvim-ts-autotag`.
|
||||
|
||||
### Enable update on insert
|
||||
|
||||
If you have that issue
|
||||
@ -66,41 +114,6 @@ vim.lsp.handlers['textDocument/publishDiagnostics'] = vim.lsp.with(
|
||||
update_in_insert = true,
|
||||
}
|
||||
)
|
||||
```
|
||||
## Default values
|
||||
|
||||
``` lua
|
||||
local filetypes = {
|
||||
'html', 'javascript', 'typescript', 'javascriptreact', 'typescriptreact', 'svelte', 'vue', 'tsx', 'jsx', 'rescript',
|
||||
'xml',
|
||||
'php',
|
||||
'markdown',
|
||||
'astro', 'glimmer', 'handlebars', 'hbs'
|
||||
}
|
||||
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-treesitter.configs'.setup {
|
||||
autotag = {
|
||||
enable = true,
|
||||
enable_rename = true,
|
||||
enable_close = true,
|
||||
enable_close_on_slash = true,
|
||||
filetypes = { "html" , "xml" },
|
||||
}
|
||||
}
|
||||
-- OR
|
||||
require('nvim-ts-autotag').setup({
|
||||
filetypes = { "html" , "xml" },
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
## Sponsor
|
||||
|
@ -2,24 +2,35 @@ local internal = require("nvim-ts-autotag.internal")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@deprecated
|
||||
---Will be removed in 1.0.0
|
||||
function M.init()
|
||||
require "nvim-treesitter".define_modules {
|
||||
autotag = {
|
||||
module_path = 'nvim-ts-autotag.internal',
|
||||
is_supported = function(lang)
|
||||
return internal.is_supported(lang)
|
||||
end
|
||||
}
|
||||
}
|
||||
local _, nvim_ts = pcall(require, "nvim-treesitter")
|
||||
if not nvim_ts then
|
||||
return
|
||||
end
|
||||
nvim_ts.define_modules({
|
||||
autotag = {
|
||||
attach = function(bufnr, _)
|
||||
vim.deprecate(
|
||||
"Nvim Treesitter Setup",
|
||||
"`require('nvim-ts-autotag').setup()`",
|
||||
"1.0.0",
|
||||
"nvim-ts-autotag",
|
||||
true
|
||||
)
|
||||
internal.attach(bufnr)
|
||||
end,
|
||||
detach = function(bufnr)
|
||||
internal.detach(bufnr)
|
||||
end,
|
||||
is_supported = function(lang)
|
||||
return internal.is_supported(lang)
|
||||
end,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
function M.setup(opts)
|
||||
internal.setup(opts)
|
||||
vim.cmd[[augroup nvim_ts_xmltag]]
|
||||
vim.cmd[[autocmd!]]
|
||||
vim.cmd[[autocmd FileType * call v:lua.require('nvim-ts-autotag.internal').attach()]]
|
||||
vim.cmd[[autocmd BufDelete * lua require('nvim-ts-autotag.internal').detach(vim.fn.expand('<abuf>'))]]
|
||||
vim.cmd[[augroup end]]
|
||||
end
|
||||
M.setup = require("nvim-ts-autotag.config.plugin").setup
|
||||
|
||||
return M
|
||||
|
@ -1,13 +1,12 @@
|
||||
if _G.__is_log then
|
||||
return require('plenary.log').new {
|
||||
plugin = 'nvim-ts-autotag',
|
||||
level = (_G.__is_log == true and 'debug') or 'warn',
|
||||
}
|
||||
return require("plenary.log").new({
|
||||
plugin = "nvim-ts-autotag",
|
||||
level = (_G.__is_log == true and "debug") or "warn",
|
||||
})
|
||||
else
|
||||
return{
|
||||
return {
|
||||
debug = function(_) end,
|
||||
info = function(_) end,
|
||||
error = function(_) end,
|
||||
|
||||
}
|
||||
end
|
||||
|
94
lua/nvim-ts-autotag/config/ft.lua
Normal file
94
lua/nvim-ts-autotag/config/ft.lua
Normal file
@ -0,0 +1,94 @@
|
||||
---@alias nvim-ts-autotag.FiletypeConfigPattern string[] A single array of patterns
|
||||
|
||||
---@alias nvim-ts-autotag.FiletypeConfig.filetype string The supported filetype for a given Filetype Config
|
||||
|
||||
-- WARN: IF YOU MESS WITH THE FIELDS IN THIS, MAKE ABSOLUTELY SURE YOU UPDATE THE PARTIAL PATTERNS
|
||||
-- IN THE NEXT CLASS AS WELL!
|
||||
--
|
||||
---@class nvim-ts-autotag.FiletypeConfig.patterns The patterns used in a tag search
|
||||
---@field start_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field start_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field end_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field end_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field close_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field close_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field element_tag nvim-ts-autotag.FiletypeConfigPattern
|
||||
---@field skip_tag_pattern nvim-ts-autotag.FiletypeConfigPattern
|
||||
|
||||
---@class nvim-ts-autotag.FiletypeConfig.patterns.p Partial patterns, lua_ls doesn't have partial support so this has to copied 🙁
|
||||
---@field start_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field start_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field end_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field end_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field close_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field close_name_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field element_tag nvim-ts-autotag.FiletypeConfigPattern?
|
||||
---@field skip_tag_pattern nvim-ts-autotag.FiletypeConfigPattern?
|
||||
|
||||
---@class nvim-ts-autotag.FiletypeConfig
|
||||
---@field filetype nvim-ts-autotag.FiletypeConfig.filetype If nil, then this is acting as a base
|
||||
---@field patterns nvim-ts-autotag.FiletypeConfig.patterns
|
||||
local FiletypeConfig = {
|
||||
filetype = "",
|
||||
patterns = {
|
||||
start_tag_pattern = {},
|
||||
start_name_tag_pattern = {},
|
||||
end_tag_pattern = {},
|
||||
end_name_tag_pattern = {},
|
||||
close_tag_pattern = {},
|
||||
close_name_tag_pattern = {},
|
||||
element_tag = {},
|
||||
skip_tag_pattern = {},
|
||||
},
|
||||
}
|
||||
|
||||
---@param filetype nvim-ts-autotag.FiletypeConfig.filetype
|
||||
---@param patterns nvim-ts-autotag.FiletypeConfig.patterns
|
||||
---@return nvim-ts-autotag.FiletypeConfig
|
||||
function FiletypeConfig.new(filetype, patterns)
|
||||
local new = {
|
||||
filetype = filetype,
|
||||
patterns = patterns,
|
||||
}
|
||||
return setmetatable(new, {
|
||||
__index = FiletypeConfig,
|
||||
})
|
||||
end
|
||||
|
||||
--- Creates a deep copy of the given FiletypeConfig
|
||||
---@private
|
||||
---@return nvim-ts-autotag.FiletypeConfig
|
||||
function FiletypeConfig:clone()
|
||||
return vim.deepcopy(self)
|
||||
end
|
||||
|
||||
--- Allows overriding of patterns for a config and returns the overriden config
|
||||
---@param filetype nvim-ts-autotag.FiletypeConfig.filetype
|
||||
---@param patterns nvim-ts-autotag.FiletypeConfig.patterns.p
|
||||
---@return nvim-ts-autotag.FiletypeConfig
|
||||
function FiletypeConfig:override(filetype, patterns)
|
||||
local new = self:clone()
|
||||
new.filetype = filetype
|
||||
new.patterns = vim.tbl_deep_extend("force", new.patterns, patterns or {})
|
||||
return new
|
||||
end
|
||||
|
||||
--- Adds additional values to a given config's patterns, updates the supported filetype, and
|
||||
--- returns a new extended cfg
|
||||
---@param filetype nvim-ts-autotag.FiletypeConfig.filetype
|
||||
---@param patterns nvim-ts-autotag.FiletypeConfig.patterns.p?
|
||||
---@return nvim-ts-autotag.FiletypeConfig
|
||||
function FiletypeConfig:extend(filetype, patterns)
|
||||
local new = self:clone()
|
||||
new.filetype = filetype
|
||||
for pat_key, pats in pairs(patterns or {}) do
|
||||
for _, pat in ipairs(pats) do
|
||||
if not vim.list_contains(new.patterns[pat_key], pat) then
|
||||
table.insert(new.patterns[pat_key], pat)
|
||||
end
|
||||
end
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
return FiletypeConfig
|
78
lua/nvim-ts-autotag/config/init.lua
Normal file
78
lua/nvim-ts-autotag/config/init.lua
Normal file
@ -0,0 +1,78 @@
|
||||
---@class nvim-ts-autotag.TagConfigs
|
||||
---@field private _cfgs { [string]: nvim-ts-autotag.FiletypeConfig }
|
||||
local TagConfigs = {
|
||||
_cfgs = {},
|
||||
}
|
||||
|
||||
---@param filetype string The filetype to get the tag config for
|
||||
---@return nvim-ts-autotag.FiletypeConfig?
|
||||
function TagConfigs:get(filetype)
|
||||
return self._cfgs[filetype]
|
||||
end
|
||||
|
||||
--- Get tag patterns by a filetype
|
||||
---@param filetype string The filetype to get the tag patterns for
|
||||
---@return nvim-ts-autotag.FiletypeConfig.patterns?
|
||||
function TagConfigs:get_patterns(filetype)
|
||||
local cfg = self._cfgs[filetype]
|
||||
if cfg then
|
||||
return cfg.patterns
|
||||
end
|
||||
end
|
||||
|
||||
---@param tag_config nvim-ts-autotag.FiletypeConfig
|
||||
function TagConfigs:add(tag_config)
|
||||
self._cfgs[tag_config.filetype] = tag_config
|
||||
end
|
||||
|
||||
--- Directly updates a given tag config stored in the TagConfigs
|
||||
---@param tag_config nvim-ts-autotag.FiletypeConfig
|
||||
function TagConfigs:update(tag_config)
|
||||
self._cfgs[tag_config] = tag_config
|
||||
end
|
||||
|
||||
--- Get a list of supported filetypes
|
||||
---@return string[]
|
||||
function TagConfigs:get_supported_filetypes()
|
||||
local supported_fts = {}
|
||||
for ft, _ in pairs(self._cfgs) do
|
||||
table.insert(supported_fts, ft)
|
||||
end
|
||||
return supported_fts
|
||||
end
|
||||
|
||||
--- Adds an alias for to an existing tag config for a new filetype
|
||||
---
|
||||
--- For example, to an add an alias for say a filetype named `quill` that we want to use the `html`
|
||||
--- tag config for we can do the following:
|
||||
---
|
||||
--- ```lua
|
||||
--- TagConfigs:add_alias("quill", "html")
|
||||
--- ```
|
||||
---@param new_filetype string The new filetype to alias for
|
||||
---@param existing_filetype string The existing filetype to link to
|
||||
function TagConfigs:add_alias(new_filetype, existing_filetype)
|
||||
local existing_cfg = self:get(existing_filetype)
|
||||
if not existing_cfg then
|
||||
error(("No existing filetype '%s' to alias to!"):format(existing_filetype))
|
||||
end
|
||||
local new = existing_cfg:extend(new_filetype)
|
||||
self:add(new)
|
||||
end
|
||||
|
||||
--- A simple wrapper around `TagConfigs.add_alias`.
|
||||
---
|
||||
--- Adds multiple aliases to an existing tag config for new filetypes.
|
||||
---
|
||||
--- ```lua
|
||||
--- TagConfigs:add_aliases({ "quill", "vue"}, "html")
|
||||
-- ```
|
||||
---@param new_filetypes string[] The new filetypes to alias for
|
||||
---@param existing_filetype string The existing filetype to link to
|
||||
function TagConfigs:add_aliases(new_filetypes, existing_filetype)
|
||||
for _, new_ft in ipairs(new_filetypes) do
|
||||
self:add_alias(new_ft, existing_filetype)
|
||||
end
|
||||
end
|
||||
|
||||
return TagConfigs
|
202
lua/nvim-ts-autotag/config/plugin.lua
Normal file
202
lua/nvim-ts-autotag/config/plugin.lua
Normal file
@ -0,0 +1,202 @@
|
||||
local FiletypeConfig = require("nvim-ts-autotag.config.ft")
|
||||
local TagConfigs = require("nvim-ts-autotag.config.init")
|
||||
|
||||
local function setup_tag_configs()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local base_cfg = FiletypeConfig:extend(nil, {
|
||||
skip_tag_pattern = {
|
||||
"area",
|
||||
"base",
|
||||
"br",
|
||||
"col",
|
||||
"command",
|
||||
"embed",
|
||||
"hr",
|
||||
"img",
|
||||
"slot",
|
||||
"input",
|
||||
"keygen",
|
||||
"link",
|
||||
"meta",
|
||||
"param",
|
||||
"source",
|
||||
"track",
|
||||
"wbr",
|
||||
"menuitem",
|
||||
},
|
||||
})
|
||||
|
||||
local html_tag_cfg = base_cfg:extend("html", {
|
||||
start_tag_pattern = { "start_tag", "STag" },
|
||||
start_name_tag_pattern = { "tag_name", "Name" },
|
||||
end_tag_pattern = { "end_tag", "ETag" },
|
||||
end_name_tag_pattern = { "tag_name", "Name" },
|
||||
close_tag_pattern = { "erroneous_end_tag" },
|
||||
close_name_tag_pattern = { "erroneous_end_tag_name" },
|
||||
element_tag = { "element" },
|
||||
skip_tag_pattern = { "quoted_attribute_value", "end_tag" },
|
||||
})
|
||||
|
||||
TagConfigs:add(html_tag_cfg)
|
||||
TagConfigs:add(html_tag_cfg:override("xml", {
|
||||
start_tag_pattern = { "STag" },
|
||||
end_tag_pattern = { "ETag" },
|
||||
}))
|
||||
|
||||
TagConfigs:add(base_cfg:extend("typescriptreact", {
|
||||
start_tag_pattern = { "jsx_opening_element", "start_tag" },
|
||||
start_name_tag_pattern = {
|
||||
"identifier",
|
||||
"nested_identifier",
|
||||
"tag_name",
|
||||
"member_expression",
|
||||
"jsx_identifier",
|
||||
},
|
||||
end_tag_pattern = { "jsx_closing_element", "end_tag" },
|
||||
end_name_tag_pattern = { "identifier", "tag_name" },
|
||||
close_tag_pattern = { "jsx_closing_element", "nested_identifier" },
|
||||
close_name_tag_pattern = { "member_expression", "nested_identifier", "jsx_identifier", "identifier", ">" },
|
||||
element_tag = { "jsx_element", "element" },
|
||||
skip_tag_pattern = {
|
||||
"jsx_closing_element",
|
||||
"jsx_expression",
|
||||
"string",
|
||||
"jsx_attribute",
|
||||
"end_tag",
|
||||
"string_fragment",
|
||||
},
|
||||
}))
|
||||
|
||||
TagConfigs:add(base_cfg:extend("glimmer", {
|
||||
start_tag_pattern = { "element_node_start" },
|
||||
start_name_tag_pattern = { "tag_name" },
|
||||
end_tag_pattern = { "element_node_end" },
|
||||
end_name_tag_pattern = { "tag_name" },
|
||||
close_tag_pattern = { "element_node_end" },
|
||||
close_name_tag_pattern = { "tag_name" },
|
||||
element_tag = { "element_node" },
|
||||
skip_tag_pattern = { "element_node_end", "attribute_node", "concat_statement" },
|
||||
}))
|
||||
|
||||
TagConfigs:add(base_cfg:extend("svelte", {
|
||||
start_tag_pattern = { "start_tag" },
|
||||
start_name_tag_pattern = { "tag_name" },
|
||||
end_tag_pattern = { "end_tag" },
|
||||
end_name_tag_pattern = { "tag_name" },
|
||||
close_tag_pattern = { "erroneous_end_tag" },
|
||||
close_name_tag_pattern = { "erroneous_end_tag_name" },
|
||||
element_tag = { "element" },
|
||||
skip_tag_pattern = { "quoted_attribute_value", "end_tag" },
|
||||
}))
|
||||
|
||||
TagConfigs:add(base_cfg:extend("templ", {
|
||||
start_tag_pattern = { "tag_start" },
|
||||
start_name_tag_pattern = { "element_identifier" },
|
||||
end_tag_pattern = { "tag_end" },
|
||||
end_name_tag_pattern = { "element_identifier" },
|
||||
close_tag_pattern = { "erroneous_end_tag" },
|
||||
close_name_tag_pattern = { "erroneous_end_tag_name" },
|
||||
element_tag = { "element" },
|
||||
skip_tag_pattern = { "quoted_attribute_value", "tag_end" },
|
||||
}))
|
||||
end
|
||||
|
||||
---@class nvim-ts-autotag.Opts
|
||||
---@field enable_rename boolean? Whether or not to auto rename paired tags
|
||||
---@field enable_close boolean? Whether or not to auto close tags
|
||||
---@field enable_close_on_slash boolean? Whether or not to auto close tags when a `/` is inserted
|
||||
local Opts = {
|
||||
enable_rename = true,
|
||||
enable_close = true,
|
||||
enable_close_on_slash = false,
|
||||
}
|
||||
|
||||
---@class nvim-ts-autotag.PluginSetup
|
||||
---@field private did_setup boolean
|
||||
---@field opts nvim-ts-autotag.Opts? General setup optionss
|
||||
---@field aliases { [string]: string }? Aliases a filetype to an existing filetype tag config
|
||||
---@field per_filetype { [string]: nvim-ts-autotag.Opts }? Per filetype config overrides
|
||||
local Setup = {
|
||||
did_setup = false,
|
||||
opts = Opts,
|
||||
aliases = {
|
||||
["astro"] = "html",
|
||||
["eruby"] = "html",
|
||||
["vue"] = "html",
|
||||
["htmldjango"] = "html",
|
||||
["markdown"] = "html",
|
||||
["php"] = "html",
|
||||
["twig"] = "html",
|
||||
["blade"] = "html",
|
||||
["javascriptreact"] = "typescriptreact",
|
||||
["javascript.jsx"] = "typescriptreact",
|
||||
["typescript.tsx"] = "typescriptreact",
|
||||
["javascript"] = "typescriptreact",
|
||||
["typescript"] = "typescriptreact",
|
||||
["rescript"] = "typescriptreact",
|
||||
["handlebars"] = "glimmer",
|
||||
["hbs"] = "glimmer",
|
||||
},
|
||||
per_filetype = {},
|
||||
}
|
||||
|
||||
--- Do general plugin setup
|
||||
---@param opts nvim-ts-autotag.PluginSetup
|
||||
function Setup.setup(opts)
|
||||
opts = opts or {}
|
||||
if Setup.did_setup then
|
||||
return
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
if opts.enable_rename or opts.enable_close or opts.enable_close_on_slash then
|
||||
vim.notify(
|
||||
"nvim-ts-autotag: Using the legacy setup opts! Please migrate to the new setup options layout as this will eventually have its support removed in 1.0.0!",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
opts = {
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
opts = opts,
|
||||
}
|
||||
end
|
||||
Setup = vim.tbl_deep_extend("force", Setup, opts or {})
|
||||
|
||||
if not Setup.did_setup then
|
||||
Setup.did_setup = true
|
||||
|
||||
setup_tag_configs()
|
||||
for new_ft, existing_ft in pairs(Setup.aliases) do
|
||||
TagConfigs:add_alias(new_ft, existing_ft)
|
||||
end
|
||||
local augroup = vim.api.nvim_create_augroup("nvim_ts_xmltag", { clear = true })
|
||||
vim.api.nvim_create_autocmd("Filetype", {
|
||||
group = augroup,
|
||||
callback = function(args)
|
||||
require("nvim-ts-autotag.internal").attach(args.buf)
|
||||
end,
|
||||
})
|
||||
vim.api.nvim_create_autocmd("BufDelete", {
|
||||
group = augroup,
|
||||
callback = function(args)
|
||||
require("nvim-ts-autotag.internal").detach(args.buf)
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the defined options for the given filetype or in general
|
||||
---@param filetype string?
|
||||
---@return nvim-ts-autotag.Opts
|
||||
function Setup.get_opts(filetype)
|
||||
if not filetype then
|
||||
return Setup.opts
|
||||
end
|
||||
|
||||
local per_ft_conf = Setup.per_filetype[filetype]
|
||||
if per_ft_conf then
|
||||
return vim.tbl_deep_extend("force", Setup.opts or {}, per_ft_conf)
|
||||
end
|
||||
|
||||
return Setup.opts
|
||||
end
|
||||
|
||||
return Setup
|
@ -1,135 +1,10 @@
|
||||
local _, ts_utils = pcall(require, "nvim-treesitter.ts_utils")
|
||||
local configs = require("nvim-treesitter.configs")
|
||||
local parsers = require("nvim-treesitter.parsers")
|
||||
local log = require("nvim-ts-autotag._log")
|
||||
local TagConfigs = require("nvim-ts-autotag.config.init")
|
||||
local Setup = require("nvim-ts-autotag.config.plugin")
|
||||
local utils = require("nvim-ts-autotag.utils")
|
||||
|
||||
local M = {}
|
||||
|
||||
-- stylua: ignore
|
||||
M.tbl_filetypes = {
|
||||
'html', 'javascript', 'typescript', 'javascriptreact', 'typescriptreact', 'svelte', 'vue', 'tsx', 'jsx', 'rescript',
|
||||
'xml',
|
||||
'php',
|
||||
'markdown',
|
||||
'astro', 'glimmer', 'handlebars', 'hbs',
|
||||
'htmldjango',
|
||||
'eruby',
|
||||
'templ',
|
||||
}
|
||||
|
||||
-- stylua: ignore
|
||||
M.tbl_skipTag = {
|
||||
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'slot',
|
||||
'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr', 'menuitem'
|
||||
}
|
||||
|
||||
-- stylua: ignore
|
||||
local HTML_TAG = {
|
||||
filetypes = {
|
||||
'astro',
|
||||
'html',
|
||||
'htmldjango',
|
||||
'markdown',
|
||||
'php',
|
||||
'xml',
|
||||
},
|
||||
start_tag_pattern = { 'start_tag' },
|
||||
start_name_tag_pattern = { 'tag_name' },
|
||||
end_tag_pattern = { 'end_tag' },
|
||||
end_name_tag_pattern = { 'tag_name' },
|
||||
close_tag_pattern = { 'erroneous_end_tag' },
|
||||
close_name_tag_pattern = { 'erroneous_end_tag_name' },
|
||||
element_tag = { 'element' },
|
||||
skip_tag_pattern = { 'quoted_attribute_value', 'end_tag' },
|
||||
}
|
||||
-- stylua: ignore
|
||||
local JSX_TAG = {
|
||||
filetypes = {
|
||||
'typescriptreact', 'javascriptreact', 'javascript.jsx',
|
||||
'typescript.tsx', 'javascript', 'typescript', 'rescript'
|
||||
},
|
||||
start_tag_pattern = { 'jsx_opening_element', 'start_tag' },
|
||||
start_name_tag_pattern = { 'identifier', 'nested_identifier', 'tag_name', 'member_expression', 'jsx_identifier' },
|
||||
end_tag_pattern = { 'jsx_closing_element', 'end_tag' },
|
||||
end_name_tag_pattern = { 'identifier', 'tag_name' },
|
||||
close_tag_pattern = { 'jsx_closing_element', 'nested_identifier' },
|
||||
close_name_tag_pattern = { 'member_expression', 'nested_identifier', 'jsx_identifier', 'identifier', '>' },
|
||||
element_tag = { 'jsx_element', 'element' },
|
||||
skip_tag_pattern = {
|
||||
'jsx_closing_element', 'jsx_expression', 'string', 'jsx_attribute', 'end_tag',
|
||||
'string_fragment'
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- stylua: ignore
|
||||
local HBS_TAG = {
|
||||
filetypes = { 'glimmer', 'handlebars', 'hbs', 'htmldjango' },
|
||||
start_tag_pattern = { 'element_node_start' },
|
||||
start_name_tag_pattern = { 'tag_name' },
|
||||
end_tag_pattern = { 'element_node_end' },
|
||||
end_name_tag_pattern = { 'tag_name' },
|
||||
close_tag_pattern = { 'element_node_end' },
|
||||
close_name_tag_pattern = { 'tag_name' },
|
||||
element_tag = { 'element_node' },
|
||||
skip_tag_pattern = { 'element_node_end', 'attribute_node', 'concat_statement' },
|
||||
}
|
||||
|
||||
|
||||
-- stylua: ignore
|
||||
local SVELTE_TAG = {
|
||||
filetypes = { 'svelte' },
|
||||
start_tag_pattern = { 'start_tag' },
|
||||
start_name_tag_pattern = { 'tag_name' },
|
||||
end_tag_pattern = { 'end_tag' },
|
||||
end_name_tag_pattern = { 'tag_name' },
|
||||
close_tag_pattern = { 'ERROR' },
|
||||
close_name_tag_pattern = { 'ERROR', 'erroneous_end_tag_name' },
|
||||
element_tag = { 'element' },
|
||||
skip_tag_pattern = { 'quoted_attribute_value', 'end_tag' },
|
||||
}
|
||||
|
||||
local TEMPL_TAG = {
|
||||
filetypes = {
|
||||
"templ",
|
||||
},
|
||||
start_tag_pattern = { "tag_start" },
|
||||
start_name_tag_pattern = { "element_identifier" },
|
||||
end_tag_pattern = { "tag_end" },
|
||||
end_name_tag_pattern = { "element_identifier" },
|
||||
close_tag_pattern = { "erroneous_end_tag" },
|
||||
close_name_tag_pattern = { "erroneous_end_tag_name" },
|
||||
element_tag = { "element" },
|
||||
skip_tag_pattern = { "quoted_attribute_value", "tag_end" },
|
||||
}
|
||||
|
||||
local all_tag = {
|
||||
HBS_TAG,
|
||||
SVELTE_TAG,
|
||||
JSX_TAG,
|
||||
TEMPL_TAG,
|
||||
}
|
||||
M.enable_rename = true
|
||||
M.enable_close = true
|
||||
M.enable_close_on_slash = false
|
||||
|
||||
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
|
||||
if opts.enable_rename ~= nil then
|
||||
M.enable_rename = opts.enable_rename
|
||||
end
|
||||
if opts.enable_close ~= nil then
|
||||
M.enable_close = opts.enable_close
|
||||
end
|
||||
if opts.enable_close_on_slash ~= nil then
|
||||
M.enable_close_on_slash = opts.enable_close_on_slash
|
||||
end
|
||||
end
|
||||
|
||||
local function is_in_table(tbl, val)
|
||||
if tbl == nil then
|
||||
return false
|
||||
@ -143,24 +18,24 @@ local function is_in_table(tbl, val)
|
||||
end
|
||||
|
||||
M.is_supported = function(lang)
|
||||
return is_in_table(M.tbl_filetypes, lang)
|
||||
return TagConfigs:get(lang) ~= nil
|
||||
end
|
||||
|
||||
local buffer_tag = {}
|
||||
|
||||
local setup_ts_tag = function()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
for _, value in pairs(all_tag) do
|
||||
if is_in_table(value.filetypes, vim.bo.filetype) then
|
||||
buffer_tag[bufnr] = value
|
||||
return value
|
||||
end
|
||||
local tag_pats = TagConfigs:get_patterns(vim.bo.filetype)
|
||||
if tag_pats then
|
||||
buffer_tag[bufnr] = tag_pats
|
||||
else
|
||||
-- HACK: Crappy fallback
|
||||
buffer_tag[bufnr] = TagConfigs:get_patterns("html")
|
||||
end
|
||||
buffer_tag[bufnr] = HTML_TAG
|
||||
end
|
||||
|
||||
local function is_in_template_tag()
|
||||
local cursor_node = ts_utils.get_node_at_cursor()
|
||||
local cursor_node = utils.get_node_at_cursor()
|
||||
if not cursor_node then
|
||||
return false
|
||||
end
|
||||
@ -182,9 +57,10 @@ local function is_in_template_tag()
|
||||
return has_element and has_template_string
|
||||
end
|
||||
|
||||
---@return nvim-ts-autotag.FiletypeConfig.patterns?
|
||||
local function get_ts_tag()
|
||||
if is_in_template_tag() then
|
||||
return HTML_TAG
|
||||
return TagConfigs:get_patterns("html")
|
||||
else
|
||||
return buffer_tag[vim.api.nvim_get_current_buf()]
|
||||
end
|
||||
@ -248,7 +124,7 @@ local function get_tag_name(node)
|
||||
end
|
||||
|
||||
local function find_tag_node(opt)
|
||||
local target = opt.target or ts_utils.get_node_at_cursor()
|
||||
local target = opt.target or utils.get_node_at_cursor()
|
||||
local tag_pattern = opt.tag_pattern
|
||||
local name_tag_pattern = opt.name_tag_pattern
|
||||
local skip_tag_pattern = opt.skip_tag_pattern
|
||||
@ -324,7 +200,7 @@ local function check_close_tag(close_slash_tag)
|
||||
|
||||
if close_slash_tag then
|
||||
-- Find start node from non closed tag
|
||||
local current = ts_utils.get_node_at_cursor()
|
||||
local current = utils.get_node_at_cursor()
|
||||
-- log.debug(current)
|
||||
target = find_start_tag(current)
|
||||
-- log.debug(target)
|
||||
@ -338,7 +214,7 @@ local function check_close_tag(close_slash_tag)
|
||||
})
|
||||
if tag_node ~= nil then
|
||||
local tag_name = get_tag_name(tag_node)
|
||||
if tag_name ~= nil and is_in_table(M.tbl_skipTag, tag_name) then
|
||||
if tag_name ~= nil and vim.list_contains(ts_tag.skip_tag_pattern, tag_name) then
|
||||
return false
|
||||
end
|
||||
if tag_node ~= nil then
|
||||
@ -367,11 +243,11 @@ local function check_close_tag(close_slash_tag)
|
||||
end
|
||||
|
||||
M.close_tag = function()
|
||||
local buf_parser = parsers.get_parser()
|
||||
local buf_parser = vim.treesitter.get_parser()
|
||||
if not buf_parser then
|
||||
return
|
||||
end
|
||||
buf_parser:parse()
|
||||
buf_parser:parse(true)
|
||||
local result, tag_name = check_close_tag()
|
||||
if result == true and tag_name ~= nil then
|
||||
vim.api.nvim_put({ string.format("</%s>", tag_name) }, "", true, false)
|
||||
@ -380,11 +256,11 @@ M.close_tag = function()
|
||||
end
|
||||
|
||||
M.close_slash_tag = function()
|
||||
local buf_parser = parsers.get_parser()
|
||||
local buf_parser = vim.treesitter.get_parser()
|
||||
if not buf_parser then
|
||||
return
|
||||
end
|
||||
buf_parser:parse()
|
||||
buf_parser:parse(true)
|
||||
local result, tag_name = check_close_tag(true)
|
||||
if result == true and tag_name ~= nil then
|
||||
vim.api.nvim_put({ string.format("%s>", tag_name) }, "", true, true)
|
||||
@ -484,8 +360,8 @@ local function rename_start_tag()
|
||||
-- log.debug("do replace")
|
||||
-- log.debug(tag_name)
|
||||
-- log.debug(close_tag_name)
|
||||
if close_tag_name == '>' then
|
||||
tag_name = tag_name .. '>'
|
||||
if close_tag_name == ">" then
|
||||
tag_name = tag_name .. ">"
|
||||
end
|
||||
replace_text_node(close_tag_node, tag_name)
|
||||
end
|
||||
@ -530,7 +406,7 @@ local function rename_end_tag()
|
||||
if start_tag_node ~= nil then
|
||||
local start_tag_name = get_tag_name(start_tag_node)
|
||||
if tag_name ~= start_tag_name then
|
||||
log.debug('replace end tag')
|
||||
log.debug("replace end tag")
|
||||
replace_text_node(start_tag_node, tag_name)
|
||||
end
|
||||
end
|
||||
@ -549,55 +425,66 @@ local function is_before(regex, range)
|
||||
end
|
||||
end
|
||||
|
||||
local is_before_word = is_before('%w', 1)
|
||||
local is_before_arrow = is_before('<', 0)
|
||||
local is_before_word = is_before("%w", 1)
|
||||
local is_before_arrow = is_before("<", 0)
|
||||
|
||||
M.rename_tag = function()
|
||||
if is_before_word() and parsers.has_parser() then
|
||||
parsers.get_parser():parse()
|
||||
if is_before_word() then
|
||||
local parser = vim.treesitter.get_parser()
|
||||
if not parser then
|
||||
return
|
||||
end
|
||||
parser:parse(true)
|
||||
rename_start_tag()
|
||||
rename_end_tag()
|
||||
end
|
||||
end
|
||||
|
||||
M.attach = function(bufnr, lang)
|
||||
M.lang = lang
|
||||
local config = configs.get_module("autotag")
|
||||
M.setup(config)
|
||||
M.attach = function(bufnr)
|
||||
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||
---@diagnostic disable-next-line: invisible
|
||||
if not Setup.did_setup then
|
||||
local _, ts_configs = pcall(require, "nvim-treesitter.configs")
|
||||
if not ts_configs then
|
||||
M.setup({ opts = {} })
|
||||
end
|
||||
|
||||
if is_in_table(M.tbl_filetypes, vim.bo.filetype) then
|
||||
local config = ts_configs.get_module("autotag")
|
||||
Setup.setup(config)
|
||||
end
|
||||
|
||||
if TagConfigs:get(vim.bo.filetype) ~= nil then
|
||||
setup_ts_tag()
|
||||
local group = vim.api.nvim_create_augroup('nvim-ts-autotag', { clear = true })
|
||||
if M.enable_close == true then
|
||||
vim.api.nvim_buf_set_keymap(bufnr or 0, "i", ">", ">", {
|
||||
local group = vim.api.nvim_create_augroup("nvim-ts-autotag", { clear = true })
|
||||
if Setup.get_opts(vim.bo.filetype).enable_close then
|
||||
vim.keymap.set("i", ">", function()
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_buf_set_text(bufnr, row - 1, col, row - 1, col, { ">" })
|
||||
M.close_tag()
|
||||
vim.api.nvim_win_set_cursor(0, { row, col + 1 })
|
||||
end, {
|
||||
noremap = true,
|
||||
silent = true,
|
||||
callback = function()
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_buf_set_text(bufnr or 0, row - 1, col, row - 1, col, { ">" })
|
||||
M.close_tag()
|
||||
vim.api.nvim_win_set_cursor(0, { row, col + 1 })
|
||||
end,
|
||||
buffer = bufnr,
|
||||
})
|
||||
end
|
||||
if M.enable_close_on_slash == true then
|
||||
vim.api.nvim_buf_set_keymap(bufnr or 0, "i", "/", "/", {
|
||||
if Setup.get_opts(vim.bo.filetype).enable_close_on_slash then
|
||||
vim.keymap.set("i", "/", function()
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_buf_set_text(bufnr, row - 1, col, row - 1, col, { "/" })
|
||||
if is_before_arrow() then
|
||||
log.debug("is_before_arrow")
|
||||
M.close_slash_tag()
|
||||
end
|
||||
local new_row, new_col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_win_set_cursor(0, { new_row, new_col + 1 })
|
||||
end, {
|
||||
noremap = true,
|
||||
silent = true,
|
||||
callback = function()
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_buf_set_text(bufnr or 0, row - 1, col, row - 1, col, { "/" })
|
||||
if is_before_arrow() then
|
||||
log.debug('is_before_arrow')
|
||||
M.close_slash_tag()
|
||||
end
|
||||
local new_row, new_col = unpack(vim.api.nvim_win_get_cursor(0))
|
||||
vim.api.nvim_win_set_cursor(0, { new_row, new_col + 1 })
|
||||
end,
|
||||
buffer = bufnr,
|
||||
})
|
||||
end
|
||||
if M.enable_rename == true then
|
||||
bufnr = bufnr or vim.api.nvim_get_current_buf()
|
||||
if Setup.get_opts(vim.bo.filetype).enable_rename then
|
||||
vim.api.nvim_create_autocmd("InsertLeave", {
|
||||
group = group,
|
||||
buffer = bufnr,
|
||||
@ -608,7 +495,7 @@ M.attach = function(bufnr, lang)
|
||||
end
|
||||
|
||||
M.detach = function(bufnr)
|
||||
local bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf()
|
||||
bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf()
|
||||
buffer_tag[bufnr] = nil
|
||||
end
|
||||
|
||||
|
@ -1,19 +1,15 @@
|
||||
local _, ts_utils = pcall(require, 'nvim-treesitter.ts_utils')
|
||||
local log = require('nvim-ts-autotag._log')
|
||||
local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text or ts_utils.get_node_text
|
||||
local log = require("nvim-ts-autotag._log")
|
||||
local get_node_text = vim.treesitter.get_node_text
|
||||
local M = {}
|
||||
|
||||
M.get_node_text = function(node)
|
||||
local _, txt = pcall(get_node_text, node, vim.api.nvim_get_current_buf())
|
||||
return vim.split(txt, '\n') or {}
|
||||
return vim.split(txt, "\n") or {}
|
||||
end
|
||||
|
||||
M.verify_node = function(node, node_tag)
|
||||
local txt = get_node_text(node, vim.api.nvim_get_current_buf())
|
||||
if
|
||||
txt:match(string.format('^<%s>', node_tag))
|
||||
and txt:match(string.format('</%s>$', node_tag))
|
||||
then
|
||||
if txt:match(string.format("^<%s>", node_tag)) and txt:match(string.format("</%s>$", node_tag)) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@ -22,6 +18,47 @@ M.get_cursor = function(bufnr)
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(bufnr or 0))
|
||||
return row - 1, col
|
||||
end
|
||||
|
||||
-- Credit to `nvim-treesitter`, where this was adapted from
|
||||
--- Get the root of the given tree for a given row & column position
|
||||
---@param row integer 0-indexed row position
|
||||
---@param col integer 0-indexec column position
|
||||
---@param root_lang_tree vim.treesitter.LanguageTree
|
||||
M.get_root_for_position = function(row, col, root_lang_tree)
|
||||
local lang_tree = root_lang_tree:language_for_range({ row, col, row, col })
|
||||
|
||||
for _, tree in pairs(lang_tree:trees()) do
|
||||
local root = tree:root()
|
||||
if root and vim.treesitter.is_in_node_range(root, row, col) then
|
||||
return root, tree, lang_tree
|
||||
end
|
||||
end
|
||||
|
||||
return nil, nil, lang_tree
|
||||
end
|
||||
|
||||
-- Credit to `nvim-treesitter`, where this was adapted from
|
||||
--- Get the current TSNode at the cursor
|
||||
---@param winnr integer?
|
||||
---@return TSNode?
|
||||
M.get_node_at_cursor = function(winnr)
|
||||
winnr = winnr or 0
|
||||
local row, col = unpack(vim.api.nvim_win_get_cursor(winnr))
|
||||
row = row - 1
|
||||
local buf = vim.api.nvim_win_get_buf(winnr)
|
||||
local root_lang_tree = vim.treesitter.get_parser(buf)
|
||||
if not root_lang_tree then
|
||||
return
|
||||
end
|
||||
|
||||
local root = M.get_root_for_position(row, col, root_lang_tree)
|
||||
if not root then
|
||||
return
|
||||
end
|
||||
|
||||
return root:named_descendant_for_range(row, col, row, col)
|
||||
end
|
||||
|
||||
M.dump_node = function(node)
|
||||
local text = M.get_node_text(node)
|
||||
for _, txt in pairs(text) do
|
||||
@ -30,22 +67,22 @@ M.dump_node = function(node)
|
||||
end
|
||||
|
||||
M.is_close_empty_node = function(node)
|
||||
local tag_name = ''
|
||||
local tag_name = ""
|
||||
if node ~= nil then
|
||||
local text = M.get_node_text(node)
|
||||
tag_name = text[#text - 1]
|
||||
end
|
||||
return tag_name:match('%<%/%>$')
|
||||
return tag_name:match("%<%/%>$")
|
||||
end
|
||||
|
||||
M.dump_node_text = function(target)
|
||||
log.debug('=============================')
|
||||
log.debug("=============================")
|
||||
for node in target:iter_children() do
|
||||
local node_type = node:type()
|
||||
local text = M.get_node_text(node)
|
||||
log.debug('type:' .. node_type .. ' ')
|
||||
log.debug("type:" .. node_type .. " ")
|
||||
log.debug(text)
|
||||
end
|
||||
log.debug('=============================')
|
||||
log.debug("=============================")
|
||||
end
|
||||
return M
|
||||
|
1
plugin/nvim-ts-autotag.lua
Normal file
1
plugin/nvim-ts-autotag.lua
Normal file
@ -0,0 +1 @@
|
||||
require("nvim-ts-autotag").init()
|
@ -1 +0,0 @@
|
||||
lua require "nvim-ts-autotag".init()
|
@ -1,181 +0,0 @@
|
||||
local ts = require('nvim-treesitter.configs')
|
||||
ts.setup({
|
||||
ensure_installed = _G.ts_filetypes,
|
||||
highlight = { enable = true },
|
||||
})
|
||||
|
||||
local data = {
|
||||
{
|
||||
name = '1 html close tag after inputting /',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>|]],
|
||||
},
|
||||
{
|
||||
name = '2 html close tag after inputting /',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div clas="laa"><| ]],
|
||||
after = [[<div clas="laa"></div>|]],
|
||||
},
|
||||
{
|
||||
name = '3 html don\'t close tag when no opening tag is found',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[/>]],
|
||||
before = [[<div><|</div> ]],
|
||||
after = [[<div></>|</div>]],
|
||||
},
|
||||
{
|
||||
name = '4 html not close inside quote',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div class="aa|"> </div> ]],
|
||||
after = [[<div class="aa/|"> </div> ]],
|
||||
},
|
||||
{
|
||||
name = '5 typescriptreact close tag after inputting /',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<Img><| ]],
|
||||
after = [[<Img></Img>| ]],
|
||||
},
|
||||
{
|
||||
name = '6 typescriptreact close after inputting /',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<div class="abc"><| ]],
|
||||
after = [[<div class="abc"></div>| ]],
|
||||
},
|
||||
{
|
||||
name = '7 typescriptreact close on inline script after inputting /',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 9,
|
||||
key = [[/]],
|
||||
before = [[const a = () => <div><| ]],
|
||||
after = [[const a = () => <div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = '8 typescriptreact not close on close tag',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<button className="btn " onClick={()}> <| ]],
|
||||
after = [[<button className="btn " onClick={()}> </button>| ]],
|
||||
},
|
||||
{
|
||||
name = '9 typescriptreact not close on expresion',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<button className="btn " onClick={(|)}> </button> ]],
|
||||
after = [[<button className="btn " onClick={(/|)}> </button> ]],
|
||||
},
|
||||
{
|
||||
name = '10 typescriptreact not close on typescript',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 6,
|
||||
key = [[/]],
|
||||
before = [[const data:Array<string| ]],
|
||||
after = [[const data:Array<string/| ]],
|
||||
},
|
||||
{
|
||||
name = '11 typescriptreact not close on script',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 6,
|
||||
key = [[/]],
|
||||
before = [[{(card.data | 0) && <div></div>}]],
|
||||
after = [[{(card.data /| 0) && <div></div>}]],
|
||||
},
|
||||
{
|
||||
name = '12 vue close tag after inputting /',
|
||||
filepath = './sample/index.vue',
|
||||
filetype = 'vue',
|
||||
linenr = 4,
|
||||
key = [[/]],
|
||||
before = [[<Img><| ]],
|
||||
after = [[<Img></Img>|]],
|
||||
},
|
||||
{
|
||||
name = '13 vue not close on script',
|
||||
filepath = './sample/index.vue',
|
||||
filetype = 'vue',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[const data:Array<string| ]],
|
||||
after = [[const data:Array<string/| ]],
|
||||
},
|
||||
{
|
||||
name = '14 typescriptreact nested indentifer close after inputting /',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<Opt.Input><| ]],
|
||||
after = [[<Opt.Input></Opt.Input>| ]],
|
||||
},
|
||||
{
|
||||
name = '15 php close tag after inputting /',
|
||||
filepath = './sample/index.php',
|
||||
filetype = 'php',
|
||||
linenr = 25,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = '16 lit template div close after inputting /',
|
||||
filepath = './sample/index.ts',
|
||||
filetype = 'typescript',
|
||||
linenr = 3,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = '17 eruby template div close after inputting /',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = '18 eruby template ruby string write raw /',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<%= <div| %>]],
|
||||
after = [[<%= <div/| %> ]],
|
||||
},
|
||||
}
|
||||
|
||||
local autotag = require('nvim-ts-autotag')
|
||||
autotag.test = true
|
||||
local run_data = _G.Test_filter(data)
|
||||
|
||||
describe('[close slash tag]', function()
|
||||
_G.Test_withfile(run_data, {
|
||||
mode = 'i',
|
||||
cursor_add = 0,
|
||||
})
|
||||
end)
|
@ -29,6 +29,13 @@ _G.__is_log=true
|
||||
_G.ts_filetypes = {
|
||||
'html', 'javascript', 'typescript', 'svelte', 'vue', 'tsx', 'php', 'glimmer', 'rescript', 'embedded_template'
|
||||
}
|
||||
require("nvim-treesitter.configs").setup({
|
||||
ensure_installed = _G.ts_filetypes,
|
||||
highlight = { enable = true },
|
||||
sync_install = true
|
||||
})
|
||||
vim.treesitter.language.register('tsx', 'typescriptreact')
|
||||
vim.treesitter.language.register('embedded_template', 'eruby')
|
||||
require("plenary/busted")
|
||||
vim.cmd[[luafile ./tests/test-utils.lua]]
|
||||
require("nvim-ts-autotag").setup({
|
||||
|
143
tests/minimal_init.lua
Normal file
143
tests/minimal_init.lua
Normal file
@ -0,0 +1,143 @@
|
||||
local M = {}
|
||||
|
||||
local utils = require("tests.utils.utils")
|
||||
local root = utils.paths.Root:push(".deps/")
|
||||
|
||||
---@class MinPlugin A plugin to download and register on the package path
|
||||
---@alias PluginName string The plugin name, will be used as part of the git clone destination
|
||||
---@alias PluginCloneInfo string | string[] The git url a plugin located at or a table of arguments to be passed to `git clone`
|
||||
---@alias MinPlugins table<PluginName, PluginCloneInfo>
|
||||
|
||||
---Downloads a plugin from a given url and registers it on the 'runtimepath'
|
||||
---@param plugin_name PluginName
|
||||
---@param plugin_clone_args PluginCloneInfo
|
||||
function M.load_plugin(plugin_name, plugin_clone_args)
|
||||
local package_root = root:push("plugins/")
|
||||
local install_destination = package_root:push(plugin_name):get()
|
||||
|
||||
vim.opt.runtimepath:append(install_destination)
|
||||
|
||||
if not vim.loop.fs_stat(package_root:get()) then
|
||||
vim.fn.mkdir(package_root:get(), "p")
|
||||
end
|
||||
|
||||
-- If the plugin install path already exists, we don't need to clone it again.
|
||||
if not vim.loop.fs_stat(install_destination) then
|
||||
print(string.format("[LOAD PLUGIN] Downloading plugin '%s' to '%s'", plugin_name, install_destination))
|
||||
if type(plugin_clone_args) == "table" then
|
||||
plugin_clone_args = table.concat(plugin_clone_args, " ")
|
||||
end
|
||||
vim.fn.system({
|
||||
"git",
|
||||
"clone",
|
||||
"--depth=1",
|
||||
plugin_clone_args,
|
||||
install_destination,
|
||||
})
|
||||
if vim.v.shell_error > 0 then
|
||||
error(
|
||||
string.format("[LOAD PLUGIN] Failed to clone plugin: '%s' to '%s'!", plugin_name, install_destination),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
end
|
||||
end
|
||||
print(("[LOAD PLUGIN] Loaded plugin '%s'"):format(plugin_name))
|
||||
end
|
||||
|
||||
function M.setup_treesitter()
|
||||
print("[TREESITTER] Setting up nvim-treesitter")
|
||||
local parser_cfgs = require("nvim-treesitter.parsers").get_parser_configs()
|
||||
for parser_name, parser_cfg in pairs({
|
||||
rescript = {
|
||||
install_info = {
|
||||
url = "https://github.com/rescript-lang/tree-sitter-rescript",
|
||||
branch = "main",
|
||||
files = { "src/parser.c" },
|
||||
generate_requires_npm = false,
|
||||
requires_generate_from_grammar = true,
|
||||
use_makefile = true,
|
||||
},
|
||||
},
|
||||
}) do
|
||||
parser_cfgs[parser_name] = parser_cfg
|
||||
end
|
||||
require("nvim-treesitter.configs").setup({
|
||||
sync_install = true,
|
||||
ensure_installed = {
|
||||
"html",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"svelte",
|
||||
"vue",
|
||||
"tsx",
|
||||
"php",
|
||||
"glimmer",
|
||||
"rescript",
|
||||
"embedded_template",
|
||||
},
|
||||
})
|
||||
print("[TREESITTER] Done setting up nvim-treesitter")
|
||||
end
|
||||
|
||||
---Do the initial setup. Downloads plugins, ensures the minimal init does not pollute the filesystem by keeping
|
||||
---everything self contained to the CWD of the minimal init file. Run prior to running tests, reproducing issues, etc.
|
||||
---@param plugins? MinPlugins
|
||||
function M.setup(plugins)
|
||||
print("[SETUP] Setting up minimal init")
|
||||
|
||||
-- Instead of disabling swap and a bunch of other stuff, we override default xdg locations for
|
||||
-- Neovim so our test client is as close to a normal client in terms of options as possible
|
||||
local xdg_root = root:push("xdg")
|
||||
local std_paths = {
|
||||
"cache",
|
||||
"data",
|
||||
"config",
|
||||
"state",
|
||||
}
|
||||
local clean = (vim.env.TEST_CLEANUP and vim.env.TEST_CLEANUP:lower() or true)
|
||||
if clean then
|
||||
vim.fn.delete(xdg_root:get(), "rf")
|
||||
elseif clean == "false" or clean == "0" then
|
||||
print("[CLEANUP]: `TEST_CLEANUP` was disabled, not cleaning " .. xdg_root:get())
|
||||
end
|
||||
for _, std_path in pairs(std_paths) do
|
||||
local xdg_str = "XDG_" .. std_path:upper() .. "_HOME"
|
||||
local xdg_path = xdg_root:push(std_path):get()
|
||||
print(("[SETUP] Set vim.env.%s -> %s"):format(xdg_str, xdg_path))
|
||||
vim.env[xdg_str] = xdg_path
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
vim.fn.mkdir(xdg_path, "p")
|
||||
end
|
||||
|
||||
-- Empty the package path so we use only the plugins specified
|
||||
vim.opt.packpath = {}
|
||||
|
||||
-- Install required plugins
|
||||
if plugins ~= nil then
|
||||
for plugin_name, plugin_clone_args in pairs(plugins) do
|
||||
M.load_plugin(plugin_name, plugin_clone_args)
|
||||
end
|
||||
end
|
||||
|
||||
-- Setup nvim-treesitter
|
||||
M.setup_treesitter()
|
||||
|
||||
-- Ensure `nvim-ts-autotag` is registed on the runtimepath and set it up
|
||||
utils.rtp_register_ts_autotag()
|
||||
require("nvim-ts-autotag").setup({
|
||||
opts = {
|
||||
enable_rename = true,
|
||||
enable_close = true,
|
||||
enable_close_on_slash = true,
|
||||
},
|
||||
})
|
||||
|
||||
print("[SETUP] Finished setting up minimal init")
|
||||
end
|
||||
|
||||
M.setup({
|
||||
["plenary.nvim"] = "https://github.com/nvim-lua/plenary.nvim",
|
||||
["popup.nvim"] = "https://github.com/nvim-lua/popup.nvim",
|
||||
["nvim-treesitter"] = "https://github.com/nvim-treesitter/nvim-treesitter",
|
||||
["playground"] = "https://github.com/nvim-treesitter/playground",
|
||||
})
|
@ -1,326 +0,0 @@
|
||||
local ts = require 'nvim-treesitter.configs'
|
||||
ts.setup({
|
||||
ensure_installed = _G.ts_filetypes,
|
||||
highlight = {
|
||||
use_languagetree = false,
|
||||
enable = true,
|
||||
},
|
||||
fold = { enable = false },
|
||||
})
|
||||
|
||||
|
||||
local data = {
|
||||
{
|
||||
name = 'html rename open tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = 'html rename open tag with attr',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala|> ]],
|
||||
},
|
||||
{
|
||||
name = 'html rename close tag with attr',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = 'html not rename close tag on char <',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 10,
|
||||
key = [[i<]],
|
||||
before = [[<div class="lla"> dsadsa |/button> ]],
|
||||
after = [[<div class="lla"> dsadsa <|/button> ]],
|
||||
},
|
||||
{
|
||||
|
||||
name = 'html not rename close tag with not valid',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<di|v class="lla" ]],
|
||||
[[ dsadsa </div>]],
|
||||
},
|
||||
after = [[<lala class="lla" ]],
|
||||
},
|
||||
-- {
|
||||
-- only=true,
|
||||
-- name = "html not rename close tag if it have parent node map with child nod" ,
|
||||
-- filepath = './sample/index.html',
|
||||
-- filetype = "html",
|
||||
-- linenr = 12,
|
||||
-- key = [[ciwlala]],
|
||||
-- before = {
|
||||
-- [[<d|iv> </div>]],
|
||||
-- [[<div> </div>"]]
|
||||
-- },
|
||||
-- after = [[<d|iv> </div>]]
|
||||
-- },
|
||||
{
|
||||
|
||||
name = 'html not rename close tag with not valid',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<div class="lla" </d|iv>]],
|
||||
},
|
||||
after = [[<div class="lla" </lala|>]],
|
||||
},
|
||||
{
|
||||
name = 'typescriptreact rename open tag',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = 'typescriptreact rename open tag with attr',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = 'typescriptreact rename close tag with attr',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'html',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = '17 typescriptreact nested indentifer ',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<Opt.In|put></Opt.Input> ]],
|
||||
after = [[<Opt.lala|></Opt.lala> ]],
|
||||
},
|
||||
{
|
||||
name = '18 rename empty node ',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ilala]],
|
||||
before = [[<|><div></div></>]],
|
||||
after = [[<lala|><div></div></lala>]],
|
||||
},
|
||||
{
|
||||
name = '19 rename start tag on svelte ',
|
||||
filepath = './sample/index.svelte',
|
||||
filetype = 'svelte',
|
||||
linenr = 18,
|
||||
key = [[ciwlala]],
|
||||
before = [[<|data></data>]],
|
||||
after = [[<lala|></lala>]],
|
||||
},
|
||||
{
|
||||
name = '20 rename end tag on svelte ',
|
||||
filepath = './sample/index.svelte',
|
||||
filetype = 'svelte',
|
||||
linenr = 18,
|
||||
key = [[ciwlala]],
|
||||
before = [[<span></spa|n>]],
|
||||
after = [[<lala></lala>]],
|
||||
},
|
||||
{
|
||||
name = "21 rescript rename open tag",
|
||||
filepath = './sample/index.res',
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]]
|
||||
},
|
||||
{
|
||||
name = "22 rescript rename open tag with attr",
|
||||
filepath = './sample/index.res',
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala> ]]
|
||||
},
|
||||
{
|
||||
name = "23 rescript rename close tag with attr",
|
||||
filepath = './sample/index.res',
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]]
|
||||
},
|
||||
{
|
||||
name = '24 test check rename same with parent',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = 'ciwkey',
|
||||
before = {
|
||||
'<Container>',
|
||||
' <di|v>',
|
||||
'',
|
||||
' <span></span>',
|
||||
'</Container>',
|
||||
},
|
||||
after = {
|
||||
'<Container>',
|
||||
' <key>',
|
||||
'',
|
||||
' <span></span>',
|
||||
'</Container>',
|
||||
},
|
||||
},
|
||||
{
|
||||
name = '25 rename start have same node with parent',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
'<div>',
|
||||
' <di|v>',
|
||||
' <span>test </span>',
|
||||
' </div>',
|
||||
'</div>',
|
||||
},
|
||||
after = {
|
||||
'<div>',
|
||||
' <lala>',
|
||||
' <span>test </span>',
|
||||
' </lala>',
|
||||
'</div>',
|
||||
},
|
||||
},
|
||||
{
|
||||
name = '26 rename should not rename tag on attribute node',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
'<div>',
|
||||
'<Navbar className="|a">',
|
||||
' <div className="flex flex-col">',
|
||||
' <div className="flex flex-row">',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>',
|
||||
},
|
||||
after = {
|
||||
'<div>',
|
||||
'<Navbar className="lala">',
|
||||
' <div className="flex flex-col">',
|
||||
' <div className="flex flex-row">',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>',
|
||||
},
|
||||
},
|
||||
{
|
||||
name = 'eruby rename open tag',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = 'eruby rename open tag with attr',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala|> ]],
|
||||
},
|
||||
{
|
||||
name = 'eruby rename close tag with attr',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = 'eruby not rename close tag on char <',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 10,
|
||||
key = [[i<]],
|
||||
before = [[<div class="lla"> dsadsa |/button> ]],
|
||||
after = [[<div class="lla"> dsadsa <|/button> ]],
|
||||
},
|
||||
{
|
||||
name = 'eruby not rename close tag with not valid',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<di|v class="lla" ]],
|
||||
[[ dsadsa </div>]],
|
||||
},
|
||||
after = [[<lala class="lla" ]],
|
||||
},
|
||||
{
|
||||
name = 'eruby not rename close tag with not valid',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<div class="lla" </d|iv>]],
|
||||
},
|
||||
after = [[<div class="lla" </lala|>]],
|
||||
},
|
||||
{
|
||||
name = 'eruby not rename tag-like ruby string',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<%= <div></d|iv> %>]],
|
||||
},
|
||||
after = [[<%= <div></lala|> %>]],
|
||||
},
|
||||
}
|
||||
|
||||
local autotag = require('nvim-ts-autotag')
|
||||
autotag.test = true
|
||||
|
||||
local run_data = _G.Test_filter(data)
|
||||
|
||||
describe('[rename tag]', function()
|
||||
_G.Test_withfile(run_data, {
|
||||
cursor_add = 0,
|
||||
before_each = function(value) end,
|
||||
})
|
||||
end)
|
177
tests/specs/close_slash_tag_spec.lua
Normal file
177
tests/specs/close_slash_tag_spec.lua
Normal file
@ -0,0 +1,177 @@
|
||||
local helpers = require("tests.utils.helpers")
|
||||
|
||||
local data = {
|
||||
{
|
||||
name = "1 html close tag after inputting /",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>|]],
|
||||
},
|
||||
{
|
||||
name = "2 html close tag after inputting /",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div clas="laa"><| ]],
|
||||
after = [[<div clas="laa"></div>|]],
|
||||
},
|
||||
{
|
||||
name = "3 html don't close tag when no opening tag is found",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[/>]],
|
||||
before = [[<div><|</div> ]],
|
||||
after = [[<div></>|</div>]],
|
||||
},
|
||||
{
|
||||
name = "4 html not close inside quote",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div class="aa|"> </div> ]],
|
||||
after = [[<div class="aa/|"> </div> ]],
|
||||
},
|
||||
{
|
||||
name = "5 typescriptreact close tag after inputting /",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<Img><| ]],
|
||||
after = [[<Img></Img>| ]],
|
||||
},
|
||||
{
|
||||
name = "6 typescriptreact close after inputting /",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<div class="abc"><| ]],
|
||||
after = [[<div class="abc"></div>| ]],
|
||||
},
|
||||
{
|
||||
name = "7 typescriptreact close on inline script after inputting /",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 9,
|
||||
key = [[/]],
|
||||
before = [[const a = () => <div><| ]],
|
||||
after = [[const a = () => <div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = "8 typescriptreact not close on close tag",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<button className="btn " onClick={()}> <| ]],
|
||||
after = [[<button className="btn " onClick={()}> </button>| ]],
|
||||
},
|
||||
{
|
||||
name = "9 typescriptreact not close on expresion",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<button className="btn " onClick={(|)}> </button> ]],
|
||||
after = [[<button className="btn " onClick={(/|)}> </button> ]],
|
||||
},
|
||||
{
|
||||
name = "10 typescriptreact not close on typescript",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 6,
|
||||
key = [[/]],
|
||||
before = [[const data:Array<string| ]],
|
||||
after = [[const data:Array<string/| ]],
|
||||
},
|
||||
{
|
||||
name = "11 typescriptreact not close on script",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 6,
|
||||
key = [[/]],
|
||||
before = [[{(card.data | 0) && <div></div>}]],
|
||||
after = [[{(card.data /| 0) && <div></div>}]],
|
||||
},
|
||||
{
|
||||
name = "12 vue close tag after inputting /",
|
||||
filepath = "./sample/index.vue",
|
||||
filetype = "vue",
|
||||
linenr = 4,
|
||||
key = [[/]],
|
||||
before = [[<Img><| ]],
|
||||
after = [[<Img></Img>|]],
|
||||
},
|
||||
{
|
||||
name = "13 vue not close on script",
|
||||
filepath = "./sample/index.vue",
|
||||
filetype = "vue",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[const data:Array<string| ]],
|
||||
after = [[const data:Array<string/| ]],
|
||||
},
|
||||
{
|
||||
name = "14 typescriptreact nested indentifer close after inputting /",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[/]],
|
||||
before = [[<Opt.Input><| ]],
|
||||
after = [[<Opt.Input></Opt.Input>| ]],
|
||||
},
|
||||
{
|
||||
name = "15 php close tag after inputting /",
|
||||
filepath = "./sample/index.php",
|
||||
filetype = "php",
|
||||
linenr = 25,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = "16 lit template div close after inputting /",
|
||||
filepath = "./sample/index.ts",
|
||||
filetype = "typescript",
|
||||
linenr = 3,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = "17 eruby template div close after inputting /",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<div><| ]],
|
||||
after = [[<div></div>| ]],
|
||||
},
|
||||
{
|
||||
name = "18 eruby template ruby string write raw /",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[/]],
|
||||
before = [[<%= <div| %>]],
|
||||
after = [[<%= <div/| %> ]],
|
||||
},
|
||||
}
|
||||
|
||||
local autotag = require("nvim-ts-autotag")
|
||||
autotag.test = true
|
||||
local run_data = helpers.Test_filter(data)
|
||||
|
||||
describe("[close slash tag]", function()
|
||||
helpers.Test_withfile(run_data, {
|
||||
mode = "i",
|
||||
cursor_add = 0,
|
||||
})
|
||||
end)
|
@ -1,95 +1,91 @@
|
||||
local ts = require('nvim-treesitter.configs')
|
||||
ts.setup({
|
||||
ensure_installed = _G.ts_filetypes,
|
||||
highlight = { enable = true },
|
||||
})
|
||||
local helpers = require("tests.utils.helpers")
|
||||
|
||||
local data = {
|
||||
{
|
||||
name = '1 html close tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "1 html close tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div| ]],
|
||||
after = [[<div>|</div>]],
|
||||
},
|
||||
{
|
||||
name = '2 html close tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "2 html close tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div clas="laa"| ]],
|
||||
after = [[<div clas="laa">|</div>]],
|
||||
},
|
||||
{
|
||||
name = '3 html not close tag on close tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "3 html not close tag on close tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div>aa</div| ]],
|
||||
after = [[<div>aa</div>|]],
|
||||
},
|
||||
{
|
||||
name = '4 html not close on input tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "4 html not close on input tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<input| ]],
|
||||
after = [[<input>| ]],
|
||||
},
|
||||
{
|
||||
name = '5 html not close inside quote',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "5 html not close inside quote",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div class="aa|"> </div> ]],
|
||||
after = [[<div class="aa>|"> </div> ]],
|
||||
},
|
||||
{
|
||||
name = '6 html not close on exist tag',
|
||||
filepath = './sample/index.html',
|
||||
filetype = 'html',
|
||||
name = "6 html not close on exist tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div><div|</div></div>]],
|
||||
after = [[<div><div>|</div></div>]],
|
||||
},
|
||||
{
|
||||
name = '7 typescriptreact close tag',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "7 typescriptreact close tag",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<Img| ]],
|
||||
after = [[<Img>|</Img> ]],
|
||||
},
|
||||
{
|
||||
name = '8 typescriptreact close',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "8 typescriptreact close",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<div class="abc"| ]],
|
||||
after = [[<div class="abc">|</div> ]],
|
||||
},
|
||||
{
|
||||
name = '9 typescriptreact not close on exist tag',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "9 typescriptreact not close on exist tag",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<div><div|</div></div>]],
|
||||
after = [[<div><div>|</div></div>]],
|
||||
},
|
||||
{
|
||||
name = '10 typescriptreact close on inline script',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "10 typescriptreact close on inline script",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 9,
|
||||
key = [[>]],
|
||||
before = [[const a = () => <div| ]],
|
||||
@ -97,27 +93,27 @@ local data = {
|
||||
},
|
||||
{
|
||||
|
||||
name = '11 typescriptreact not close on close tag',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "11 typescriptreact not close on close tag",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<button className="btn " onClick={()}> </button| ]],
|
||||
after = [[<button className="btn " onClick={()}> </button>| ]],
|
||||
},
|
||||
{
|
||||
name = '12 typescriptreact not close on expresion',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "12 typescriptreact not close on expresion",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<button className="btn " onClick={(|)}> </button> ]],
|
||||
after = [[<button className="btn " onClick={(>|)}> </button> ]],
|
||||
},
|
||||
{
|
||||
name = '13 typescriptreact not close on typescript',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "13 typescriptreact not close on typescript",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 6,
|
||||
key = [[>]],
|
||||
before = [[const data:Array<string| ]],
|
||||
@ -125,45 +121,45 @@ local data = {
|
||||
},
|
||||
|
||||
{
|
||||
name = '14 typescriptreact not close on script',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "14 typescriptreact not close on script",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 6,
|
||||
key = [[>]],
|
||||
before = [[{(card.data | 0) && <div></div>}]],
|
||||
after = [[{(card.data >| 0) && <div></div>}]],
|
||||
},
|
||||
{
|
||||
name = '15 vue auto close tag',
|
||||
filepath = './sample/index.vue',
|
||||
filetype = 'vue',
|
||||
name = "15 vue auto close tag",
|
||||
filepath = "./sample/index.vue",
|
||||
filetype = "vue",
|
||||
linenr = 4,
|
||||
key = [[>]],
|
||||
before = [[<Img| ]],
|
||||
after = [[<Img>|</Img>]],
|
||||
},
|
||||
{
|
||||
name = '16 vue not close on script',
|
||||
filepath = './sample/index.vue',
|
||||
filetype = 'vue',
|
||||
name = "16 vue not close on script",
|
||||
filepath = "./sample/index.vue",
|
||||
filetype = "vue",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[const data:Array<string| ]],
|
||||
after = [[const data:Array<string>| ]],
|
||||
},
|
||||
{
|
||||
name = '17 typescriptreact nested indentifer ',
|
||||
filepath = './sample/index.tsx',
|
||||
filetype = 'typescriptreact',
|
||||
name = "17 typescriptreact nested indentifer ",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[>]],
|
||||
before = [[<Opt.Input| ]],
|
||||
after = [[<Opt.Input>|</Opt.Input> ]],
|
||||
},
|
||||
{
|
||||
name = '18 php div ',
|
||||
filepath = './sample/index.php',
|
||||
filetype = 'php',
|
||||
name = "18 php div ",
|
||||
filepath = "./sample/index.php",
|
||||
filetype = "php",
|
||||
linenr = 25,
|
||||
key = [[>]],
|
||||
before = [[<div| ]],
|
||||
@ -179,27 +175,27 @@ local data = {
|
||||
-- after = [[<div>|</div> ]],
|
||||
-- },
|
||||
{
|
||||
name = '19 lit template div',
|
||||
filepath = './sample/index.ts',
|
||||
filetype = 'typescript',
|
||||
name = "19 lit template div",
|
||||
filepath = "./sample/index.ts",
|
||||
filetype = "typescript",
|
||||
linenr = 3,
|
||||
key = [[>]],
|
||||
before = [[<div| ]],
|
||||
after = [[<div>|</div> ]],
|
||||
},
|
||||
{
|
||||
name = '20 eruby template div',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
name = "20 eruby template div",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<div| ]],
|
||||
after = [[<div>|</div> ]],
|
||||
},
|
||||
{
|
||||
name = '20 eruby template ruby string',
|
||||
filepath = './sample/index.html.erb',
|
||||
filetype = 'eruby',
|
||||
name = "20 eruby template ruby string",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[>]],
|
||||
before = [[<%= <div| %>]],
|
||||
@ -207,14 +203,14 @@ local data = {
|
||||
},
|
||||
}
|
||||
|
||||
local autotag = require('nvim-ts-autotag')
|
||||
local autotag = require("nvim-ts-autotag")
|
||||
autotag.test = true
|
||||
local run_data = _G.Test_filter(data)
|
||||
local run_data = helpers.Test_filter(data)
|
||||
|
||||
describe('[close tag]', function()
|
||||
_G.Test_withfile(run_data, {
|
||||
mode = 'i',
|
||||
describe("[close tag]", function()
|
||||
helpers.Test_withfile(run_data, {
|
||||
mode = "i",
|
||||
cursor_add = 0,
|
||||
before_each = function(value) end,
|
||||
before_each = function() end,
|
||||
})
|
||||
end)
|
317
tests/specs/renametag_spec.lua
Normal file
317
tests/specs/renametag_spec.lua
Normal file
@ -0,0 +1,317 @@
|
||||
local helpers = require("tests.utils.helpers")
|
||||
|
||||
local data = {
|
||||
{
|
||||
name = "html rename open tag",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "html rename open tag with attr",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala|> ]],
|
||||
},
|
||||
{
|
||||
name = "html rename close tag with attr",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = "html not rename close tag on char <",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 10,
|
||||
key = [[i<]],
|
||||
before = [[<div class="lla"> dsadsa |/button> ]],
|
||||
after = [[<div class="lla"> dsadsa <|/button> ]],
|
||||
},
|
||||
{
|
||||
|
||||
name = "html not rename close tag with not valid",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<di|v class="lla" ]],
|
||||
[[ dsadsa </div>]],
|
||||
},
|
||||
after = [[<lala class="lla" ]],
|
||||
},
|
||||
-- {
|
||||
-- only=true,
|
||||
-- name = "html not rename close tag if it have parent node map with child nod" ,
|
||||
-- filepath = './sample/index.html',
|
||||
-- filetype = "html",
|
||||
-- linenr = 12,
|
||||
-- key = [[ciwlala]],
|
||||
-- before = {
|
||||
-- [[<d|iv> </div>]],
|
||||
-- [[<div> </div>"]]
|
||||
-- },
|
||||
-- after = [[<d|iv> </div>]]
|
||||
-- },
|
||||
{
|
||||
|
||||
name = "html not rename close tag with not valid",
|
||||
filepath = "./sample/index.html",
|
||||
filetype = "html",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<div class="lla" </d|iv>]],
|
||||
},
|
||||
after = [[<div class="lla" </lala|>]],
|
||||
},
|
||||
{
|
||||
name = "typescriptreact rename open tag",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "typescriptreact rename open tag with attr",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "typescriptreact rename close tag with attr",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "html",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = "17 typescriptreact nested indentifer ",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<Opt.In|put></Opt.Input> ]],
|
||||
after = [[<Opt.lala|></Opt.lala> ]],
|
||||
},
|
||||
{
|
||||
name = "18 rename empty node ",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ilala]],
|
||||
before = [[<|><div></div></>]],
|
||||
after = [[<lala|><div></div></lala>]],
|
||||
},
|
||||
{
|
||||
name = "19 rename start tag on svelte ",
|
||||
filepath = "./sample/index.svelte",
|
||||
filetype = "svelte",
|
||||
linenr = 18,
|
||||
key = [[ciwlala]],
|
||||
before = [[<|data></data>]],
|
||||
after = [[<lala|></lala>]],
|
||||
},
|
||||
{
|
||||
name = "20 rename end tag on svelte ",
|
||||
filepath = "./sample/index.svelte",
|
||||
filetype = "svelte",
|
||||
linenr = 18,
|
||||
key = [[ciwlala]],
|
||||
before = [[<span></spa|n>]],
|
||||
after = [[<lala></lala>]],
|
||||
},
|
||||
{
|
||||
name = "21 rescript rename open tag",
|
||||
filepath = "./sample/index.res",
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "22 rescript rename open tag with attr",
|
||||
filepath = "./sample/index.res",
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "23 rescript rename close tag with attr",
|
||||
filepath = "./sample/index.res",
|
||||
filetype = "rescript",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = "24 test check rename same with parent",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = "ciwkey",
|
||||
before = {
|
||||
"<Container>",
|
||||
" <di|v>",
|
||||
"",
|
||||
" <span></span>",
|
||||
"</Container>",
|
||||
},
|
||||
after = {
|
||||
"<Container>",
|
||||
" <key>",
|
||||
"",
|
||||
" <span></span>",
|
||||
"</Container>",
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "25 rename start have same node with parent",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
"<div>",
|
||||
" <di|v>",
|
||||
" <span>test </span>",
|
||||
" </div>",
|
||||
"</div>",
|
||||
},
|
||||
after = {
|
||||
"<div>",
|
||||
" <lala>",
|
||||
" <span>test </span>",
|
||||
" </lala>",
|
||||
"</div>",
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "26 rename should not rename tag on attribute node",
|
||||
filepath = "./sample/index.tsx",
|
||||
filetype = "typescriptreact",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
"<div>",
|
||||
'<Navbar className="|a">',
|
||||
' <div className="flex flex-col">',
|
||||
' <div className="flex flex-row">',
|
||||
" </div>",
|
||||
" </div>",
|
||||
"</div>",
|
||||
},
|
||||
after = {
|
||||
"<div>",
|
||||
'<Navbar className="lala">',
|
||||
' <div className="flex flex-col">',
|
||||
' <div className="flex flex-row">',
|
||||
" </div>",
|
||||
" </div>",
|
||||
"</div>",
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "eruby rename open tag",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v> dsadsa </div> ]],
|
||||
after = [[<lala|> dsadsa </lala> ]],
|
||||
},
|
||||
{
|
||||
name = "eruby rename open tag with attr",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<di|v class="lla"> dsadsa </div> ]],
|
||||
after = [[<lala| class="lla"> dsadsa </lala|> ]],
|
||||
},
|
||||
{
|
||||
name = "eruby rename close tag with attr",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[ciwlala]],
|
||||
before = [[<div class="lla"> dsadsa </di|v> ]],
|
||||
after = [[<lala class="lla"> dsadsa </lal|a> ]],
|
||||
},
|
||||
{
|
||||
name = "eruby not rename close tag on char <",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 10,
|
||||
key = [[i<]],
|
||||
before = [[<div class="lla"> dsadsa |/button> ]],
|
||||
after = [[<div class="lla"> dsadsa <|/button> ]],
|
||||
},
|
||||
{
|
||||
name = "eruby not rename close tag with not valid",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<di|v class="lla" ]],
|
||||
[[ dsadsa </div>]],
|
||||
},
|
||||
after = [[<lala class="lla" ]],
|
||||
},
|
||||
{
|
||||
name = "eruby not rename close tag with not valid",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<div class="lla" </d|iv>]],
|
||||
},
|
||||
after = [[<div class="lla" </lala|>]],
|
||||
},
|
||||
{
|
||||
name = "eruby not rename tag-like ruby string",
|
||||
filepath = "./sample/index.html.erb",
|
||||
filetype = "eruby",
|
||||
linenr = 12,
|
||||
key = [[ciwlala]],
|
||||
before = {
|
||||
[[<%= <div></d|iv> %>]],
|
||||
},
|
||||
after = [[<%= <div></lala|> %>]],
|
||||
},
|
||||
}
|
||||
|
||||
local autotag = require("nvim-ts-autotag")
|
||||
autotag.test = true
|
||||
|
||||
local run_data = helpers.Test_filter(data)
|
||||
|
||||
describe("[rename tag]", function()
|
||||
helpers.Test_withfile(run_data, {
|
||||
cursor_add = 0,
|
||||
before_each = function() end,
|
||||
})
|
||||
end)
|
@ -1,189 +0,0 @@
|
||||
local utils = require('nvim-ts-autotag.utils')
|
||||
local log = require('nvim-ts-autotag._log')
|
||||
local api = vim.api
|
||||
|
||||
local helpers = {}
|
||||
|
||||
function helpers.feed(text, feed_opts, is_replace)
|
||||
feed_opts = feed_opts or 'n'
|
||||
if not is_replace then
|
||||
text = vim.api.nvim_replace_termcodes(text, true, false, true)
|
||||
end
|
||||
vim.api.nvim_feedkeys(text, feed_opts, true)
|
||||
end
|
||||
|
||||
function helpers.insert(text, is_replace)
|
||||
helpers.feed('i' .. text, 'x', is_replace)
|
||||
end
|
||||
|
||||
utils.insert_char = function(text)
|
||||
api.nvim_put({ text }, 'c', true, true)
|
||||
end
|
||||
|
||||
utils.feed = function(text, num)
|
||||
local result = ''
|
||||
for _ = 1, num, 1 do
|
||||
result = result .. text
|
||||
end
|
||||
api.nvim_feedkeys(
|
||||
api.nvim_replace_termcodes(result, true, false, true),
|
||||
'x',
|
||||
true
|
||||
)
|
||||
end
|
||||
|
||||
_G.eq = assert.are.same
|
||||
|
||||
_G.Test_filter = function(data)
|
||||
local run_data = {}
|
||||
for _, value in pairs(data) do
|
||||
if value.only == true then
|
||||
table.insert(run_data, value)
|
||||
break
|
||||
end
|
||||
end
|
||||
if #run_data == 0 then
|
||||
run_data = data
|
||||
end
|
||||
return run_data
|
||||
end
|
||||
|
||||
local compare_text = function(linenr, text_after, name, cursor_add, end_cursor)
|
||||
cursor_add = cursor_add or 0
|
||||
local new_text = vim.api.nvim_buf_get_lines(
|
||||
0,
|
||||
linenr - 1,
|
||||
linenr + #text_after - 1,
|
||||
true
|
||||
)
|
||||
for i = 1, #text_after, 1 do
|
||||
local t = string.gsub(text_after[i], '%|', '')
|
||||
if t
|
||||
and new_text[i]
|
||||
and t:gsub('%s+$', '') ~= new_text[i]:gsub('%s+$', '')
|
||||
then
|
||||
eq(t, new_text[i], '\n\n text error: ' .. name .. '\n')
|
||||
end
|
||||
local p_after = string.find(text_after[i], '%|')
|
||||
if p_after then
|
||||
local row, col = utils.get_cursor()
|
||||
if end_cursor then
|
||||
eq(row, linenr + i - 2, '\n\n cursor row error: ' .. name .. '\n')
|
||||
eq(
|
||||
col + 1,
|
||||
end_cursor,
|
||||
'\n\n end cursor column error : ' .. name .. '\n'
|
||||
)
|
||||
else
|
||||
eq(row, linenr + i - 2, '\n\n cursor row error: ' .. name .. '\n')
|
||||
p_after = p_after + cursor_add
|
||||
eq(
|
||||
col,
|
||||
math.max(p_after - 2, 0),
|
||||
'\n\n cursor column error : ' .. name .. '\n'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
_G.Test_withfile = function(test_data, cb)
|
||||
for _, value in pairs(test_data) do
|
||||
it('test ' .. value.name, function(done)
|
||||
local text_before = {}
|
||||
value.linenr = value.linenr or 1
|
||||
local pos_before = {
|
||||
linenr = value.linenr,
|
||||
colnr = 0,
|
||||
}
|
||||
if not vim.tbl_islist(value.before) then
|
||||
value.before = { value.before }
|
||||
end
|
||||
for index, text in pairs(value.before) do
|
||||
local txt = string.gsub(text, '%|', '')
|
||||
table.insert(text_before, txt)
|
||||
if string.match(text, '%|') then
|
||||
if string.find(text, '%|') then
|
||||
pos_before.colnr = string.find(text, '%|')
|
||||
pos_before.linenr = value.linenr + index - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if not vim.tbl_islist(value.after) then
|
||||
value.after = { value.after }
|
||||
end
|
||||
vim.bo.filetype = value.filetype or 'text'
|
||||
vim.cmd(':bd!')
|
||||
if cb.before_each then
|
||||
cb.before_each(value)
|
||||
end
|
||||
if vim.fn.filereadable(vim.fn.expand(value.filepath)) == 1 then
|
||||
vim.cmd(':e ' .. value.filepath)
|
||||
if value.filetype then
|
||||
vim.bo.filetype = value.filetype
|
||||
end
|
||||
vim.cmd(':e')
|
||||
else
|
||||
vim.cmd(':new')
|
||||
if value.filetype then
|
||||
vim.bo.filetype = value.filetype
|
||||
end
|
||||
end
|
||||
vim.api.nvim_buf_set_lines(
|
||||
0,
|
||||
value.linenr - 1,
|
||||
value.linenr + #text_before,
|
||||
false,
|
||||
text_before
|
||||
)
|
||||
vim.api.nvim_win_set_cursor(
|
||||
0,
|
||||
{ pos_before.linenr, pos_before.colnr - 1 }
|
||||
)
|
||||
log.debug('insert:' .. value.key)
|
||||
|
||||
if type(value.key) == 'string' then
|
||||
if cb.mode == 'i' then
|
||||
helpers.insert(value.key, value.not_replace_term_code)
|
||||
else
|
||||
helpers.feed(value.key, 'x')
|
||||
end
|
||||
else
|
||||
for _, key in pairs(value.key) do
|
||||
helpers.feed(key, 'x')
|
||||
vim.wait(1)
|
||||
end
|
||||
end
|
||||
vim.wait(2)
|
||||
helpers.feed('<esc>')
|
||||
compare_text(
|
||||
value.linenr,
|
||||
value.after,
|
||||
value.name,
|
||||
cb.cursor_add,
|
||||
value.end_cursor
|
||||
)
|
||||
if cb.after_each then
|
||||
cb.after_each(value)
|
||||
end
|
||||
vim.cmd(':bd!')
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
_G.dump_node = function(node)
|
||||
local text = utils.get_node_text(node)
|
||||
for _, txt in pairs(text) do
|
||||
log.debug(txt)
|
||||
end
|
||||
end
|
||||
|
||||
_G.dump_node_text = function(target)
|
||||
for node in target:iter_children() do
|
||||
local node_type = node:type()
|
||||
local text = utils.get_node_text(node)
|
||||
log.debug('type:' .. node_type .. ' ')
|
||||
log.debug(text)
|
||||
end
|
||||
end
|
13
tests/test.lua
Normal file
13
tests/test.lua
Normal file
@ -0,0 +1,13 @@
|
||||
require("tests.minimal_init")
|
||||
|
||||
---@type string
|
||||
local test_file = vim.v.argv[#vim.v.argv]
|
||||
if test_file == "" or not test_file:find("tests/specs/", nil, true) then
|
||||
test_file = "tests/specs"
|
||||
end
|
||||
print("[STARTUP] Running all tests in " .. test_file)
|
||||
|
||||
require("plenary.test_harness").test_directory(test_file, {
|
||||
minimal_init = "tests/minimal_init.lua",
|
||||
sequential = true,
|
||||
})
|
160
tests/utils/helpers.lua
Normal file
160
tests/utils/helpers.lua
Normal file
@ -0,0 +1,160 @@
|
||||
local test_utils = require("tests.utils.utils")
|
||||
|
||||
-- Some helpers depend on utilities from the main plugin, so we have to register the plugin on the
|
||||
-- path if it isn't already present
|
||||
test_utils.rtp_register_ts_autotag()
|
||||
|
||||
local utils = require("nvim-ts-autotag.utils")
|
||||
local log = require("nvim-ts-autotag._log")
|
||||
|
||||
local M = {}
|
||||
|
||||
local helpers = {}
|
||||
|
||||
function helpers.feed(text, feed_opts, is_replace)
|
||||
feed_opts = feed_opts or "n"
|
||||
if not is_replace then
|
||||
text = vim.api.nvim_replace_termcodes(text, true, false, true)
|
||||
end
|
||||
vim.api.nvim_feedkeys(text, feed_opts, true)
|
||||
end
|
||||
|
||||
function helpers.insert(text, is_replace)
|
||||
helpers.feed("i" .. text, "x", is_replace)
|
||||
end
|
||||
|
||||
M.insert_char = function(text)
|
||||
vim.api.nvim_put({ text }, "c", true, true)
|
||||
end
|
||||
|
||||
M.feed = function(text, num)
|
||||
local result = ""
|
||||
for _ = 1, num, 1 do
|
||||
result = result .. text
|
||||
end
|
||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(result, true, false, true), "x", true)
|
||||
end
|
||||
|
||||
M.Test_filter = function(data)
|
||||
local run_data = {}
|
||||
for _, value in pairs(data) do
|
||||
if value.only == true then
|
||||
table.insert(run_data, value)
|
||||
break
|
||||
end
|
||||
end
|
||||
if #run_data == 0 then
|
||||
run_data = data
|
||||
end
|
||||
return run_data
|
||||
end
|
||||
|
||||
local compare_text = function(linenr, text_after, name, cursor_add, end_cursor)
|
||||
cursor_add = cursor_add or 0
|
||||
local new_text = vim.api.nvim_buf_get_lines(0, linenr - 1, linenr + #text_after - 1, true)
|
||||
for i = 1, #text_after, 1 do
|
||||
local t = string.gsub(text_after[i], "%|", "")
|
||||
if t and new_text[i] and t:gsub("%s+$", "") ~= new_text[i]:gsub("%s+$", "") then
|
||||
assert.are.same(t, new_text[i], "\n\n text error: " .. name .. "\n")
|
||||
end
|
||||
local p_after = string.find(text_after[i], "%|")
|
||||
if p_after then
|
||||
local row, col = utils.get_cursor()
|
||||
if end_cursor then
|
||||
assert.are.same(row, linenr + i - 2, "\n\n cursor row error: " .. name .. "\n")
|
||||
assert.are.same(col + 1, end_cursor, "\n\n end cursor column error : " .. name .. "\n")
|
||||
else
|
||||
assert.are.same(row, linenr + i - 2, "\n\n cursor row error: " .. name .. "\n")
|
||||
p_after = p_after + cursor_add
|
||||
assert.are.same(col, math.max(p_after - 2, 0), "\n\n cursor column error : " .. name .. "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
M.Test_withfile = function(test_data, cb)
|
||||
for _, value in pairs(test_data) do
|
||||
it("test " .. value.name, function()
|
||||
local text_before = {}
|
||||
value.linenr = value.linenr or 1
|
||||
local pos_before = {
|
||||
linenr = value.linenr,
|
||||
colnr = 0,
|
||||
}
|
||||
if not vim.islist(value.before) then
|
||||
value.before = { value.before }
|
||||
end
|
||||
for index, text in pairs(value.before) do
|
||||
local txt = string.gsub(text, "%|", "")
|
||||
table.insert(text_before, txt)
|
||||
if string.match(text, "%|") then
|
||||
if string.find(text, "%|") then
|
||||
pos_before.colnr = string.find(text, "%|")
|
||||
pos_before.linenr = value.linenr + index - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if not vim.islist(value.after) then
|
||||
value.after = { value.after }
|
||||
end
|
||||
vim.bo.filetype = value.filetype or "text"
|
||||
vim.cmd(":bd!")
|
||||
if cb.before_each then
|
||||
cb.before_each(value)
|
||||
end
|
||||
if vim.fn.filereadable(vim.fn.expand(value.filepath)) == 1 then
|
||||
vim.cmd(":e " .. value.filepath)
|
||||
if value.filetype then
|
||||
vim.bo.filetype = value.filetype
|
||||
end
|
||||
vim.cmd(":e")
|
||||
else
|
||||
vim.cmd(":new")
|
||||
if value.filetype then
|
||||
vim.bo.filetype = value.filetype
|
||||
end
|
||||
end
|
||||
vim.api.nvim_buf_set_lines(0, value.linenr - 1, value.linenr + #text_before, false, text_before)
|
||||
vim.api.nvim_win_set_cursor(0, { pos_before.linenr, pos_before.colnr - 1 })
|
||||
log.debug("insert:" .. value.key)
|
||||
|
||||
if type(value.key) == "string" then
|
||||
if cb.mode == "i" then
|
||||
helpers.insert(value.key, value.not_replace_term_code)
|
||||
else
|
||||
helpers.feed(value.key, "x")
|
||||
end
|
||||
else
|
||||
for _, key in pairs(value.key) do
|
||||
helpers.feed(key, "x")
|
||||
vim.wait(1)
|
||||
end
|
||||
end
|
||||
vim.wait(2)
|
||||
helpers.feed("<esc>")
|
||||
compare_text(value.linenr, value.after, value.name, cb.cursor_add, value.end_cursor)
|
||||
if cb.after_each then
|
||||
cb.after_each(value)
|
||||
end
|
||||
vim.cmd(":bd!")
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
M.dump_node = function(node)
|
||||
local text = utils.get_node_text(node)
|
||||
for _, txt in pairs(text) do
|
||||
log.debug(txt)
|
||||
end
|
||||
end
|
||||
|
||||
M.dump_node_text = function(target)
|
||||
for node in target:iter_children() do
|
||||
local node_type = node:type()
|
||||
local text = utils.get_node_text(node)
|
||||
log.debug("type:" .. node_type .. " ")
|
||||
log.debug(text)
|
||||
end
|
||||
end
|
||||
return M
|
77
tests/utils/paths.lua
Normal file
77
tests/utils/paths.lua
Normal file
@ -0,0 +1,77 @@
|
||||
local M = {}
|
||||
|
||||
---Search up from the current file until we find the full path of the base `tests/` directory and
|
||||
---return it
|
||||
---@param test_file string Filename to look for in each directory, if it exists then stop the search
|
||||
---@return fun(): string dir A function wrapping the found directory
|
||||
local function search_dir_up(test_file)
|
||||
-- This is the path of the directory of the current file
|
||||
local cur_dir = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":p")
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
while not vim.uv.fs_stat(cur_dir .. "/" .. test_file, nil) and cur_dir ~= "/" do
|
||||
cur_dir = vim.fn.fnamemodify(cur_dir, ":h")
|
||||
end
|
||||
if cur_dir == "/" then
|
||||
error("Failed to locate the base 'tests/' directory!")
|
||||
end
|
||||
-- We return a wrapping function instead of a bare string so its easier to enforce "readonly"
|
||||
-- uses of the searched directory
|
||||
return function()
|
||||
return cur_dir
|
||||
end
|
||||
end
|
||||
|
||||
--- WARN: DO NOT MUTATE THESE VALUES!
|
||||
---
|
||||
--- Table containing useful paths to directories within the plugin
|
||||
M.static = {
|
||||
--- Full path of the `tests/` directory
|
||||
tests_dir = search_dir_up("test.lua"),
|
||||
--- Full base path of the plugin where the `.git` directory resides
|
||||
ts_autotag_dir = search_dir_up(".git"),
|
||||
}
|
||||
|
||||
---@class nvim-ts-autotag.Root
|
||||
---@field package path string
|
||||
local Root = {
|
||||
path = M.static.tests_dir(),
|
||||
}
|
||||
|
||||
---@package
|
||||
---@nodiscard
|
||||
---@return nvim-ts-autotag.Root
|
||||
function Root:new(o)
|
||||
o = o or {}
|
||||
|
||||
setmetatable(o, self)
|
||||
-- The last part of the path must be a "/" to work correctly
|
||||
if self.path:sub(#self.path, #self.path) ~= "/" then
|
||||
self.path = self.path .. "/"
|
||||
end
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
--- Create a new instance, pushing the given path onto it
|
||||
---@nodiscard
|
||||
---@param path string The new path to add
|
||||
---@return nvim-ts-autotag.Root
|
||||
function Root:push(path)
|
||||
local new_root = self:new()
|
||||
-- Since we're extending the path, the current path must be a directory, ensure it ends in a "/"
|
||||
if new_root.path:sub(#new_root.path) ~= "/" then
|
||||
new_root.path = new_root.path .. "/"
|
||||
end
|
||||
new_root.path = new_root.path .. path
|
||||
return new_root
|
||||
end
|
||||
|
||||
--- Get the current path string
|
||||
---@return string path
|
||||
function Root:get()
|
||||
return self.path
|
||||
end
|
||||
|
||||
M.Root = Root:new()
|
||||
|
||||
return M
|
14
tests/utils/utils.lua
Normal file
14
tests/utils/utils.lua
Normal file
@ -0,0 +1,14 @@
|
||||
local path_utils = require("tests.utils.paths")
|
||||
local M = {}
|
||||
|
||||
M.paths = path_utils
|
||||
|
||||
--- Register the main plugin (`nvim-ts-autotag`) on the runtimepath if it hasn't already been
|
||||
--- registered
|
||||
M.rtp_register_ts_autotag = function()
|
||||
if not vim.list_contains(vim.opt.runtimepath, path_utils.static.ts_autotag_dir()) then
|
||||
vim.opt.runtimepath:append(path_utils.static.ts_autotag_dir())
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
Loading…
x
Reference in New Issue
Block a user