Configurer Neovim from scratch avec Lua et Lazy.nvim

Configurer Neovim from scratch avec Lua et Lazy.nvim

·8 min de lecture·Mis à jour le 9 janvier 2026

Pourquoi Lua

Neovim 0.5, sorti en 2021, a fait de Lua un citoyen de première classe. Pour une config montée de zéro aujourd'hui, il n'y a plus de raison de rester sur Vimscript :

  • Performance : Lua passe par LuaJIT et devient du bytecode. Vimscript reste interprété.
  • Modularité : on structure sa config comme du vrai code, avec des modules et une arborescence qui a du sens.
  • Écosystème : presque tous les plugins modernes sont écrits en Lua, et les APIs sont natives.
  • Lisibilité : pour quiconque touche du JS, du Python ou du TypeScript, Lua est intuitif.

La config décrite ici tourne autour de 1800 lignes réparties sur 46 plugins, et démarre en moins de 35 ms.

Structure du projet

~/.config/nvim/
├── init.lua                  # Point d'entrée
├── lua/
│   ├── config/
│   │   ├── options.lua       # Options Vim globales
│   │   ├── keymaps.lua       # Raccourcis clavier
│   │   ├── lazy.lua          # Bootstrap Lazy.nvim
│   │   ├── autocmds.lua      # Autocommandes
│   │   └── icons.lua         # Icônes centralisées
│   └── plugins/
│       ├── ui.lua            # Thème, statusbar, bufferline
│       ├── editor.lua        # Neo-tree, which-key, alpha
│       ├── lsp.lua           # LSP natif + Mason
│       ├── completion.lua    # Autocomplétion
│       ├── treesitter.lua    # Syntax highlighting
│       └── ...

Le init.lua est minimal, son rôle est de charger les modules dans le bon ordre :

-- init.lua
vim.loader.enable() -- Cache bytecode Lua pour un démarrage plus rapide

require("config.options")
require("config.keymaps")
require("config.lazy")
require("config.autocmds")

vim.loader.enable() active le cache bytecode Lua intégré à Neovim depuis 0.9 : les fichiers Lua se compilent une seule fois puis sortent du cache. Environ 30 % gagnés sur le temps de démarrage.

Les options essentielles

Tout centralisé dans options.lua plutôt que dispersé en vim.opt un peu partout :

-- lua/config/options.lua
local opt = vim.opt

-- Désactiver netrw (remplacé par Neo-tree)
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1

-- Leaders
vim.g.mapleader = " "
vim.g.maplocalleader = ","

-- Numéros de ligne
opt.number = true
opt.relativenumber = true

-- Indentation : 2 espaces, pas de tabs
opt.tabstop = 2
opt.shiftwidth = 2
opt.expandtab = true
opt.smartindent = true

-- Recherche intelligente
opt.ignorecase = true
opt.smartcase = true
opt.hlsearch = true

-- Interface
opt.signcolumn = "yes"
opt.cursorline = true
opt.termguicolors = true
opt.showmode = false

-- Splits : ouverture à droite et en bas
opt.splitright = true
opt.splitbelow = true

-- Presse-papier système
opt.clipboard = "unnamedplus"

-- Undo persistant, pas de swap
opt.undofile = true
opt.swapfile = false

-- Caractères invisibles
opt.list = true
opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" }

-- Réactivité
opt.updatetime = 250
opt.timeoutlen = 300

-- Scrolloff pour garder du contexte
opt.scrolloff = 10

Quelques choix qui méritent une explication :

  • Space comme leader : la touche la plus accessible quand les deux mains sont sur le clavier. Couplé à which-key, ça donne un système de commandes ergonomique.
  • relativenumber : essentiel pour faire des 5j, 12k sans compter les lignes.
  • 2 espaces : le standard de facto dans le monde JS/TS/Lua.
  • Pas de swap, undo persistant : les swap files n'ont plus d'intérêt, l'undo persistant entre sessions si.

Bootstrap de Lazy.nvim

Lazy.nvim est le gestionnaire de plugins de référence : lazy-loading par défaut, config simple, et il s'auto-installe au premier lancement.

-- lua/config/lazy.lua
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",
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup("plugins", {
  defaults = { lazy = true },
  install = { colorscheme = { "catppuccin" } },
  checker = { enabled = true, notify = false },
  change_detection = { notify = false },
  ui = { border = "rounded" },
})

require("lazy").setup("plugins") charge automatiquement tous les fichiers du dossier lua/plugins/. Chaque fichier retourne une table (ou une liste de tables) décrivant les plugins et leur configuration. Le lazy-loading par défaut signifie que les plugins ne sont chargés qu'au besoin : au lancement d'une commande, à l'ouverture d'un type de fichier, ou sur un événement.

Le thème : Catppuccin Mocha

Catppuccin en variante Mocha : couleurs reposantes sur une longue session, contraste propre, et surtout des intégrations pour à peu près tous les plugins de l'écosystème.

{
  "catppuccin/nvim",
  name = "catppuccin",
  priority = 1000,
  lazy = false,
  opts = {
    flavour = "mocha",
    integrations = {
      cmp = true,
      gitsigns = true,
      neotree = true,
      treesitter = true,
      telescope = { enabled = true },
      which_key = true,
      native_lsp = {
        enabled = true,
        underlines = {
          errors = { "undercurl" },
          warnings = { "undercurl" },
        },
      },
    },
  },
  config = function(_, opts)
    require("catppuccin").setup(opts)
    vim.cmd.colorscheme("catppuccin")
  end,
}

