Skip to content

bullets-vim/bullets.vim

Repository files navigation

Bullets.vim

All Contributors

ℹ️ Looking for help/maintainers #126

Description

Bullets.vim is a Vim plugin for automated bullet lists.

Simple bullets:

demo

Wrapped text bullets: wrapped bullets

Renumbering lines: renumber demo

Installation

With Vim 8.1+ native package manager:

Clone into

.vim/pack/plugins/start

Make sure to include packloadall in your vimrc.

With VimPlug:

Plug 'bullets-vim/bullets.vim'

Then source your bundle file and run :PlugInstall.

Usage

In markdown or a text file start a bulleted list using - or *. Press return to go to the next line, a new list item will be created.

Configuration

Filetypes

You can choose which file types this plugin will work on:

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

You can disable this plugin for empty buffers (no filetype):

let g:bullets_enable_in_empty_buffers = 0 " default = 1

Enable/disable default key mappings:

let g:bullets_set_mappings = 0 " default = 1

Add a leader key before default mappings:

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

Customize key mappings:

let g:bullets_set_mappings = 0 " disable adding default key mappings, default = 1

" default = []
" N.B. You can set these mappings as-is without using this g:bullets_custom_mappings option but it
" will apply in this case for all file types while when using g:bullets_custom_mappings it would
" take into account file types filter set in g:bullets_enabled_file_types, and also
" g:bullets_enable_in_empty_buffers option.
let g:bullets_custom_mappings = [
  \ ['imap', '<cr>', '<Plug>(bullets-newline)'],
  \ ['inoremap', '<C-cr>', '<cr>'],
  \
  \ ['nmap', 'o', '<Plug>(bullets-newline)'],
  \
  \ ['vmap', 'gN', '<Plug>(bullets-renumber)'],
  \ ['nmap', 'gN', '<Plug>(bullets-renumber)'],
  \
  \ ['nmap', '<leader>x', '<Plug>(bullets-toggle-checkbox)'],
  \
  \ ['imap', '<C-t>', '<Plug>(bullets-demote)'],
  \ ['nmap', '>>', '<Plug>(bullets-demote)'],
  \ ['vmap', '>', '<Plug>(bullets-demote)'],
  \ ['imap', '<C-d>', '<Plug>(bullets-promote)'],
  \ ['nmap', '<<', '<Plug>(bullets-promote)'],
  \ ['vmap', '<', '<Plug>(bullets-promote)'],
  \ ]

Enable/disable deleting the last empty bullet when hitting <cr> (insert mode) or o (normal mode):

let g:bullets_delete_last_bullet_if_empty = 0 " default = 1

Line spacing between bullets (1 = no blank lines, 2 = one blank line, etc.):

let g:bullets_line_spacing = 2 " default = 1

Don't/add extra padding between the bullet and text when bullets are multiple characters long:

let g:bullets_pad_right = 1 " default = 1
" I. text
" II. text
" III. text
" IV.  text
" V.   text
"     ^ extra spaces to align the text with the longest bullet

let g:bullets_pad_right = 0
" I. text
" II. text
" III. text
" IV. text
"    ^ no extra space between bullet and text

Indent new bullets when the previous bullet ends with a colon:

let g:bullets_auto_indent_after_colon = 1 " default = 1
" a. text
" b. text:
"   i. text

Maximum number of alphabetic characters to use for bullets:

let g:bullets_max_alpha_characters = 2 " default = 2
" ...
" y. text
" z. text
" aa. text
" ab. text

let g:bullets_max_alpha_characters = 1
" ...
" y. text
" z. text
" text

Nested outline bullet levels:

let g:bullets_outline_levels = ['ROM', 'ABC', 'num', 'abc', 'rom', 'std-', 'std*', 'std+'] " default
" Ordered list containing the heirarchical bullet levels, starting from the outer most level.
" Available bullet level options (cannot use the same marker more than once)
" ROM/rom = upper/lower case Roman numerals (e.g., I, II, III, IV)
" ABC/abc = upper/lower case alphabetic characters (e.g., A, B, C)
" std[-/*/+] = standard bullets using a hyphen (-), asterisk (*), or plus (+) as the marker.
" chk = checkbox (- [ ])

