I have those setting in .vimrc

setlocal textwidth=0 wrap formatoptions=tc2n linebreak
noremap <buffer> j gj
noremap <buffer> k gk
noremap <buffer> $ g$
noremap <buffer> ^ g^
noremap <buffer> 0 g0

nnoremap <buffer> dd g0vg$D
nnoremap <buffer> D g0vg$D

nnoremap I g^i
nnoremap A g$BEa

nnoremap V g0vg$

But I found g$ stop at the last character including space in vim, where in vscode-neovim, using the same .vimrc, g$ will stop at the last non-blank character. How to get the same g$ behavior in vim? Or, how to jump to last non-black character in vim? so that I don't have to map A as g$BEa, since it sometimes don't jump to the location I want, if the last character is ".

g$ in vim enter image description here

g$ in vsocde with vscode-neovim plugin(I have proved there is a space after the character) enter image description here


3 Answers 3


Since Vim patch v9.0.1753 you can now use g<end> to jump to the last non-blank char on a line.


I have written a dirty solution that work as describe. But if you can provide built-in functionality that work exactly is appreciated.

function! IsBlank()
    normal! g$
    let char = getline(".")[col(".") - 1]
    if char == "\t" || char == " "
        normal! gel
  • Note that getline(".")[col(".") - 1] won't work correct as it will use byte offsets rather than character offsets. So if you have an UTF-8 multibyte character it won't work as expected. See this question. Just FYI. Jun 23, 2023 at 17:39

As far as I know, there isn't anything built in to Vim for this, but you can remap g$ (and/or $) to backup a bit if it lands on a space:

fun! s:gdollar()
    normal! g$
    while strcharpart(strpart(getline('.'), col('.') - 1), 0, 1) == ' '
        if col('.') == 1
        normal! h
nnoremap <silent> g$ :call <SID>gdollar()<CR>

This will actually skip all spaces; change the while and endwhile to if and endif to only move back once (you can also remove the if col('.') == 1 then). You may also want to check for tab ("\t") perhaps; I'm not entirely sure how the VSCode plugin behaves.

Please note that nnoremap are "non recursive mappings", which means that they will always use the built-in functionality for keys rather than anything you redefined. Usually this is what you want, but here:

nnoremap g$ :call <SID>gdollar()<CR>
nnoremap $  g$

Means that $ will call the built-in version of g$, rather than your custom version. You will have to either use nmap or call that function in every mapping where you want the new behaviour.

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.