Open-source project
bullets-vim/bullets.vim avatar
bullets-vim/bullets.vim

Bullets.vim: automatic outline bullets for Vim and Neovim

🔫 Bullets.vim is a Vim/NeoVim plugin for automated bullet lists.

606 stars48 forksLuaNOASSERTION

At a glance

What is it?
Bullets.vim turns a typed hyphen or asterisk into a maintained outline, handling renumbering, demotion, promotion and nested checkboxes with a single key press. It is a Vim plugin first, with a separate Lua port for Neovim.
Who is it for?
Bullets.vim suits Vim users who write long markdown, org or plain text outlines and want list maintenance to happen while typing, especially with mixed bullet styles or task checkboxes. It is the wrong choice if you are on Neovim, where the README points you to the separate bullets.nvim port, or if you want only one small behaviour and would rather set a filetype option yourself.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 99 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

A bullet list that maintains itself while you type

Bullets.vim is a Vim plugin for automated bullet lists. The core interaction is short. In a markdown or text file you start a bulleted list using `-` or `*`, and pressing return creates the next list item, already carrying the right marker and indentation.

The plugin is aimed at the messy part of that interaction rather than the first keystroke. The README shows demos for simple bullets, for wrapped text bullets where the continuation lines of an item have to line up under the text rather than under the marker, and for renumbering lines. That third one is the substantive feature. Ordered lists in plain text drift out of sequence as soon as you insert, demote or delete an item, and the plugin renumbers the affected list as you work.

The result is a plugin that changes the shape of writing rather than adding a command you remember to run. Whether that is a good trade depends on how much of your day is spent in structured text.

Two installation routes, one is native Vim 8.1

The README describes installation with the Vim 8.1 native package manager and with VimPlug.

For the native package manager you clone into the start directory of your packages folder.

vim
`.vim/pack/plugins/start`

You then have to make sure your vimrc includes `packloadall`, otherwise the plugin is on disk but never loaded.

For VimPlug the configuration is a single line in your plugin list.

vim
Plug 'bullets-vim/bullets.vim'

After that you source your Vim config and run `:PlugInstall`, and the plugin is fetched. The top level of the repository has the usual Vim plugin layout with a plugin/ directory, plus doc/, test/, img/ and a LICENSE.md, so the documentation and the test files ship with the code rather than living on a separate site.

Neovim users are pointed elsewhere

The first thing the README says is that Neovim users should check out the Neovim Lua port at the bullets-vim/bullets.nvim repository. That is worth taking at face value rather than trying to force the Vimscript plugin into a Lua plugin manager.

The repository metadata for this project lists Lua as its primary language, which is a sign of how the two projects relate. The port is the newer direction, and a Neovim user who installs bullets.vim is choosing the older implementation for the sake of one workflow.

The topic tags tell you the format range the plugin is designed for: markdown, org-mode, lists and GFM style checkboxes. The GFM checkbox support is the part that pushes beyond plain text lists, and it is configurable down to the characters used for partial completion.

Filetypes decide where the plugin acts at all

The plugin does not run everywhere by default, and the filter is explicit.

vim
let g:bullets_enabled_file_types = [
    \ 'markdown',
    \ 'text',
    \ 'gitcommit',
    \ 'scratch'
    \]

So the default set is markdown, plain text, git commit messages and scratch buffers. If you write org-mode files, that type is not in the list, and the plugin will not touch them until you add it.

A second switch controls whether the plugin runs in buffers with no filetype.

vim
let g:bullets_enable_in_empty_buffers = 0 " default = 1

This matters more than it looks. A new unnamed buffer has no filetype, so with the option enabled the plugin is active in it, and pressing return in a scratch buffer can produce a bullet. Setting it to zero restricts the behaviour to files you actually intend to be lists.

Default mappings can be disabled, prefixed or replaced

The plugin installs its own key mappings, and all three aspects of that are configurable. The first is whether mappings are set at all.

vim
let g:bullets_set_mappings = 0 " default = 1

The second is a prefix for the default mappings, which is how you keep them from colliding with your own keys.

vim
let g:bullets_mapping_leader = '<M-b>' " default = ''

The third is a full custom list. The README documents g:bullets_custom_mappings as an array of mode, key and plug command triples, covering insert and normal mode newline handling, a visual mode renumber, a toggle checkbox mapping under your leader, and demote and promote mappings in insert, normal and visual mode. The mapping list runs to more than a dozen entries, and the README adds a note that matters for anyone configuring it: mappings set this way respect the g:bullets_enabled_file_types filter and the empty buffer option, while mappings set by hand with the same plug commands apply to all file types.

Renumbering and checkbox completion are separate switches

Two behaviours that people expect to be tied together are in fact independent options.

The first is automatic renumbering of an ordered list when the indent level changes or a new bullet is inserted, which the README shows producing a corrected sequence where an item that was 3 becomes 2 after the item above it is demoted.

