I want to create a simple notes file and manager it with vim. The system is really easy (plain text rules):

- [ ] new entry
- [x] done entry

Well, in my .vimrc I only have the keybind for add new entries, always on NORMAL MODE:

map tn i- [ ]

Now, I can't create a keybind for to remplace - [] for - [x].

How to make it?

P.S.: I found a guide with a commands with similar behavior (but here add the date, useful for someone):

" check off a todo item and time stamp it
map gd ^rx: <Esc>:r! date +" [\%H:\%M]"<ENTER>kJA<Esc>$
" create a new todo item
map gt 

2 Answers 2


Now, I can't create a keybind for to remplace - [] for - [x]

Try the following:

nnoremap <leader>x :s/^- \[\]/\- \[x\]/<CR>

<leader> is by default \, so if you didn't make any change to it than put your cursor on the line:

- [] hello world

and press \x to make it:

- [x] hello world


Using the following you will be able to toggle x:

fun! ToggleCheckBox()
    if getline('.') =~ '^- \[\]'
        :s/^- \[\]/\- \[x\]/
    elseif getline('.') =~ '^- \[x\]'
        :s/^- \[x\]/- \[\]/

nnoremap <leader>x :call ToggleCheckBox()<CR>


I end up using the following:

fun! ListToggleCheckBox()
    let rx_bullets = '^\(\s*[-*]\+\s*\)'
    let rx_empty_checkbox = '\(\s*\[ \?\]\+\s*\)'
    let rx_marked_checkbox = '\(\s*\[[Xx]\]\+\s*\)'
    let line = getline('.')
    if line =~ rx_bullets && line !~ rx_bullets.rx_empty_checkbox.'\|'.rx_marked_checkbox
        exe ':s/'.rx_bullets.'/\1\[ \] /'
    elseif line =~ rx_bullets.rx_empty_checkbox
        exe ':s/'.rx_bullets.rx_empty_checkbox.'/\1\[x\] /'
    elseif line =~ rx_bullets.rx_marked_checkbox
        exe ':s/'.rx_bullets.rx_marked_checkbox.'/\1\[ \] /'

command! ListToggleCheckBox :call ListToggleCheckBox()

nnoremap <leader>x :ListToggleCheckBox<CR>

It does:

  • work for the list items starting with *, **(*), -
  • if no checkboxes, it will create empty one
  • otherwise it toggles them between [ ] and [x]
  • You dont need the colons prefixing the substitute command in your function
    – D. Ben Knoble
    Commented Oct 18, 2018 at 15:01
  • I know. I have just copied it from my command window (:<C-f>)
    – Maxim Kim
    Commented Oct 19, 2018 at 9:18
  • @MaximKim very helpful have a only one keybind for toggle. Thank you.
    – p_ks
    Commented Oct 23, 2018 at 5:54
  • 1
    Function definition @alec
    – Maxim Kim
    Commented Jun 15, 2020 at 17:26
  • 1
    @alec save cursor position in the beginning, restore in the end. :h getcurpos()
    – Maxim Kim
    Commented Jun 15, 2020 at 18:19

I started by suggesting you use the first part of the example command you included since it does what you want but it's totally broken as far as I can tell...certainly it's not compatible with your format. So let's see what we can do.

How would you accomplish the same thing manually if the cursor was somewhere on the line you want to "check"? This is one way: 02f rx.

  • 0 :: Move cursor to the beginning of the line.
  • 2f :: Go forward to the second occurrence of Space
  • rx :: Change the character under the cursor to 'x'.

These are all done while in "Normal mode". How do we create mappings that are available in Normal mode? With :nmap. Or, more precisely, with this variation of that command: :nnoremap (see paragraph under :h :nnoremap for explanation)...

:nnoremap \x 02f rx

That's it. Or not. It would be nice to do nothing if there's already a 'x' in the column...or if we're not even on an item type of line...

So here's a better command: :s/^- \[\zs /x/

This is a substituion that replaces the character in the "check" column with x...but only if there's a space there. Otherwise it does nothing.

The pattern is a little mysterious so here's a breakdown:

  • ^ :: match the beginning of the line
  • - \[ :: eat up the three characters that start an item
  • \zs :: this says that we don't actually do substitution against characters that have been matched before this point
  • :: match a space

Mapping it, again with :nnoremap, we just need to "hit Enter" (<CR>) at the end...

:nnoremap \x :s/^- \[\zs /x/<CR>

For good measure, here's a mapping to do the reverse ("un-check" an item):

:nnoremap \o :s/^- \[\zsx/ /<CR>

You can, of course, use whatever unassigned keys you want in place of \x and \o.

  • Thank you @B Layer for the clear info. Now I understood some more about the create mappings system of vim.
    – p_ks
    Commented Oct 23, 2018 at 5:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.