Complétion IA dans Neovim : Codeium, Gemini et nvim-cmp

Complétion IA dans Neovim : Codeium, Gemini et nvim-cmp

·5 min de lecture·Mis à jour le 23 janvier 2026

Pourquoi trois couches de complétion

Le LSP reste le standard de la complétion, mais l'IA générative a multiplié les options — et tout empiler dans un seul menu devient vite illisible.

D'où un système à trois couches distinctes, chacune résolvant un problème spécifique : le ghost text pour les suggestions passives, un modèle IA dans le menu cmp pour les cas élaborés, et nvim-cmp classique pour le LSP, les snippets et le reste.

Couche 1 : NeoCodeium (ghost text)

NeoCodeium est le client Neovim pour Codeium : gratuit et illimité (pas de tokens à tracker), en ghost text grisé directement dans le buffer plutôt qu'en popup, et sans conflit avec nvim-cmp.

Le ghost text couvre les cas évidents : fermer une parenthèse, finir un nom de variable, du boilerplate. On accepte avec un raccourci, ou on continue à taper et la suggestion disparaît.

{
  "monkoose/neocodeium",
  event = "VeryLazy",
  config = function()
    local neocodeium = require("neocodeium")
    neocodeium.setup()

    -- Alt-f pour accepter la suggestion
    vim.keymap.set("i", "<A-f>", neocodeium.accept)
    -- Alt-n / Alt-p pour naviguer entre les suggestions
    vim.keymap.set("i", "<A-n>", neocodeium.cycle_or_complete)
    vim.keymap.set("i", "<A-p>", function()
      neocodeium.cycle_or_complete(-1)
    end)
    -- Alt-c pour effacer la suggestion
    vim.keymap.set("i", "<A-c>", neocodeium.clear)
  end,
}

Les keybindings sont choisis pour éviter tout conflit avec nvim-cmp.

Couche 2 : Minuet AI (Gemini dans le menu cmp)

Minuet intègre Gemini dans le menu cmp. Contrairement au ghost text passif, c'est une source on-demand qui apparaît dans le même menu que le LSP.

{
  "milanglacier/minuet-ai.nvim",
  config = function()
    require("minuet").setup({
      provider = "gemini",
      provider_options = {
        gemini = {
          model = "gemini-2.5-flash",
          api_key = "GEMINI_API_KEY",
        },
      },
      cmp = {
        enable_auto_complete = false,
      },
    })
  end,
}

Le modèle gemini-2.5-flash offre un bon équilibre vitesse/qualité, la clé API vient de la variable d'environnement GEMINI_API_KEY, et enable_auto_complete = false empêche Gemini de s'activer automatiquement — on l'appelle avec Alt-y dans le menu cmp.

C'est délibéré : l'IA générative est utile pour les suggestions compliquées (refactoring, patterns complexes, génération de doc), mais ne doit pas ralentir le workflow normal.

Couche 3 : nvim-cmp (le moteur principal)

nvim-cmp orchestre toutes les sources : popup, navigation, confirmation.

{
  "hrsh7th/nvim-cmp",
  event = "InsertEnter",
  dependencies = {
    "hrsh7th/cmp-nvim-lsp",
    "hrsh7th/cmp-buffer",
    "hrsh7th/cmp-path",
    "saadparwaiz1/cmp_luasnip",
  },
  config = function()
    local cmp = require("cmp")
    local luasnip = require("luasnip")

    cmp.setup({
      snippet = {
        expand = function(args)
          luasnip.lsp_expand(args.body)
        end,
      },
      completion = { completeopt = "menu,menuone,noinsert" },
      window = {
        completion = cmp.config.window.bordered(),
        documentation = cmp.config.window.bordered(),
      },
      performance = {
        fetching_timeout = 2000,
      },
      mapping = cmp.mapping.preset.insert({
        ["<C-n>"] = cmp.mapping.select_next_item(),
        ["<C-p>"] = cmp.mapping.select_prev_item(),
        ["<C-Space>"] = cmp.mapping.complete(),
        ["<C-e>"] = cmp.mapping.abort(),
        ["<CR>"] = cmp.mapping.confirm({ select = true }),
        ["<A-y>"] = require("minuet").make_cmp_map(),
        ["<Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_next_item()
          elseif luasnip.expand_or_jumpable() then
            luasnip.expand_or_jump()
          else
            fallback()
          end
        end, { "i", "s" }),
        ["<S-Tab>"] = cmp.mapping(function(fallback)
          if cmp.visible() then
            cmp.select_prev_item()
          elseif luasnip.jumpable(-1) then
            luasnip.jump(-1)
          else
            fallback()
          end
        end, { "i", "s" }),
      }),
      sources = cmp.config.sources({
        { name = "minuet" },
        { name = "nvim_lsp" },
        { name = "luasnip" },
        { name = "path" },
      }, {
        { name = "buffer" },
      }),
    })
  end,
}

Quatre choix structurants : les sources ordonnées par priorité (Minuet, puis LSP, LuaSnip, path, et buffer en fallback), des fenêtres bordées pour la lisibilité, un timeout de 2 secondes (fetching_timeout = 2000) qui empêche une source IA lente de bloquer le workflow, et des Tab/S-Tab intelligents qui naviguent dans le menu cmp quand il est ouvert, et sautent entre les positions de snippets sinon.

LuaSnip : le moteur de snippets

LuaSnip gère les snippets, avec le pack friendly-snippets qui fournit des snippets VSCode-style pour tous les langages courants.

{
  "L3MON4D3/LuaSnip",
  build = "make install_jsregexp",
  dependencies = {
    {
      "rafamadriz/friendly-snippets",
      config = function()
        require("luasnip.loaders.from_vscode").lazy_load()
      end,
    },
  },
}

Les snippets se déclenchent via nvim-cmp et la navigation entre positions se fait au Tab/S-Tab grâce au mapping ci-dessus.

Comment tout ça coexiste

Les trois couches ne se gênent pas : NeoCodeium affiche du ghost text dans le buffer sans toucher au menu cmp, nvim-cmp gère le popup classique (LSP, snippets, path), et Minuet s'intègre comme source cmp mais ne se déclenche que manuellement.

En pratique, pendant qu'on code :

  1. Le ghost text Codeium apparaît en grisé — Alt-f pour accepter.
  2. Pour les résultats LSP, le menu cmp s'ouvre automatiquement ou via C-Space.
  3. Pour une suggestion IA plus élaborée, Alt-y interroge Gemini sans quitter le menu.

Le timeout de 2 secondes est important : si Gemini traîne, la complétion continue avec les autres sources, sans freeze.

Conclusion

Ce système à trois couches combine l'IA pour les suggestions intelligentes et le LSP pour la précision, chaque couche ayant son propre mode d'interaction. On sait toujours d'où vient une suggestion et comment l'accepter — un setup qui tourne au quotidien sur des projets TypeScript et Python sans ralentissement.

PartagerLinkedInXBluesky

Articles similaires