Vim-Gewohnheit, KI daneben: Neovim als IDE

Ich habe Urlaub, und damit auch etwas mehr Zeit, um Dinge auszuprobieren. Ganz im Sinne des „Jahrzehnts der Bash" – das habe ich persönlich für mich ausgerufen :D – wollte ich weg von einer IDE wie Code OSS und mal etwas rein textbasiertes testen. Rausgekommen ist, wie ich finde, ein ziemlich cooles Setup mit Neovim: LSP-Support für Go und Bash, dazu eine KI-Integration, die nicht an einen bestimmten Anbieter gebunden ist.

Hauptsächlich habe ich getestet und verschiedene Varianten durchgespielt. Da ich bei Mittwald einen API-Token habe, lief der Test entsprechend darüber – der openai_compatible-Adapter würde aber genauso mit jedem anderen Server funktionieren, der dieselben API-Konventionen einhält. Auch lokale LLM würden gehen, aber auf meinem 2013er ThinkPad teste ich das lieber nicht.

Aus Erfahrung weiß ich: Wenn man den Qwen-Modellen im Kontext auf die Sprünge hilft, liefern sie durchaus passable Ergebnisse – zumindest bei Bash, das kann ich beurteilen. Bei allem anderen bin ich vorsichtig mit einem Urteil, weil ich schlicht nicht tief genug drinstecke, um zu sehen, wo das Modell danebenliegt.

Ganz allgemein: Ich glaube, die Entwicklung bei KI geht genau in diese Richtung. Open-Weight-Modelle werden kleiner und gleichzeitig leistungsfähiger – was vor kurzem noch ein Rechenzentrum brauchte, läuft heute auf brauchbarer Consumer-Hardware. Das ist für mich der eigentlich interessante Trend, spannender als jedes neue Flaggschiff-Modell: weg von "man muss zwingend über die API eines großen Anbieters gehen", hin zu "man kann sich sein Modell selbst hinstellen". Ich gehe davon aus, dass dieser Sprung zum lokalen Betrieb kommt – vielleicht nicht morgen, aber ich rechne fest damit. Und wenn er kommt, ist die Neovim-Config schon vorbereitet.

Warum Neovim

Man sollte immer offen für Neues sein, gleichzeitig darf man sich an eine Bedienung auch einfach gewöhnen, das ist normal und menschlich. Ich komme von Vim und Sway, da ist Neovim irgendwie ein logischer Schritt, glaube ich. Ich bin da ehrlich gesagt auch einfach faul und wollte nicht groß neu lernen. Trotzdem darf das nicht heißen, dass man aufhört zu verstehen. Copy-Paste ohne nachzudenken führt früher oder später zu Problemen. Man sollte zumindest verstehen, was ein Befehl oder eine Funktion tut. Nachvollziehen, was gerade in der eigenen Datei passiert, bevor man eine Änderung annimmt. Wer die Bash versteht und bedient, statt sich nur durchzuklicken, macht sich am Ende das Leben leichter.

Das 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, so wie man es installiert, bringt erstmal nichts mit – kein Plugin-Manager, keine vorgefertigte Config. Das ist der Unterschied zu fertigen "Distributionen" wie LazyVim oder NvChad, die ein komplettes Setup mit Plugins schon mitliefern. Ich hab mich für den eigenen Weg entschieden, weil ich gerne rumprobiere. Standard-Plugin-Manager dafür ist lazy.nvim, eingetragen in ~/.config/nvim/init.lua. Die Config bringt mit:

Der LSP-Teil gehört in den config-Block des nvim-lspconfig-Eintrags, also innerhalb von require("lazy").setup({ ... }):

{
  "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,
}

Nachlesen lässt sich das direkt in Neovim selbst mit :help lsp-config, oder in der Doku von nvim-lspconfig – dort steht auch, welche Settings die einzelnen Language Server (gopls, bashls, …) jeweils akzeptieren.

4. Neovim starten und LSPs installieren

Nach dem ersten Start installiert Lazy alle Plugins automatisch. Danach in Neovim mit :Mason prüfen, ob gopls und bash-language-server installiert sind – falls nicht, darauf navigieren und i drücken.

Die KI-Anbindung: CodeCompanion.nvim

Der entscheidende Teil ist der openai_compatible-Adapter von CodeCompanion. Er spricht nicht mit einem bestimmten Anbieter, sondern mit jedem Server, der die OpenAI-API-Konventionen einhält – Basis-URL, /v1/chat/completions, /v1/models. Für mich sieht das so aus:

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,

Der API-Key kommt als Umgebungsvariable:

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

Wer einen anderen Anbieter nutzt oder einen eigenen Server über vLLM betreibt: nur url und model anpassen, der Rest bleibt gleich.

Im Alltag

Im Chat-Fenster lässt sich der Dateiinhalt des aktuell offenen Buffers mit #{buffer} als Kontext mitschicken, ohne Code manuell reinzukopieren. Bei Aktionen, die etwas verändern, fragt CodeCompanion vorher nach:

Please select an option:
- g1 - Always accept
- g2 - Accept
- g3 - Reject
- g4 - Cancel

Das sind Tastenkombinationen im Normal-Modus, keine Texteingabe: g gefolgt von der Zahl. Für den Anfang lohnt sich g2 (einmal erlauben) statt gleich g1 (immer erlauben) – dann sieht man erstmal, was das Tool tatsächlich tut, bevor man ihm dauerhaft freie Hand gibt.

Für direkte Änderungen im Code, ohne den Umweg über den Chat, gibt's das Inline-Editieren:

<leader>ci

Mit vorheriger Auswahl (v + Bewegung) bezieht sich das gezielt auf den markierten Codeabschnitt. Das Modell schreibt dann direkt in den Buffer – kein separates Diff-Fenster, aber jede Änderung lässt sich ganz normal mit u rückgängig machen.

Warum das wichtig ist

Man sollte immer neugierig sein und dazulernen, Fehler machen, finden und ausbessern. Mir gefällt der Fokus auf Bash extrem. Andere brauchen eine grafische Oberfläche – jeder kann sich das Werkzeug nehmen, das ihm entspricht.