Comment j'ai configuré neovim pour écrire du LaTeX - Partie 1 : le LSP
Il arrive un moment dans la vie d'un⋅e thésard⋅e où on doit écrire son manuscrit. Personnellement, c'est maintenant. Évidemment il y a pléthore d'outils pour faciliter le processus (je pense notamment aux outils de gestion bibliographique), mais certains sont moins connus que d'autres.
Si vous faites (ou avez déjà fait) une bibliographie, il y a de bonnes chances pour que vous ayez déjà utilisé un logiciel comme Zotero (utilisez autre chose par pitié, Elsevier c'est le mal) ou Jabref. Mais avez-vous réfléchi à vos outils d'écriture ? Pour écrire du LaTeX, utilisez-vous un logiciel clef-en-main comme TeXstudio ?
Pas n'importe quel éditeur de code
Un éditeur de texte est un outil qui, comme son nom l'indique, sert à modifier
un fichier texte.
Il y en a des brouettes, les plus connus étant
Notepad,
Emacs et vim.
Personnellement, j'utilise neovim, une version modifiée de
vim
qui ajoute des fonctionnalités, comme la possibilité d'être configuré en
Lua
ou d'écrire des plugins dans n'importe quel langage informatique.
Ce qui suit sera donc étroitement lié à neovim
, mais devrait en théorie être
adaptable à n'importe quel éditeur de texte pouvant gérer le
Language Server Protocol.
Language Server Protocol ?
Un LSP (Language Server Protocol (server), ou Protocole de serveur de langage) est une spécification permettant à un logiciel de "discuter avec un langage". Dit simplement, un serveur implémentant le LSP pour un langage donné va être capable d'analyser le code (dans le langage en question) qu'on lui fournit, et de retourner une liste d'erreur, d'informations syntaxiques, voire de correctifs concernant votre code. Cela permet aux éditeurs de code d'afficher les erreurs dans votre code, sans que vous ayez besoin de manuellement le compiler, et dans votre interface de développement. C'est un peu magique, c'est très utile, et ce n'est pas très difficile à mettre en place.
La mise en place
Liste des ingrédients
Un peu comme une recette de cuisine, il y a plusieurs choses dont on va avoir
besoin.
Je vais me concentrer sur LaTeX
et neovim
, mais comme je l'ai déjà dit c'est
adaptable à tout éditeur de texte supportant les LSPs et tout langage disposant
d'un serveur implémentant le protocole---c'est l'avantage des spécifications,
tout le monde peut faire sa propre implémentation.
On a donc besoin de :
- neovim v0.10.0 ou supérieure ;
- nvim-lspconfig, le plugin officiel proposant des configurations de base pour lap lupart des serveurs LSP ;
- texlab, le serveur LSP le plus connu
pour
LaTeX
.
Bien qu'on n'ait pas besoin de plus pour que tout fonctionne, on va quand même utiliser d'autres plugins pour nous rendre la vie plus facile. Notamment :
- lazy.nvim, un gestionnaire de plugins ;
- mason.nvim, un gestionnaire de serveurs LSP ;
Je ne pense pas parler de complétion automatique aujourd'hui, ce sera peut-être l'occasion d'un deuxième article sur le sujet.
Tout récupérer
Si vous utilisez neovim
vous ne serez probablement pas trop étonné⋅e par la
suite (même si, dans le cas où vous utilisez un autre gestionnaire de plugin, ce
ne sera peut-être pas la même syntaxe).
neovim
se paramètre via des fichiers de configuration placés dans
$HOME/.config/nvim
par défaut.
Par la suite, je vais utiliser la variable $NVIM
pour référencer ce dossier.
On commence donc par créer (ou modifier) $NVIM/init.lua
, et on y écrit :
require("plugin")
require("lazy").setup("plugin")
Vous pouvez changer les deux occurences de plugin
par le terme de votre choix,
tant que vous utilisez le même à chaque fois.
On crée un dossier $NVIM/lua/plugin
qui va accueillir la configuration de nos
plugins :
$ > cd ~/.config/nvim/
$ > mkdir -p lua/plugin
Dans $NVIM/lua/plugin
, on crée un fichier init.lua
, qui contient
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", -- latest stable release
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
return {}
lazy.nvim
est un grand plugin, il est propre et se gère tout seul.
Ce bout de code est simplement là pour que neovim
vérifie qu'il soit là, le
télécharge au besoin, puis passe le contenu de la variable lazypath
à lazy
pour qu'il voit les plugins ainsi téléchargés.
Maintenant, on spécifie le serveur qu'on veut récupérer par son nom (pas celui
du langage).
Ça se passe dans $NVIM/lua/plugin/lsp/init.lua
:
_G.lsp_root_dir = vim.fn.stdpath("data") .. "/mason/bin"
local servers = {
"texlab",
}
Enfin, il reste à récupérer nvim-lspconfig
et mason
pour télécharger
texlab
et le configurer.
Toujours dans $NVIM/lua/plugin/lsp/init.lua
:
return {
{
"neovim/nvim-lspconfig",
dependencies = {
"saghen/blink.cmp",
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
vim.diagnostic.config(
{
virtual_text = false,
severity_sort = { true, reverse = true },
}
)
local lspconfig = require("lspconfig")
local capabilities = vim.lsp.protocol.make_client_capabilities()
for _, server in ipairs(servers) do
local opts = {
capabilities = capabilities,
}
local plugin = string.format("plugin.lsp.%s", server)
require(plugin).setup(opts)
lspconfig[server].setup(opts)
end
end
},
{
"williamboman/mason.nvim",
cmd = "Mason",
opts = { ensure_installed = servers },
},
}
C'est tout pour le moment !
Ce bout de code est assez simple à comprendre : mason.nvim
s'assure que tous
les serveurs de la liste servers
soient installés lorsqu'il est configuré, et
au besoin les télécharge.
Ensuite, on appelle nvim-lspconfig
qui regroupe une configuraton de base pour
la plupart des serveurs LSP
, on désactive le texte virtuel (préférence
personnelle, je trouve que ça pollue mon interface -- mais je peux afficher les
messages avec un raccourci clavier, j'y reviendrai si je fais un article sur la
complétion) et on affiche en priorité les choses les moins sévères (pour savoir
que le LSP peut nous aider au lieu de simplement savoir qu'il y a une erreur).
On récupère ensuite les infos sur neovim
et ce qu'il pourrait obtenir du
serveur via make_client_capabilities
, puis on configure le serveur en lui
passant ces capacités au moment d'appeler texlab.setup()
.
On peut passer à la dernière petite étape, qui est de modifier un tout petit
peu la configuration de base pour texlab
.
Dans $NVIM/lua/plugin/lsp/texlab.lua
:
local M = {}
M.setup = function(opts)
opts.settings = {
build = { executable = "pdflatex" },
texlab = {
diagnostics = { ignoredPatterns = { "Unused label" } }
},
}
end
return M
On spécifie qu'on utilise pdflatex
(et pas mklatex
, valeur par défaut) et
qu'on veut ignorer les warnings concernant un \label
non-utilisé (je mets
très souvent un label sur mes environnement LaTeX, il y en a donc beaucoup que
je me retrouve à ne pas utiliser), et voilà.
It's done, Sam! It's done!
Il reste à configurer les plugins de complétion. On verra ça dans un prochain billet. :)