I don't think there is a built-in way to achieve what you want to do, so I decided to try to implement it by myself. I got a working solution but this kind of text manipulation has a ton of corner cases and they are most probably not handled by my solution. Hopefully that will give you some inspiration to code your own (better) solution. Also there might already be an existing plugin to do what you want, but I don't know it.
The idea is to create a CenterFn()
which will do several things:
- Check that the last selection was a visual block (handling other kind of selections is "left as an exercise for the reader"). Caution If you copy-paste the code make sure you insert the
^V
character with ctrl+vctrl+v
- Get the content which was selected
- Use this content to determine the size of the block (i.e. the difference between the first selected column and the furthest last one)
- Get the size of the space to center in. Getting inspiration from
:h :center
we use either the value of &textwidth
or 80 if &textwidth
is 0.
- Given the size of the space and the size of the block, determine how many whitespaces we need to add in front of the block
- Add the padding
This would look like this:
function CenterFn()
" Abort if last visual selection was not in block mode
" Caution: The character between the quote was inserted via <C-v><C-v>
if (visualmode() != "^V")
echom "not visual block"
return
endif
" Get the selected text lines in a list
try
let a_save = @a
normal! gv"ay
let selectedText = getreg('a', 1, v:true)
finally
let @a = a_save
endtry
" Get the first column of the selection and iterate through all lines
" to get the len of the longest line in the selection
let startCol = getpos("'<")[2]
let maxLen = 0
for line in selectedText
let lineLen = len(line)
let maxLen = (maxLen < lineLen ? lineLen : maxLen)
endfor
let endCol = startCol + maxLen
" Get the total number of columns like :center does
let size = (&textwidth != 0 ? &textwidth : 80)
" Get the size of the padding
let padSize = size/2 - (endCol - startCol)/2
" For all the selected lines add the white space padding
execute("'<,'>norm! " . padSize . "I ")
endfunction
And to make it more convenient we can create a user-defined command which will ease the call to the function:
command! -range Center :call CenterFn()
This way you can select your text and use :Center
to get what you want. The -range
parameter makes it that you can press :
while you are still in visual mode which will give :'<,'>Center
and the command will still work (otherwise you have to use <C-u>
to get rid of the range '<,'>
in the command prompt.
I tested this solution against your specific test case and it works but it would need some rework, for example in the case where you want to center a block with some text before like this:
XXXPythagoras:
XXX
XXX |\
XXX | \
XXXa² | \ c²
XXX | \
XXX |____\
->
XXX Pythagoras:
XXX
XXX |\
XXX | \
XXX a² | \ c²
XXX | \
XXX |____\