This is done by adding a function to the completefunc
option that returns a list of your registers.
set completefunc=Registers
To make the function you have to return the register contents in a format that completefunc
likes:
function! Registers(findstart, base)
if a:findstart == 1
return 0
endif " Imagine, if you will, this is a full list
" of all your registers...
let l:regs = [ '"', '0', '1', '2', '3', '4', '5', '6', ...] return {
\'words': map(l:regs,
\{ i, reg -> {
\'key': reg,
\'word': getreginfo(reg).regcontents[0],
\'abbr': '@' . reg,
\'menu': slice(trim(getreginfo(reg).regcontents[0]), 0, 20)
\}
\})
\}
endfunction
Add this to your vimrc and you’ll be able to insert the contents of your registers using CTRL-X CTRL-U in insert mode.
How it’s done
X Mode
Like most Vim devs I use a plugin to handle most of my completion needs, it just opens up automatically when I start typing and I use CTRL-N and CTRL-P to cycle through the options.
But did you know that Vim has a special X mode that can choose between all sorts of completion sources?
Try going into insert mode and pressing CTRL-X, you’ll see this message printed at the bottom of your view:
These are all the different X mode options.
You might already know CTRL-X CTRL-F which complete’s directory paths or CTRL-X CTRL-L which does whole line completion — it can choose from any whole line you’ve already written in an open buffer…
For our registers list I’m using CTRL-X CTRL-U which is the user defined completion menu. It’ll complete from a function you add to the completefunc
option.
Function Format
You can learn all about the completefunc
option format in :h complete-functions
but it’s not a wild read so let me sum it up.
The function you add to completefunc
has to accept two arguments findstart
and base
.
The function you add to completefunc
will run twice. The first time it runs it’ll expect the index for where to start the menu. The second time it’ll expect the contents of the menu.
You can use findstart
to tell you whether this is the first or second time the function is running. If its value is 1
it’s the first time and madly if it’s 0
it’s the second time, I know… #vimscriptingsmental.
So first of we write an if statement to check findstart
and we return 0
to tell the menu to start at the first option:
function Registers(findstart, base) " You have to prepend arg names with "a:"
if a:findstart == 1
return 0
endifendfunction
You won’t need to base
but you do have to add it because Vim doesn’t like you ignoring function args.
Completion Menu Format
Next we have to return our list of options in a special format. It has to be a dictionary (aka an object) with an array of dictionaries called words
, which have three useful string parameters, word
, abbr
and menu
:
{ 'words': [{ 'word': '', 'abbr': '', 'menu': '' }, ...] }
word
— the text you want the option to be.abbr
— the label for the option in the dropdown.menu
— a pretty version of the text to display in the dropdown.
So next if the findstart
arg is not 1
we’ll return our dictionary:
function Registers(findstart, base)
if a:findstart == 1
return 0
endif return { 'words': [
\{
\'word': 'yanked stuff full version',
\'abbr': '@"',
\'menu': 'yanked stuff'
\},
\]}endfunction
Getting reg contents
Vim provides in-built functions to get the contents of most common lists, for example getbufinfo()
for :ls
gives you data about all your buffers, getchangelist()
for :changes
, getjumplist()
for :jumps
and so on — you can browse through Vim’s functions with :h functions
.
Annoyingly though I couldn’t find one for registers.
But it does have a function for showing you a register one at a time called getreginfo
which accepts a register name and returns an object with a list called regcontents
.
You can try it out by echoing it in command mode:
:echo getreginfo('"')
This’ll print an object with some info about the "
register…
{'regcontents': ['yanked stuff'], 'regtype': 'v', 'points_to': '-'}
Don’t ask me why regcontents
is a list.
Let’s get the actual contents of the "
reg and put it into Registers
:
function Registers(findstart, base)
if a:findstart == 1
return 0
endifreturn { 'words': [
\{
\'word': getreginfo('"').regcontents[0], " This adds @" to the start of the option so we can
" clearly see which reg it is...
\'abbr': '@"', " I'm using slice() and trim() to remove whitespace
" and shorten the text for the menu
\'menu': slice(trim(getreginfo('"').regcontents[0]), 0, 20)
\},
\]}endfunction
Mapping Registers
Last of all we have to do some mapping trickery to convert a full list of registers into the right format.
First make a list of register names:
" The "l:" make regs a "local" variable...
let l:regs = ['"', '1', '2', '3', '4', ...]
Then use Vim’s best function, map
, to convert them…
map(l:regs, { index, reg -> {} })
Looks weird right? map
can accept a lambda function (a bit like a javascript fat-arrow =>
function) that receives an index
and then the value in the list. It returns whatever you tell it to for each item in regs
.
Now let’s add the formatting code:
map(l:regs, { i, reg -> { \'key': reg,
\'word': getreginfo(reg).regcontents[0],
\'abbr': '@' . reg,
\'menu': slice(trim(getreginfo(reg).regcontents[0]), 0, 20)\} })
Finally add that to Registers
…
function! Registers(findstart, base)
if a:findstart == 1
return 0
endif let l:regs = [ '"', '0', '1', '2', '3', '4', '5', '6', ...] return {
\'words': map(l:regs,
\{ i, reg -> {
\'key': reg,
\'word': getreginfo(reg).regcontents[0],
\'abbr': '@' . reg,
\'menu': slice(trim(getreginfo(reg).regcontents[0]), 0, 20)
\}
\})
\}
endfunction
Press CTRL-X CTRL-U and you’ll see a list of your registers with their names prepended.