CLI tool
m4xshen/autoclose.nvim avatar
m4xshen/autoclose.nvim

autoclose.nvim: minimalist bracket pairing for Neovim

A minimalist Neovim plugin that auto pairs & closes brackets

650 stars23 forksLuaMIT

At a glance

What is it?
autoclose.nvim is a small Lua plugin for Neovim that inserts closing brackets, escapes them when typed again, deletes the pair as one unit and auto-indents inside a pair. It suits people who want reliable pairing without the feature set of nvim-autopairs, and it is deliberately small enough to read in one sitting.
Who is it for?
autoclose.nvim is the right choice for a Neovim user who wants the four pairing behaviours and little else: it does that in Lua with no dependencies and a configuration table you can read in a minute. It is the wrong choice if you rely on Treesitter-aware pair detection, fast wrap, or a plugin that composes with other pair plugins, since the README itself points at nvim-autopairs and ultimate-autopair.nvim for those.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 40 days ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the plugin actually does: four behaviours and nothing else

autoclose.nvim is described as a minimalist Neovim plugin that auto pairs and closes brackets, written in 100% Lua. The feature list is short, and reading it tells you the whole scope of the project.

The first is auto-close: pressing an opening character inserts both the opening and the closing character and places the cursor between them. The second is auto-delete, which the README says works in BS and C-W, so backspacing over an opening character removes its partner too instead of leaving a stray bracket behind. The third is auto-escape, where typing the closing character again skips over the existing one rather than inserting a second copy. The fourth is auto-indent, which works in CR and S-CR and, unlike the others, only in insert mode.

That last detail matters in practice. The README states that most functions work in both insert and command-line mode, so the pairing behaviour is active when you type a search pattern or a command, and the auto-indent path is insert-only because there is no buffer indentation to compute in the command line.

The repository layout matches the claim. The tree is lua/, doc/, an .editorconfig, a stylua.toml and the README, with no build step, no dependencies and no generated code. A stylua configuration file means the source is formatted with a single tool, which is what you would expect from a plugin small enough to maintain in one file's worth of logic.

The requirement is Neovim v0.7.0 or later, which is a low floor by current standards and means the plugin will load in older setups without version negotiation.

Installing with vim-plug or packer.nvim and calling setup()

Installation follows the shape of every other Neovim plugin, with two manager examples the README lists. With vim-plug:

VimL
Plug 'm4xshen/autoclose.nvim'

With packer.nvim:

Lua
use 'm4xshen/autoclose.nvim'

The second step is the setup call in your init.lua, and the README notes that for default settings you call setup without an argument:

Lua
require("autoclose").setup()

That single line matters, because the plugin is not automatic on load. Autocommands that fire on every keystroke are registered by setup, so a user who copies only the package manager line gets a plugin that is installed and inactive. It is a small thing, but it is the first support question this plugin gets.

For the other managers the README gives the general instruction to install via your favorite package manager, so any manager that can pull a Git repository will work. Nothing in the plugin depends on the manager, since it is a plain Lua module loaded through require.

From there, the plugin starts behaving as the four documented behaviours: typing an opening bracket inserts the pair, typing it again escapes, backspace removes both, and pressing enter inside a pair indents.

The keys table: pair, close, escape and per-filetype switches

The configuration is a single table passed to setup, merged with the defaults, and it has two halves: keys and options.

Each entry in keys describes one character. The pair option is the string representing the opening and closing characters, and the README is explicit that it should be a two-character string with the opening character first and the closing one second. The close option, when true, means pressing the character inserts both halves and places the cursor between them. The escape option, when true, means pressing the character again escapes it instead of inserting another closing character.

The remaining two fields control where a key applies. disabled_filetypes is a table of filetypes where that specific key should not be autoclosed, and enabled_filetypes restricts a key to a set of filetypes, taking precedence over disabled_filetypes.

Adding a new pair is a matter of describing it:

Lua
require("autoclose").setup({
   keys = {
      ["$"] = { escape = true, close = true, pair = "$$", disabled_filetypes = {} },
   },
})

Overwriting part of a default is also allowed, and the README says items not specified remain the default. Removing the escape behaviour of the greater-than sign, for instance, is a single field:

