Global plugin
An interesting thing about Vim and Neovim is that a plugin is just a script that gets executed automatically.
There is no special API for plugins. At the end of the day a plugin is a script that can be written in vimscript or lua. And that's it.
The runtimepath
The runtimepath
is a vim option, it contains a list of directory paths. Those are the places where Neovim looks for "runtime files."
What's a runtime file?
It can be anything really. Documentation files, color schemes, syntax files, plugin scripts, filetype plugins. There's a lot of things. You can see the full list in the documentation.
:help 'runtimepath'
Here is the interesting thing: our personal configuration directory is usually the first directory in the runtimepath
. It means we can have any kind of runtime file as part of our personal configuration. Which leads us to...
The plugin directory
We can create a global plugin simply by adding a script in a directory called plugin
. And, as mentioned before, this can be written in vimscript or lua.
Let's say for example we have a Neovim configuration with this structure:
nvim
├── init.vim
└── plugin
├── diagnostics.lua
└── snippets.vim
In this case plugin/diagnostics.lua
and plugin/snippets.vim
are global plugins. They will be executed automatically during the startup process, after the init.vim
file.
Is worth mention Neovim will first execute all the plugin script written in vimscript and then it will execute the ones written in lua.
The content of the plugins is not very relevant but I can show you some code.
This is the example code for snippets.vim
.
" Learn about vim abbreviations feature here:
" https://vonheikemen.github.io/devlog/tools/using-vim-abbreviations/
" Ctrl+d will expand abbreviations
inoremap <C-d> @<C-]>
function! s:lang_lua() abort
iabbrev <buffer> ff@ function()<CR>end<Esc>O
iabbrev <buffer> fun@ function Z()<CR>end<Esc>O<Up>fZa<BS>
iabbrev <buffer> if@ if Z then<CR>end<Esc>O<Esc>0<Up>fZa<BS>
iabbrev <buffer> elif@ elseif Z then<Esc>FZa<BS>
iabbrev <buffer> foreach@ for i, x in pairs(Z) do<CR>end<Esc>O<Esc><Up>fZa<BS>
endfunction
autocmd FileType lua call s:lang_lua()
And here is the content of diagnostics.lua
.
local levels = vim.diagnostic.severity
local opts = {
virtual_text = true,
float = {
border = 'rounded',
},
signs = {
text = {
[levels.ERROR] = '✘',
[levels.WARN] = '▲',
[levels.HINT] = '⚑',
[levels.INFO] = '»',
},
},
}
local sign_define = function(name, text)
local hl = 'DiagnosticSign' .. name
vim.fn.sign_define(hl, {
texthl = hl,
text = text,
numhl = ''
})
end
if vim.fn.has('nvim-0.10') == 0 then
sign_define('Error', opts.signs.text[levels.ERROR])
sign_define('Warn', opts.signs.text[levels.WARN])
sign_define('Hint', opts.signs.text[levels.HINT])
sign_define('Info', opts.signs.text[levels.INFO])
vim.keymap.set('n', '<C-w>d', '<cmd>lua vim.diagnostic.open_float()<cr>')
vim.keymap.set('n', '<C-w><C-d>', '<cmd>lua vim.diagnostic.open_float()<cr>')
vim.keymap.set('n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<cr>')
vim.keymap.set('n', ']d', '<cmd>lua vim.diagnostic.goto_next()<cr>')
end
vim.diagnostic.config(opts)
Plugin vs package
You might have notice this definition of plugin doesn't really align with the expectation most people have. When we think of a plugin the thing that comes to mind is something like vim-fugitive. This project isn't just one script, it has a ton of things. It does have a plugin
directory, but there's also doc
, syntax
, ftplugin
. Well, according to the documentation, vim-fugitive would be considered a Vim package.
If I had to summarize the difference between a global plugin and a package:
Global plugin -> one script
Vim package -> many related runtime files
Is this information important? Probably not. The term "Vim package" is not really popular in the community. I use these terms because it what you'll find in the official documentation.