Neovim als IDE mit KI-Anbindung

Neovim von Grund auf als IDE eingerichtet: Plugin-Manager, LSP für Go und Bash, dazu eine KI-Anbindung über CodeCompanion.nvim, die per openai_compatible-Adapter mit jedem OpenAI-kompatiblen Server spricht – egal ob gehosteter Endpunkt oder selbst betrieben über vLLM.

Den Hintergrund und meine Gedanken dazu – warum Neovim, warum anbieterunabhängig – gibt's im Blogeintrag Vim-Gewohnheit, KI daneben. Hier nur die reine Anleitung zum Nachbauen.

Setup

1. Voraussetzungen installieren

Auf openSUSE Tumbleweed reicht ein Befehl für alles, was gebraucht wird – Neovim selbst, Git für den Plugin-Manager, Node.js/npm für den Bash-Language-Server, Go für gopls, und ripgrep, das Telescope für die Fuzzy-Suche braucht:

sudo zypper install neovim git nodejs npm go ripgrep

2. Config-Verzeichnis anlegen

mkdir -p ~/.config/nvim

Neovim liest seine Konfiguration aus ~/.config/nvim/init.lua.

3. Plugin-Manager und Plugins eintragen

Pures Neovim bringt kein Plugin-Manager mit. Standard dafür ist lazy.nvim, eingetragen in ~/.config/nvim/init.lua:

-- Bootstrap lazy.nvim
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({
  { "neovim/nvim-lspconfig" },
  { "mason-org/mason.nvim" },
  { "mason-org/mason-lspconfig.nvim" },
  { "hrsh7th/nvim-cmp" },
  { "hrsh7th/cmp-nvim-lsp" },
  { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" },
  { "nvim-telescope/telescope.nvim", dependencies = { "nvim-lua/plenary.nvim" } },
  { "nvim-tree/nvim-tree.lua" },
  { "olimorris/codecompanion.nvim", dependencies = { "nvim-lua/plenary.nvim", "nvim-treesitter/nvim-treesitter" } },
})

Drei der Plugins brauchen noch eine minimale eigene Config, sonst tun sie nichts. Kommt ebenfalls in die init.lua:

-- Mason installiert die eigentlichen LSP-Server-Binaries (gopls, bash-language-server).
-- mason-lspconfig verbindet das mit nvim-lspconfig und lädt bei Bedarf automatisch nach.
require("mason").setup()
require("mason-lspconfig").setup({
  ensure_installed = { "gopls", "bashls" },
})

-- nvim-cmp: ohne diese Config zeigt der Editor keine Vervollständigungs-Vorschläge an
local cmp = require("cmp")
cmp.setup({
  mapping = cmp.mapping.preset.insert({
    ["<CR>"] = cmp.mapping.confirm({ select = true }),
    ["<Tab>"] = cmp.mapping.select_next_item(),
  }),
  sources = {
    { name = "nvim_lsp" },
  },
})

-- nvim-tree: Taste zum Ein-/Ausblenden des Dateibaums
vim.keymap.set("n", "<leader>e", ":NvimTreeToggle<CR>", { desc = "Dateibaum umschalten" })

4. LSP konfigurieren

Der LSP-Teil gehört in den config-Block des nvim-lspconfig-Eintrags:

{
  "neovim/nvim-lspconfig",
  config = function()
    local capabilities = require("cmp_nvim_lsp").default_capabilities()

    vim.lsp.config("gopls", {
      capabilities = capabilities,
      settings = {
        gopls = {
          gofumpt = true,
          staticcheck = true,
        },
      },
    })

    vim.lsp.config("bashls", {
      capabilities = capabilities,
    })

    vim.lsp.enable({ "gopls", "bashls" })
  end,
}

Details zu den Settings der einzelnen Language Server: :help lsp-config in Neovim, oder die Doku von nvim-lspconfig.

5. Neovim starten

Nach dem ersten Start installiert Lazy alle Plugins automatisch, mason-lspconfig lädt dank ensure_installed direkt gopls und bash-language-server nach. Zur Kontrolle in Neovim :Mason öffnen – dort sollten beide mit einem grünen Häkchen stehen. Falls nicht, draufnavigieren und i drücken.

KI-Anbindung: CodeCompanion.nvim

Der openai_compatible-Adapter spricht mit jedem Server, der die OpenAI-API-Konventionen einhält – Basis-URL, /v1/chat/completions, /v1/models:

mein_llm = function()
  return require("codecompanion.adapters").extend("openai_compatible", {
    env = {
      url = "https://llm.aihosting.mittwald.de",
      chat_url = "/v1/chat/completions",
      models_endpoint = "/v1/models",
      api_key = "MITTWALD_LLM_API_KEY", -- Name der Umgebungsvariable, kein Klartext-Key
    },
    schema = {
      model = {
        default = "mein-modell-name",
      },
    },
  })
end,

Wichtig: api_key bekommt hier den Namen der Umgebungsvariable, nicht den Key selbst als Klartext. CodeCompanion löst das intern auf und liest den tatsächlichen Wert zur Laufzeit aus der Shell-Umgebung.

Der API-Key landet nicht in der Config:

echo 'export MITTWALD_LLM_API_KEY="dein-token-hier"' >> ~/.bashrc
source ~/.bashrc

Welche Modelle der Endpunkt anbietet, lässt sich vorher abfragen:

curl -s https://llm.aihosting.mittwald.de/v1/models \
  -H "Authorization: Bearer $MITTWALD_LLM_API_KEY" | jq

Anderer Anbieter oder eigener Server über vLLM: nur url und model anpassen, der Rest bleibt gleich. Man kann auch magische Weichen bauen, das habe ich aber noch nicht getestet.

Bedienung