Lua
require("autoclose").setup({
   keys = {
      [">"] = { escape = false },
   },
})

The merge semantics are what make this pleasant to configure. You can flip one boolean on one key without restating the pair or the filetype rules, and the merge keeps everything else intact.

The options table: touch_regex, pair_spaces and filetype scopes

The options table holds the settings that apply to the plugin as a whole rather than to one key.

disabled_filetypes is a table of strings, and the plugin is disabled under those filetypes. The default value is a table containing text, which means plain text files are untouched by default and everything else gets pairing. Adding markdown is a one-line change:

Lua
require("autoclose").setup({
   options = {
      disabled_filetypes = { "text", "markdown" },
   },
})

disable_when_touch is a boolean, false by default, and it works together with touch_regex, a string that defaults to the pattern [%w(%[{], which the README glosses as alphanumeric characters or the characters (, [ or {. When the option is on and the cursor touches a character matching that pattern, the pair is not closed. The README's illustration is minimal: the buffer contains the word word with the cursor at the start of it, and pressing ( gives an opening parenthesis but no closing one, because the cursor is touching the w. The pattern exists so that typing a bracket right after an identifier does not produce a stray closing bracket that you then have to delete.

pair_spaces is a boolean, false by default, and when the cursor is inside a pair of keys it pairs the spaces as well. The README's example starts from an import statement with the cursor between the braces, and after inserting a space it becomes import { | }, with the space padded on both sides so the closing brace stays aligned with the opening one.

auto_indent enables the auto-indent feature and defaults to true, while disable_command_mode disables autoclose for command mode globally and also defaults to false.

The default configuration, read as the plugin's specification

The README prints the default config in full, and it is the most efficient way to understand the design, because the defaults encode the decisions the author made about which characters should pair and which should only escape:

Lua
local config = {
   keys = {
      ["("] = { escape = false, close = true, pair = "()" },
      ["["] = { escape = false, close = true, pair = "[]" },
      ["{"] = { escape = false, close = true, pair = "{}" },

      [">"] = { escape = true, close = false, pair = "<>" },
      [")"] = { escape = true, close = false, pair = "()" },
      ["]"] = { escape = true, close = false, pair = "[]" },
      ["}"] = { escape = true, close = false, pair = "{}" },

      ['"'] = { escape = true, close = true, pair = '""' },
      ["'"] = { escape = true, close = true, pair = "''" },
      ["`"] = { escape = true, close = true, pair = "``" },
   },
   options = {
      disabled_filetypes = { "text" },
      disable_when_touch = false,
      touch_regex = "[%w(%[{]",
      pair_spaces = false,
      auto_indent = true,
      disable_command_mode = false,
   },
}

Three things stand out. The opening brackets, the closing brackets and the angle brackets have close set to false, because typing ( must insert the pair while typing ) only ever skips over an existing one. The quote characters are different: the double quote, the single quote and the backtick all have close set to true, since an empty quoted string is what you usually want after typing the opening quote. The angle bracket is listed with pair <>, so the greater-than sign is treated as the closing half of that pair rather than as an operator, with escape enabled so that typing it inside an existing pair skips out rather than inserting another.

The default option values are all conservative: pairing is on everywhere except text files, the touch rule is off, spaces are not paired, auto-indent is on, and command-line mode keeps its pairing.

autoclose.nvim against nvim-autopairs and ultimate-autopair

The README has a section comparing this plugin to others, and it is unusually candid about what you give up. It notes that plugins such as nvim-autopairs and ultimate-autopair.nvim provide a wider range of features, naming fast wrap and Treesitter pair checking among them, but that some users may not need all of that. The claim is that if you want the basic functionality of editing with pairs, autoclose.nvim achieves the same thing in a simpler and faster way.

The difference in approach is genuine and worth spelling out. The larger plugins are built to integrate with the rest of the editor: a Treesitter-aware plugin can ask the parser whether a bracket actually closes a construct rather than guessing from characters, and a wrap plugin can transform a selection between brackets. autoclose.nvim works purely at the character level, with a regex for the touch check and no parser involvement at all. That is why the touch behaviour is the weak point: the plugin cannot know that the bracket you typed after an identifier is a function call rather than a grouping, so it stays out of the way by not closing.

There is a second practical difference. The heavier plugins are usually Lua modules with their own configuration surface, sometimes with a plugin ecosystem of their own. This one has a single table with two sections, documented key by key, which is why a whole configuration file fits on a screen.

If you are choosing between them, the question is whether you want the plugin to know about your code's structure or only about your characters.

Maintenance, licensing and what the project does not promise

The licence is MIT, and the repository carries a LICENSE file, so the plugin can be vendored, modified or embedded without ceremony.

On maintenance, the facts are modest. The last push to the repository was on 2026-09-13 and the repository is not archived, so the work is not finished. But the repository has no GitHub releases at all, which means there is no tagged version to pin in a plugin manager spec, no changelog in the release sense, and no way to upgrade to a known good state other than tracking the branch. For a plugin of this size that is a workable arrangement, since the whole implementation fits in a Lua module, but it does mean you cannot say which version you are running.

The doc/ directory in the tree is the help file, which is where Neovim's own :help documentation for the plugin lives, generated for panvimdoc as the README's ignore markers suggest.

What the project does not promise is as important. There is no compatibility with Vim, only Neovim, and the v0.7.0 floor means a very old Neovim is still acceptable but a Vim user is out. There is no Treesitter integration, no wrap, no comments or snippets handling, and no per-language behaviour beyond the filetype tables, which is why the plugin competes on the grounds of being small rather than on the grounds of being capable.

The realistic risk is not that the plugin breaks, but that it does exactly what it says and nothing more, and a user moving from a heavier pairing plugin has to accept that difference rather than configure their way out of it.

Editorial conclusion

autoclose.nvim is the right choice for a Neovim user who wants the four pairing behaviours and little else: it does that in Lua with no dependencies and a configuration table you can read in a minute. It is the wrong choice if you rely on Treesitter-aware pair detection, fast wrap, or a plugin that composes with other pair plugins, since the README itself points at nvim-autopairs and ultimate-autopair.nvim for those. Verify first that your Neovim is at least v0.7.0, that the plugin manager command you use is one of the two the README names, and that the touch_regex behaviour matches how you type, because disable_when_touch can silently stop a pair from closing in code where you type right after an identifier.

Frequently asked questions

What are the requirements for autoclose.nvim?

The README lists Neovim v0.7.0 or later as the only requirement, and the plugin is written in 100% Lua with no dependencies. Vim itself is not supported.

How do I install and enable autoclose.nvim?

Install it with your package manager, for example Plug 'm4xshen/autoclose.nvim' for vim-plug or use 'm4xshen/autoclose.nvim' for packer.nvim, then call require("autoclose").setup() in your init.lua. The README notes that calling setup without an argument uses the default settings.

How do I add a new bracket pair such as $$?

Add an entry to the keys table with the pair string, the opening character first, for example ["$"] = { escape = true, close = true, pair = "$$" }. The table you pass to setup is merged with the defaults, so unspecified fields keep their default values.

How do I turn autoclose off in markdown files?

Add the filetype to options.disabled_filetypes, for example disabled_filetypes = { "text", "markdown" }, which replaces the default table containing text. A key can also be limited per filetype with disabled_filetypes and enabled_filetypes inside its own entry in the keys table.

What does disable_when_touch do?

When it is set to true, the auto-close function is disabled if the cursor touches a character matching touch_regex, which defaults to the pattern for alphanumeric characters or the characters (, [ and {. The README illustrates it with the cursor at the start of a word, where pressing ( inserts an opening bracket without a matching close.

Does autoclose.nvim work in command-line mode?

The README says most functions work in both insert and command-line mode, with auto-indent as the exception since it is insert-only. The global option disable_command_mode, false by default, turns autoclose off in command mode when set to true.

Official sources

  1. Issues
  2. License: MIT
  3. m4xshen/autoclose.nvim on GitHub
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/m4xshen-autoclose-nvim.svg)](https://hysenlabs.com/projects/m4xshen-autoclose-nvim)

Was this useful?