let g:bullets_outline_levels = ['num', 'abc', 'std-']
" Example [keys pressed to get this bullet]:
" 1. first parent
"   a. child bullet [ <cr><C-t> ]
"     - unordered bullet [ <cr><C-t> ]
"   b. second child bullet [ <cr><C-d> ]
" 2. second parent [ <cr><C-d> ]

Enable/disable automatically renumbering the current ordered bullet list when changing the indent level of bullets or inserting a new bullet:

let g:bullets_renumber_on_change = 1 " default = 1
" Example 1:
" 1. first existing bullet
"   a. second existing bullet [ hit <C-t> ]
" 2. third existing bullet [ this got renumbered 3 -> 2 when bullet 2 got demoted ]
"
" Example 2:
" 1. first existing bullet
" 2. second existing bullet [ use <cr>/o to add a new bullet below this ]
" 3. new bullet
" 4. third existing bullet [ this got renumbered 3 -> 2 when bullet 2 got demoted ]

let g:bullets_renumber_on_change = 0
" Example:
" 1. first existing bullet
"   a. second existing bullet [ hit <C-t> ]
" 3. third existing bullet [ no renumbering so this bullet remained `3` ]
"
" Example 2:
" 1. first existing bullet
" 2. second existing bullet [ use <cr>/o to add a new bullet below this ]
" 3. new bullet
" 3. third existing bullet [ no renumbering so this bullet remained `3` ]

Enable/disable toggling parent and child checkboxes to indicate "completion" of child checkboxes:

let g:bullets_nested_checkboxes = 1 " default = 1
" Example:
" - [ ] first bullet
"   - [ ] child bullet  [ type <leader>x ]
"     - [ ] sub-child
"   - [ ] child bullet
"
" Result:
" - [o] first bullet   [ <- indicates partial completion of sub-tasks ]
"   - [X] child bullet
"     - [X] sub-child  [ <- children get checked when parents get checked ]
"   - [ ] child bullet

Define the checkbox markers to use to indicate unchecked, checked, and "partially" checked. When only two marker characters are defined, the use of partial completion markers will be disabled. If more than two markers are defined, each character between the first and last characters will be used to indicate a percentage of the child checkboxes that are checked. Each marker corresponds to 1/n, where n is the number of partial completion markers. By default, there are three partial completion markers, ., o, and O, corresponding to 33%, 66%, and up to but less than 100%, respectively. Note that unchecked ([ ]) and checked ([x] or [X]) statuses using the default markers are always valid, even if you set custom markers for unchecked and checked.

let g:bullets_checkbox_markers = ' .oOX'
" Example:
" - [o] parent bullet  [ <- `o` indicates 66% - 99% of children are checked ]
"   - [ ] child bullet
"   - [.] child bullet [ <- partial completions don't count as complete ]
"     - [ ] sub-child bullet [ <- 1/4 of children checked so parent is `.` ]
"     - [ ] sub-child bullet
"     - [ ] sub-child bullet
"     - [X] sub-child bullet
"   - [X] child bullet
"   - [X] child bullet
"
" You can use fancy markers:
" let g:bullets_checkbox_markers = 'βœ—β—‹β—β—βœ“'
" - [βœ—] unchecked
" - [β—‹] partial
"   - [βœ“] checked
"   - [βœ—] unchecked
"   - [βœ—] unchecked
"   - [βœ—] unchecked

Define whether toggling partially complete checkboxes sets the checkbox to checked or unchecked:

