1

Goal

I would like to create a command that can automatically put an x inside a [].
This command should work in both normal mode and visual mode.

normal mode

If it is in normal mode, and the cursor is on the shown line, it should work as follows:

[] my task

Executing the command should give:

[x] my task  
visual mode

If it is in visual mode, and I have selected all lines shown below:

[] my task
   [] step 1
   [] step 2

Executing the command should give:

[x] my task
   [x] step 1
   [x] step 2

What I tried so far

normal mode

I managed to create a command that works in normal mode:

function Fill()
  " Fill first checkbox on this line
  snomagic/[]/[x]/e
  nohlsearch
endfunction  
command Fill :call Fill()
visual mode

I managed to create a command that does what I want in visual mode:

function Gfill()
  " Fill all selected checkboxes
  *snomagic/[]/[x]/e
  nohlsearch
endfunction  
" -range allows usage of visual mode
command -range Gfill :call Gfill()
Can you merge these commands?

Unfortunately, I have a hard time merging these commands into one.
I would love to use just :Fill (which I can abbreviate to :F) that handles both cases.
Any help would be thoroughly appreciated because I use :F on a daily basis to check off the to-do's in my calendar (which I exclusively edit with vim).

2

You are almost there. I would do it the following:

  1. Define the command as taking an explicit range:
    command -range Fill :<line1>,<line2>call Fill()

This will call the function with the range that you supplied to the command (and in case no range was given, it will just call the function with the current line).

  1. Make the function aware of ranges. Therefore we will be using the range attribute to the function definition. In the function, we will then use the special parameters a:firstline and a:lastline to refer to the given range:
function Fill() range
  " Fill all selected checkboxes
  execute a:firstline . ',' . a:lastline . 'snomagic/[]/[x]/e'
endfunction  

Since we are using a normal ex-command, that does not understand Vim variables, we have to glue the parameters to the ex-commands as string using the execute command to eval the parameters during the runtime.

That should then work.

Homework:

  • Have it toggle :)
2
  • Awesome! I wish I could upvote this @Christian! Unfortunately, I lack the reputation right now. I had to dive into the docs to get a full understanding and active learning is the best way to understand something new. For those wanting to understand how this works, I consulted :help func-range for info on a:lastline and a:firstline, :help <line1> for info on <line1> and <line2>, and :help execute for the exe[cute] command and . operator.
    – melvio
    Aug 12 at 16:29
  • I think it is cool that in normal mode a:firstline . ',' . a:lastline . 'snomagic/[]/[x]/e' resolves to ,snomagic/[]/[x]/e'. The , range is interpreted as the current line and that is why this function works in normal mode too.
    – melvio
    Aug 12 at 16:35
2

Alternately, keep your original Fill definition, and always type (or enter from a mapping)

:call Fill()

When in normal mode, this works fine (as you’ve observed).

From visual mode, with a mapping or typed out the range is preserved, so you actually ru

:'<,'>call Fill()

And a function that is not declared range but given one by :call will simply be run once for every line in the range, at that line.

I use this trick in some mappings for makefiles.

The mappings look like

nnoremap yourkeys :call Fill()<cr>
xnoremap yourkeys :call Fill()<cr>
4
  • Nice approach, Ben. I see that you are an expert in vim-mappings, would you be so kind to include the specific mapping that you are referring to? I think that would really help the more novice vimmers. (Myself included, haha)
    – melvio
    Aug 12 at 17:02
  • 1
    There are some in the links, but I’ll edit in an example for your case
    – D. Ben Knoble
    Aug 12 at 17:09
  • Thanks for the help, Ben. It can be hard to figure out how the <buffer>, <LocalLeader>X, b:undo_ftplugin, and abort factor in if you are making your first vim-mapping. I'm grateful that you took the time to add the minimal solution.
    – melvio
    Aug 12 at 19:09
  • 1
    @melvio Try pressing K on vim stuff you dont understand, or asking (even in Vi and Vim Chat), or just looking at what’s in :help.
    – D. Ben Knoble
    Aug 12 at 21:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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