Comment j'ai configure mon neovim sans lazy.nvim
Il y a quelque temps je suis tombé sur un post reddit qui présentait une configuration pour neovim sans aucun plugin, uniquement avec les fonctionnalités de base de neovim. J’ai essayé de repartir de cette idée. C’était intéressant, mais un peu trop compliqué pour moi à utiliser. Le confort des plugins me manquait trop.
Quelques mois plus tard, j’ai repris ma configuration avec un objectif plus simple: l’alléger maintenant que neovim gère plus de choses nativement, surtout côté lsp.
Je reste tout de même avec 17 plugins installés, donc je suis encore assez loin d’une configuration OOTB à la helix. Mais ça reste bien plus léger que ce que j’avais avant.
Plugins
Pour la gestion des plugins, ma configuration n’utilise pas lazy.nvim, mais directement celui de neovim.
Je voulais aussi profiter de fonctionnalités de neovim qu’on voit un peu moins mises en avant, comme la lecture automatique des fichiers placés dans plugin (~/.config/nvim/plugin/*.lua).
Le dossier plugin
Tous les fichiers placés dans ce dossier sont lus automatiquement au démarrage.
Avec un ./plugin/foo.lua contenant :
vim.print("foobarbaz")
En lançant nvim, on aura directement foobarbaz affiché dans vim.
Avec cette feature, on peut se passer d’un init.lua qui require tous les fichiers de configuration nécessaires.
Les fichiers contenus dans plugin/ sont lus dans l’ordre alphabétique. Dans mon cas, je veux que le fichier qui gère l’installation des plugins soit lu en premier, pour que le reste, notamment ce qui touche au lsp, arrive avec les plugins déjà chargés.
Pour ça je crée un fichier +plugin.lua. Avec ce +, on s’assure qu’il sera lu avant les autres.
-- ./plugin/+plugin.lua
vim.pack et le lazy loading
Comme dit plus haut, j’utilise vim.pack, mais je voulais garder du lazy loading car j’ouvre regulierement neovim depuis opencode pour editer mes messages et si a chaque fois je le lance je dois attendre quelques centaines de ms ca va render l’experience tres penible.
Pour ça, il existe justement des plugins faits pour ca :
Pour ma part j’utilise lze. Je ne sais pas du tout si lz.n est mieux.
Son api est assez similaire a celle de lazy:
-- ./plugin/+plugin.lua
require('lze').load({
-- ici le plugin nvim-tree sera chargé uniquement après avoir exécuté l'un des deux raccourcis via la fonction after
{
"nvim-tree.lua" --
keys = {
{ "<leader>bo", ":NvimTreeFindFileToggle<cr>", silent = true },
{ "<leader>bf", ":NvimTreeFindFile<cr>", silent = true },
},
after = function()
require("nvim-tree").setup({ view = { side = "right" } })
end,
},
-- même principe, le plugin sera chargé dès que l'événement vim est exécuté
{
"nvim-autopairs",
event = "InsertEnter",
after = function()
require("nvim-autopairs").setup()
end,
},
-- on charge ce plugin quand le colorscheme en question est chargé
{
"tokyonight.nvim",
colorscheme = "tokyonight-night",
after = function()
require("tokyonight").setup()
end,
},
-- pour certains plugins on souhaite les charger directement sans lazy load, il suffit de rajouter le `lazy = false`
{
"mason.nvim",
lazy = false,
after = function()
require("mason").setup()
end,
},
})
Dans ma config, j’utilise surtout les attributs keys et event, avec un colorscheme dans un cas (tokyonight). lze propose plus d’options de lazy loading, tout est expliqué dans le README.
Au démarrage lze est uniquement chargé, et j’installe les autres plugins sans les charger tout de suite:
-- ./plugin/+plugin.lua
vim.pack.add({
"https://github.com/BirdeeHub/lze",
}, { load = true }) -- load = true doit être spécifié si on charge les plugins depuis /plugin
vim.pack.add({
"https://github.com/nvim-tree/nvim-tree.lua",
"https://github.com/folke/tokyonight.nvim",
"https://github.com/mason-org/mason.nvim",
}, { load = function() end, confirm = true })
require('lze').load({
... -- comme plus haut
})
Pour les plugins, c’est tout. Je lance de temps en temps :lua vim.pack.update(), et pour le moment je n’ai eu aucun problème avec cette gestion des plugins.
LSP
Pour le lsp, j’utilise:
- mason.nvim pour installer les language servers
- conform pour gérer le formatage des fichiers
Je n’utilise pas nvim-lspconfig directement. Par contre, je récupère souvent les configurations des language servers depuis ce repo.
Les plugins
Pour la partie configuration en lua:
-- ./plugin/+plugins.lua
require("lze").load({
{
"mason.nvim",
lazy = false,
after = function()
require("mason").setup()
end,
},
{
"conform.nvim",
lazy = false,
after = function()
local get_js_formatter = function(bufnr)
if require("conform").get_formatter_info("biome", bufnr).available then
return { "biome", lsp_format = "never", stop_after_first = true }
end
return {}
end
require("conform").setup({
notify_on_error = false,
format_on_save = function(bufnr)
local disable_filetypes = { c = true, cpp = true }
return {
timeout_ms = 2000,
lsp_fallback = not disable_filetypes[vim.bo[bufnr].filetype],
}
end,
formatters_by_ft = {
lua = { "stylua" },
typescript = get_js_formatter,
typescriptreact = get_js_formatter,
javascript = get_js_formatter,
javascriptreact = get_js_formatter,
c = { "clang_format" },
},
clang_format = {
prepend_args = { "--style=file", "--fallback-style=LLVM" },
},
})
end,
},
Activer les language servers
Pour activer les language servers, on peut directement utiliser vim.lsp.enable:
-- ./plugin/lsp.lua
vim.lsp.enable({
"astro",
"lua_ls",
...
})
Neovim va alors chercher dans ~/.config/nvim/lsp les configurations des language servers à partir des noms donnés dans vim.lsp.enable.
Dans mon exemple, les fichiers astro.lua et lua_ls.lua sont dans ce dossier.
On peut donc les nommer comme on veut, astro_legacy_123.lua par exemple s’il existait un vieux lsp pour astro, puis l’activer avec vim.lsp.enable({ "astro_legacy_123" }).
Pour le contenu des fichiers, je prends souvent exemple sur ceux du repo de nvim-lspconfig. Par exemple celui d’astro.
Je pense que c’est la partie la moins pertinente de ma config. Utiliser nvim-lspconfig serait probablement plus simple, parce que toutes les configurations seraient déjà disponibles. Là, quand j’essaie un nouveau langage ou une nouvelle techno avec un lsp, je vais sur le repo de lspconfig, je cherche le fichier correspondant dans lsp/, puis je le copie chez moi. Comme ça m’arrive très rarement, ça me va.
LspAttach
Enfin, pour configurer mes keymaps ou d’autres réglages liés au lsp, j’utilise une autocommand sur l’event LspAttach:
-- ./plugin/autocmd.lua
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(event)
vim.keymap.set("n", "gd", vim.lsp.buf.definition)
local client = vim.lsp.get_client_by_id(event.data.client_id)
if client and client:supports_method("textDocument/documentHighlight", event.buf) then
local highlight_augroup = vim.api.nvim_create_augroup("HighlightCursorHold", { clear = false })
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
group = highlight_augroup,
buffer = event.buf,
callback = vim.lsp.buf.document_highlight,
})
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
group = highlight_augroup,
buffer = event.buf,
callback = vim.lsp.buf.clear_references,
})
vim.api.nvim_create_autocmd("LspDetach", {
group = vim.api.nvim_create_augroup("LspDetach", { clear = true }),
callback = function(event2)
vim.lsp.buf.clear_references()
vim.api.nvim_clear_autocmds({ group = "HighlightCursorHold", buffer = event2.buf })
end,
})
end
end,
group = vim.api.nvim_create_augroup("LspAttach", { clear = true }),
})