Search code examples
vimvikeymapping

how to map the same key with two different behavior in vim?


Okay here is my problem I want to find a way on how to map a key that have two different behaviors based on the number of hits. so as an example let's suppose that I want to add a ; at the end of the line when I hit the key ; and if I hit it twice it will be removed.

Note: the semicolon is only an example the main idea is how to toggle the key


Solution

  • This'll do, despite it being a bit nutty...

    nnoremap ; :if(getline('.')[col('$')-2]==';')\|exe "norm $x"\|else\|exe "norm A;"\|endif<cr><cr>
    

    It gets the last character of the current line (string indexing is 0-based, col('$') returns the 1-based column just past the end of line so that's why there's a -2 in there), compares that to semicolon, and either removes or appends the last character from the line.

    I don't know why it requires two <CR>s at the end. The exes are because norm captures everything to the end of line so the bar doesn't separate "lines" otherwise.

    Edit for changing requirements: :-)

    If you want to ensure that one and only one semicolon appears at the end of a line and also handle trailing spaces (including spaces between semicolons), it's actually a lot easier than toggling the last semicolon if you don't need to keep the trailing spaces. This will also make sure that the one semicolon doesn't have any spaces before it, if you actually want to leave spaces before the semicolon remove the first \s*.

    nnoremap ; :s/\v\s*(;\s*)*$/;/<cr>

    If you still want to toggle but need to handle trailing spaces then we need to figure out what you intend to do with those trailing spaces. My preference is just to delete them so you can go on with the simpler things in life... Hook this up to a key if you want, I actually do it while handling BufWritePre in an autocmd.

    :%s/\s\*$//e