djv
djv

Reputation: 466

Vim function toggle replace character under cursor

I'm trying to change "X" to " " and vice versa to mark a checkbox in a markdown file in normal mode:

- [X] Zucchini
- [ ] Nutmeg

Here's what I've tried:

First

function! ToggleComplete()
  if getline('.')[col('.')-1] == 'X'
    return ' '
  else
    return 'X'
  endif
endfunction

nnoremap <C-x> :call ToggleComplete()<CR>

Second

function! ToggleComplete()
  if getline('.')[col('.')-1] == 'X'
    return '\r\<Space>'
  else
    return '\rX'
  endif
endfunction

nnoremap <C-x> :call ToggleComplete()<CR>

Upvotes: 1

Views: 400

Answers (1)

yolenoyer
yolenoyer

Reputation: 9445

It really can't work like this; the main reason is how you use the return statement : your function returns a space or an X char, but the returned value is never used, and is lost when you use call ToggleComplete(). Actually there's nothing in your code that changes the content of your buffer.

A secondary point: your if test is very restrictive; it requires your cursor to be exactly on the right char in the line, in order to work (because of [col('.')-1]). Maybe it's what you want, but you may also add some flexibility by using a test which works without depending on the cursor column.

The following is one possibility of doing what you want:

function! ToggleComplete()
    " Get current line:
    let l:line = getline('.')

    " Get the char to test with the help of a pattern, ' ' or 'X':
    " \zs and \ze lets you retrieve only the part between themselves:
    let l:char = matchstr(l:line, '\[\zs.\ze]')

    " Invert the value:
    if l:char == 'X'
        let l:char = ' '
    else
        let l:char = 'X'
    endif

    " Replace the current line with a new one, with the right
    " char substituted:
    call setline(line('.'), substitute(l:line, '\[\zs.\ze]', l:char, ''))

    " Please note that this last line is doing the desired job. There is
    " no need to return anything
endfunction

Upvotes: 2

Related Questions