" Example 1:
let g:bullets_checkbox_partials_toggle = 1 " default = 1
" - [o] partially checked  [ type <leader>x ]
"   - [x] sub bullet
"   - [ ] sub bullet
"
" Result:
" - [x] checked
"   - [x] sub bullet
"   - [x] sub bullet
"
" Example 2:
let g:bullets_checkbox_partials_toggle = 0
" - [o] partially checked  [ type <leader>x ]
"   - [x] sub bullet
"   - [ ] sub bullet
"
" Result:
" - [ ] checked
"   - [ ] sub bullet
"   - [ ] sub bullet

Mappings

  • Insert new bullet in INSERT mode: <cr> (Return key)
  • Same as in case you want to unmap in INSERT mode (compatibility depends on your terminal emulator): <C-cr>
  • Insert new bullet in NORMAL mode: o
  • Renumber current visual selection: gN
  • Renumber entire bullet list containing the cursor in NORMAL mode: gN
  • Toggle a checkbox in NORMAL mode: <leader>x
  • Demote a bullet (indent it, decrease bullet level, and make it a child of the previous bullet):
    • NORMAL mode: >>
    • INSERT mode: <C-t>
    • VISUAL mode: >
  • Promote a bullet (unindent it and increase the bullet level):
    • NORMAL mode: <<
    • INSERT mode: <C-d>
    • VISUAL mode: >

Disable default mappings:

let g:bullets_set_mappings = 0

Add a leader key before default mappings:

let g:bullets_mapping_leader = '<M-b>'
" Set <M-b> to the leader before all default mappings:
" Example: renumbering becomes `<M-b>gN` instead of just `gN`

Just add above to your .vimrc

Documentation

:h bullets

Testing

The test suite is written using vimrunner. It is known to run on macOS with MacVim installed, and on travis. Your vim must have +clientserver and either have its own GUI or in a virtual X11 window.

On your mac run:

bundle install
bundle exec rspec

On linux:

bundle install
xvfb-run bundle exec rspec

You should see a Vim window open which will run each test, same general idea as Capybara integration testing. ❀️

TODO

  • eliminate trailing bullet on previous line if user pressed twice
  • allow indenting while in insert mode (C-l: indent right, C-h: indent left)
  • scope the keybindings and functions to markdown and perhaps text
  • allow GFM-style checkbox auto bullet
  • prefix shortcuts and allow disabling them
  • add numbered list
  • reset numbers (user selects numbered bullets 3-5 and copies to middle of document, then reselects and resets them to 1-3)
  • check if plugin initialized and don't load if it did
  • allow for return without creating a bullet (only possible in GuiVim unfortunately)
  • check if user is at EOL before appending auto-bullet - they may just want to
  • attempt to keep the same total bullet width even as number width varies (right padding)
  • detect lists that have multiline bullets (should have no empty lines between lines).
  • add alphabetic list
  • support for intelligent alphanumeric indented bullets e.g. 1. \t a. \t 1.
  • change nested outline levels in visual mode
  • support renumbering of alphabetical, roman numerals, and nested lists
  • update documentation for nested bullets
  • support nested bullets with child and partial completion
  • support for nested numerical bullets, e.g., 1. -> 1.1 -> 1.1.1, 1.1.2
  • add option to turn non-bullet lines into new bullets with <C-t>/>>/>

About

Hashrocket logo

Bullets.vim is kindly supported by Hashrocket, a multidisciplinary design and development consultancy. If you'd like to work with us or join our team, don't hesitate to get in touch.

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Dorian Karter

πŸ’» ⚠️ πŸ“– 🚧

Cormac Relf

πŸ’» πŸ›

Keith Miyake

πŸ’» πŸ“– πŸ€” 🚧

Chayoung You

πŸ’» πŸ“–

Adriaan Zonnenberg

πŸ’»

eater

πŸ’»

hut

πŸ’» πŸ“–

mykoza

πŸ’» πŸ€”

noodlor

πŸ’»

Harshad Srinivasan

πŸ’» πŸ›

Erick A. ChacΓ³n MontalvΓ‘n

πŸ€”

Sam Griesemer

πŸ’» πŸ›

Charles Pence

πŸ’»

Marko Stojanovic

πŸ“–

Clark

πŸ“–

Wenzel

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!