vim
let g:bullets_renumber_on_change = 1 " default = 1

Setting it to zero leaves the numbers as they are, so you get a list that shows a gap after the edit.

The second is nested checkboxes, where checking a parent marks its children and a parent with mixed children gets a partial marker.

vim
let g:bullets_nested_checkboxes = 1 " default = 1

The markers themselves are configurable, and the README explains the arithmetic. Unchecked, checked and partial markers are defined as characters, and when more than two markers are defined each character between the first and the last is used to indicate the proportion of children that are checked, where each marker corresponds to one nth of the total. The default gives three partial markers, `.`, `o` and `O`, for 33 percent, 66 percent and up to but not including 100 percent.

Outline levels, padding and spacing rules

The bullet style of a nested level is not hard coded. g:bullets_outline_levels takes an ordered list describing the hierarchical levels from the outermost inward, and the README lists the available options: ROM and rom for upper and lower case Roman numerals, ABC and abc for alphabetic characters, num for numbers, std with a hyphen, asterisk or plus for standard bullets, and chk for checkboxes. The same marker cannot be used twice in the list.

vim
let g:bullets_outline_levels = ['num', 'abc', 'std-']

A second group of options controls formatting around the marker. g:bullets_max_alpha_characters caps how many alphabetic characters a bullet may use, which matters once you pass z. g:bullets_pad_right adds extra padding between the bullet and the text when bullets are multiple characters long, so the text stays aligned against the longest bullet in the list, and setting it to zero removes that padding. g:bullets_line_spacing sets the number of blank lines between bullets, with one meaning no blank lines. g:bullets_auto_indent_after_colon indents a new bullet when the previous bullet ended with a colon, which is how nested sub items are usually introduced in prose.

Finally, g:bullets_delete_last_bullet_if_empty controls what happens to an empty bullet when you press return or open a new line. The three documented behaviours are deleting it, leaving it in place, or removing the last empty bullet only when it is the outermost one.

Maintenance, tooling and the licence question

The release list shows 1.10.0 on 2022-10-08, v1.11.0 on 2024-01-31 and v2.0.0 on 2025-02-11, and the last push was on 2026-07-16. The repository is not archived, and the tree carries committed.toml, lefthook.yml, mise.toml, dprint.json and renovate.json, which suggests the project uses commit message conventions, git hooks, tool version pinning, a formatter configuration and automated dependency updates.

The licence is the detail to check before you vendor it. The licence field is a custom one that GitHub cannot classify, and the terms live in LICENSE.md in the repository root. The README does not reproduce them, so read that file rather than assuming it matches the permissive licences common to Vim plugins. The same caution applies to the code: the repository includes a test/ directory, but nothing in the documentation claims a coverage figure or a test suite you can rely on for a specific Vim version.

One practical boundary deserves repeating. This is a Vim plugin with a Neovim port elsewhere. If your day is in Neovim, the decisions you make about mappings, filetypes and outline levels belong in the bullets.nvim repository, not here.

Editorial conclusion

Bullets.vim suits Vim users who write long markdown, org or plain text outlines and want list maintenance to happen while typing, especially with mixed bullet styles or task checkboxes. It is the wrong choice if you are on Neovim, where the README points you to the separate bullets.nvim port, or if you want only one small behaviour and would rather set a filetype option yourself. Verify first which of your file types are enabled, since g:bullets_enabled_file_types defaults to markdown, text, gitcommit and scratch, and check the licence file, because the repository uses a custom licence rather than a standard one.

Frequently asked questions

How do I install Bullets.vim with VimPlug?

Add Plug 'bullets-vim/bullets.vim' to your plugin list, then source your Vim config and run :PlugInstall. The README also documents the Vim 8.1 native package manager route, where you clone into .vim/pack/plugins/start and make sure packloadall is included in your vimrc.

Which file types does Bullets.vim act on by default?

The default value of g:bullets_enabled_file_types lists markdown, text, gitcommit and scratch. Separately, g:bullets_enable_in_empty_buffers controls whether the plugin runs in buffers with no filetype, and it defaults to 1.

Is there a Neovim version of Bullets.vim?

The README points Neovim users to the Neovim Lua port at the bullets-vim/bullets.nvim repository. The Vim plugin described here is the one documented on this repository's README.

How do I stop Bullets.vim from installing its own key mappings?

Set g:bullets_set_mappings to 0. You can also keep the defaults but move them behind a prefix with g:bullets_mapping_leader, or replace them entirely with the g:bullets_custom_mappings array, whose entries respect the file type filter and the empty buffer option.

Official sources

  1. bullets-vim/bullets.vim on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
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/bullets-vim-bullets-vim.svg)](https://hysenlabs.com/projects/bullets-vim-bullets-vim)

Was this useful?