priority = 1000 et lazy = false garantissent que le thème est chargé en premier, avant les plugins qui en dépendent pour leurs couleurs.

L'interface

Lualine : la barre de statut

Lualine remplace la statusline par défaut avec du contenu utile : branche Git, diffs, erreurs LSP, type de fichier, position dans le fichier.

{
  "nvim-lualine/lualine.nvim",
  event = "VeryLazy",
  opts = {
    options = {
      theme = "catppuccin",
      component_separators = { left = "", right = "" },
      section_separators = { left = "", right = "" },
    },
    sections = {
      lualine_a = { "mode" },
      lualine_b = { "branch", "diff", "diagnostics" },
      lualine_c = { "filename" },
      lualine_x = { "encoding", "fileformat", "filetype" },
      lualine_y = { "progress" },
      lualine_z = { "location" },
    },
  },
}

Bufferline : les onglets de buffers

Bufferline affiche les buffers ouverts comme des onglets, avec icône de fermeture au survol, compteur d'erreurs par buffer et navigation au clavier.

{
  "akinsho/bufferline.nvim",
  event = "VeryLazy",
  opts = {
    options = {
      diagnostics = "nvim_lsp",
      close_icon = "",
      buffer_close_icon = "󰅖",
      modified_icon = "●",
      offsets = {
        { filetype = "neo-tree", text = "File Explorer", highlight = "Directory" },
      },
    },
  },
}

Neo-tree : l'explorateur de fichiers

Neo-tree remplace netrw. Toggle sur <leader>n, avec le statut Git directement dans l'arborescence :

{
  "nvim-neo-tree/neo-tree.nvim",
  cmd = "Neotree",
  keys = {
    { "<leader>n", "<cmd>Neotree toggle<cr>", desc = "Toggle file explorer" },
  },
  opts = {
    filesystem = {
      follow_current_file = { enabled = true },
      use_libuv_file_watcher = true,
    },
    window = {
      width = 35,
      mappings = { ["<space>"] = "none" },
    },
    default_component_configs = {
      git_status = {
        symbols = {
          added = "✚",
          modified = "",
          deleted = "✖",
          renamed = "󰁕",
          untracked = "",
        },
      },
    },
  },
}

Alpha : le dashboard d'accueil

Au lancement sans argument, Alpha affiche un logo ASCII et des raccourcis vers les actions fréquentes.

{
  "goolord/alpha-nvim",
  event = "VimEnter",
  config = function()
    local alpha = require("alpha")
    local dashboard = require("alpha.themes.dashboard")

    dashboard.section.header.val = {
      "                                                     ",
      "  ███╗   ██╗███████╗ ██████╗ ██╗   ██╗██╗███╗   ███╗",
      "  ████╗  ██║██╔════╝██╔═══██╗██║   ██║██║████╗ ████║",
      "  ██╔██╗ ██║█████╗  ██║   ██║██║   ██║██║██╔████╔██║",
      "  ██║╚██╗██║██╔══╝  ██║   ██║╚██╗ ██╔╝██║██║╚██╔╝██║",
      "  ██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║",
      "  ╚═╝  ╚═══╝╚══════╝ ╚═════╝   ╚═══╝  ╚═╝╚═╝     ╚═╝",
    }

    dashboard.section.buttons.val = {
      dashboard.button("r", "  Fichiers récents", "<cmd>Telescope oldfiles<cr>"),
      dashboard.button("n", "  Nouveau fichier", "<cmd>ene<cr>"),
      dashboard.button("f", "󰈞  Chercher un fichier", "<cmd>Telescope find_files<cr>"),
      dashboard.button("g", "  Grep dans les fichiers", "<cmd>Telescope live_grep<cr>"),
      dashboard.button("c", "  Configuration", "<cmd>e $MYVIMRC<cr>"),
      dashboard.button("l", "󰒲  Lazy", "<cmd>Lazy<cr>"),
      dashboard.button("q", "  Quitter", "<cmd>qa<cr>"),
    }

    alpha.setup(dashboard.config)
  end,
}

Which-key : le guide des raccourcis

Which-key change tout sur la découvrabilité des keybindings : on appuie sur Space, on attend une demi-seconde, et un popup affiche tous les raccourcis organisés. Un cheat sheet permanent qui ne prend aucune place à l'écran.

Temps de démarrage

46 plugins, lazy-loading bien configuré, 35 ms au démarrage — vérifiable avec :Lazy profile. À comparer aux 2 à 5 secondes d'un éditeur GUI classique : quand on ouvre et ferme son éditeur des dizaines de fois par jour, la différence compte.

Conclusion

Monter une configuration Neovim de zéro demande un investissement réel. Mais une fois la structure en place — les dossiers, le init.lua, les modules config/ — chaque ajout devient trivial : créer un fichier Lua dans plugins/, ajouter une table, c'est tout.

Le résultat est un éditeur spécialisé pour sa propre façon de travailler, sans features inutiles, qui démarre instantanément et dont on comprend chaque couche. Lua et Lazy.nvim rendent la chose accessible même sans avoir jamais maîtrisé Vimscript.

PartagerLinkedInXBluesky

Articles similaires