The gJ mapping does this; from :help gJ:
Join [count] lines, with a minimum of two lines. Don't insert or remove any spaces.
You could rebind it to J, if you want to save a keystroke:
:nnoremap J gJ
Note that this doesn't remove any spaces, so if either the current line ends with a space or next line starts with one or more spaces they will be left as is; so this:
Hello
world
Becomes:
Hello world
We could use Jx in this case, then it will be Helloworld, but that won't work in all cases; from the help:
Join the highlighted lines, with a minimum of two lines. Remove the indent and insert up to two
spaces
[...]
These commands, except "gJ", insert one space in place of the unless
there is trailing white space or the next line starts with a ')'.
So in some cases more than one space or no space is inserted. As far as I can
see, there is no easy way to change this behaviour; I created a function to
modify gJ to always join without spaces:
" Like gJ, but always remove spaces
fun! s:join_spaceless()
execute 'normal! gJ'
" Remove character under the cursor if it's whitespace.
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~ '\s'
execute 'normal! dw'
endif
endfun
" Map it to a key
nnoremap <Leader>J :call <SID>join_spaceless()<CR>
If you want, you can add let save = winsaveview() at the start of the function, and call winrestview(save) at the end to prevent the cursor from moving (but the current behaviour is the same as gJ).
See also: :help J, :help 'joinspaces'
let save = winsavesave()at the start, andwinrestview(save)at the end. – Martin Tournoij Apr 11 '20 at 11:33normal! "_dwwould prevent the deleted whitespace from stomping your @" register. Execute doesn't seem necessary for the two normal commands. Thanks for this answer! – idbrii Feb 15 '22 at 18:22