Simple yet convenient Neovim plugin for tabbing in and out of brackets, parentheses, quotes, and more.
demo.mp4
Unlike other similar plugins that tabout out of treesitter
nodes,
neotab.nvim
uses simple logic to move in and out of pairs or
find the next best matching pair.
This approach ensures a consistent behavior regardless of the file type
or the state of the parsed treesitter
tree.
This plugin doesn't aim to replace existing similar plugins. Instead, it offers a different, less fancy but more predictable and consistent method for tabbing out of pairs.
{
"kawre/neotab.nvim",
event = "InsertEnter",
opts = {
-- configuration goes here
},
}
To see full configuration types check types.lua
{
tabkey = "<Tab>",
act_as_tab = true,
behavior = "nested", ---@type ntab.behavior
pairs = { ---@type ntab.pair[]
{ open = "(", close = ")" },
{ open = "[", close = "]" },
{ open = "{", close = "}" },
{ open = "'", close = "'" },
{ open = '"', close = '"' },
{ open = "`", close = "`" },
{ open = "<", close = ">" },
},
exclude = {},
smart_punctuators = {
enabled = false,
semicolon = {
enabled = false,
ft = { "cs", "c", "cpp", "java" },
},
escape = {
enabled = false,
triggers = {}, ---@type table<string, ntab.trigger>
},
},
}
the key that triggers the tabout
action
to not bind to any key, set it to ""
tabkey = "<Tab>",
fallback to tab, if tabout
action is not available
act_as_tab = true,
prioritize valid nested pairs first
nested.mp4
prioritize closing pair first
closing.mp4
ignore these filetypes for tabout
exclude = {},
intellij like behavior for semicolons
semicolon = {
enabled = false,
ft = { "cs", "c", "cpp", "java" },
},
escapes pairs with specified punctuators
configuration from demo.mp4
escape = {
enabled = true,
triggers = { ---@type table<string, ntab.trigger>
["+"] = {
pairs = {
{ open = '"', close = '"' },
},
-- string.format(format, typed_char)
format = " %s ", -- " + "
ft = { "java" },
},
[","] = {
pairs = {
{ open = "'", close = "'" },
{ open = '"', close = '"' },
},
format = "%s ", -- ", "
},
["="] = {
pairs = {
{ open = "(", close = ")" },
},
ft = { "javascript", "typescript" },
format = " %s> ", -- ` => `
-- string.match(text_between_pairs, cond)
cond = "^$", -- match only pairs with empty content
},
},
},
When the ft
is defined, the corresponding trigger will apply to the specified
file types. Otherwise, it will be effective no matter the file type.
There is a high chance neotab.nvim won't work out of the box, because of other plugins/config overriding the tabkey keymap.
To help you find the location that overrides the tabkey you can use
:verbose imap <Tab>
Mode | plug | action |
---|---|---|
i | <Plug>(neotab-out) |
tabout |
i | <Plug>(neotab-out-luasnip) |
(experimental) tabout before luasnip |
-
set tabkey to
""
-
set
<Plug>(neotab-out)
as a fallback to luasnip
{
"L3MON4D3/LuaSnip",
build = "make install_jsregexp",
dependencies = { "neotab.nvim", },
keys = {
{
"<Tab>",
function()
return require("luasnip").jumpable(1) --
and "<Plug>luasnip-jump-next"
or "<Plug>(neotab-out)"
end,
expr = true,
silent = true,
mode = "i",
},
},
}
-
set tabkey to
""
-
set
require("neotab").tabout()
as a fallback to both nvim-cmp and luasnip
local cmp = require("cmp")
local luasnip = require("luasnip")
local neotab = require("neotab")
["<Tab>"] = cmp.mapping(function()
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.jumpable(1) then
luasnip.jump(1)
else
neotab.tabout()
end
end),