Vims Mapping-System ist eines der Werkzeuge, an denen sich die ersten ernsthaften Konfigurations-Schritte machen — und gleichzeitig eines der häufigsten Stolperfelder. Mappings sind modus-spezifisch: was im Normal-Mode eine Aktion auslöst, kann im Insert-Mode etwas anderes tun oder gar nichts. Sie sind rekursiv oder nicht-rekursiv — und die Wahl zwischen nmap und nnoremap entscheidet, ob Mappings unerwartete Ketten bilden oder sauber stoppen. Sie können global oder buffer-lokal wirken, bedingt ausgewertet werden, und sie haben einen eigenen Namespace (<Plug>) für Plugin-Autoren, mit dem sich Plugin-Aktionen sauber von Endnutzer-Mappings trennen lassen. Dieser Artikel fasst alle diese Aspekte zusammen — mit Schwerpunkt auf der Praxis-relevanten Frage „warum macht meine Taste nicht das, was ich erwarte?".

Mapping-Grundlagen — die Befehlsfamilie

Vim hat für jeden Modus ein eigenes Mapping-Kommando. Die acht relevanten:

BefehlModus
:nmap / :nnoremapNormal-Mode
:imap / :inoremapInsert-Mode
:vmap / :vnoremapVisual und Select
:xmap / :xnoremapVisual (ohne Select)
:smap / :snoremapSelect (ohne Visual)
:omap / :onoremapOperator-pending
:cmap / :cnoremapCommand-Line
:tmap / :tnoremapTerminal-Mode (Vim 8+)
:map / :noremapNormal + Visual + Operator-pending (Sammel-Variante)

Das Grund-Muster ist :[modus][nore]map [flags] <lhs> <rhs> — Modus-Präfix, optional nore für non-recursive, optionale Flags, links die zu mappende Tastenfolge, rechts das auszuführende Resultat.

vim Mapping-Anatomie
" Anatomie:
" :nnoremap <silent> <leader>w :write<CR>
"  ↑       ↑         ↑          ↑
"  Modus   Flag      lhs        rhs

" Im Klartext:
" - Normal-Mode (n)
" - non-recursive (nore)
" - mit <silent> (keine Echo-Ausgabe)
" - Tastenfolge "Leader + w"
" - führt :write<CR> aus

Die <lhs> (left-hand side) ist die getippte Tastenfolge, die <rhs> (right-hand side) das, was Vim stattdessen ausführt. Beides kann jede Tasten-Notation enthalten — <C-x>, <leader>, <CR>, <Esc> und so weiter (siehe Tasten-Notation).

Rekursiv vs. non-recursive — die wichtigste Wahl

Der einzige fundamentale Unterschied zwischen den map-Varianten ist die Frage, ob die rechte Seite eines Mappings selbst noch durch andere Mappings interpretiert wird.

nmap — rekursiv

nmap a b mappt a auf b. Wenn b selbst gemappt ist (z. B. nmap b c), folgt Vim der Kette: abc. Endergebnis: a führt c aus.

Bei Endlos-Schleifen schützt Vim sich selbst — nmap a b und nmap b a führt nicht zu unendlicher Rekursion, sondern zu einer Fehlermeldung „recursive mapping". Aber die unbeabsichtigte Verkettung über zwei oder drei Stufen passiert in der Praxis ständig und ist schwer zu debuggen.

nnoremap — non-recursive

nnoremap a b mappt a auf b und stoppt dort. Auch wenn b selbst gemappt ist, wird das Ziel als rohe Tastenfolge ausgeführt — ohne weitere Mapping-Auflösung.

Praxis-Empfehlung

Immer nnoremap (und entsprechende Modus-Varianten: inoremap, vnoremap, …) verwenden. Es gibt genau eine legitime Ausnahme: <Plug>-Mappings (siehe nächster Abschnitt). In allen anderen Fällen ist die rekursive Variante eine Falle.

vim Der Klassiker — warum nnoremap wichtig ist
" GEFÄHRLICH: rekursives Mapping
" Ziel: j soll "visuell eine Zeile runter" (gj) sein.
nmap j gj

" Problem: wenn ein Plugin später nmap gj <C-d> definiert,
" wird daraus: j → gj → <C-d> — Halbe Seite scroll statt
" einer visuellen Zeile.

" SICHER: non-recursive
nnoremap j gj
" j führt jetzt unbedingt gj aus — egal was Plugins später tun.

Diese Gefahr ist nicht theoretisch. Vim-Plugin-Ökosysteme haben hunderte Mappings, und Konflikte sind die Regel, nicht die Ausnahme. nnoremap ist die einzige Verteidigungslinie.

<Plug>-Mappings — die Plugin-Konvention

<Plug> ist Vims Konvention für Plugin-interne Mapping-Anker. Plugins definieren ihre Aktionen unter <Plug>(name) und überlassen es dem Endnutzer, eine konkrete Taste darauf zu binden. Diese Trennung erlaubt es, Plugin-Mappings ohne Konflikte zu konfigurieren.

Ein Beispiel — angenommen, ein hypothetisches Plugin definiert:

vim im Plugin-Code
" Plugin definiert seine Aktion unter einem <Plug>-Anker
nnoremap <Plug>(MyPlugin-Action) :call s:my_plugin_action()<CR>

Der Endnutzer bindet diese Aktion an eine Taste seiner Wahl:

vim ~/.vimrc
" WICHTIG: hier ist nmap (rekursiv) richtig, nicht nnoremap!
" <Plug>(MyPlugin-Action) ist selbst ein Mapping, das aufgelöst
" werden MUSS. nnoremap würde die Auflösung verhindern und
" das wörtliche "<Plug>(MyPlugin-Action)" in den Buffer schreiben.
nmap <leader>a <Plug>(MyPlugin-Action)

Das ist die einzige verbreitete Ausnahme von der „immer nnoremap"-Regel. Wer ein Plugin-Mapping mit nnoremap bindet und sich wundert, dass nichts funktioniert: das ist der Grund.

<Plug>(Name) ist eine reine Konvention, kein Sprachmerkmal — Vim behandelt das <Plug> als spezielle Tasten-Notation, die niemals von echten Tasten gesendet wird. Das macht sie zu einem garantiert konflikt-freien Namespace für Plugin-Aktionen.

Mapping-Flags im Detail

Vor der <lhs> können Flags stehen, die das Mapping-Verhalten modifizieren:

FlagWirkung
<silent>unterdrückt das Echo der <rhs> in der Command-Line
<buffer>Mapping gilt nur im aktuellen Buffer
<expr><rhs> wird als Vimscript-Expression evaluiert; das Ergebnis wird ausgeführt
<nowait>Mapping wartet nicht, ob noch eine längere Mapping-Kette passt
<unique>Fehler, wenn das Mapping schon existiert — sauberes Plugin-Verhalten
<script>rekursive Auflösung nur für Mappings desselben Scripts

<silent> ist im Alltag die nützlichste. Viele Mappings führen :-Befehle aus, deren Echo am Bildschirm visuelles Rauschen produzieren:

vim <silent> vs. ohne
" Ohne <silent>: nach jedem Druck blinkt ":noh" kurz auf
nnoremap <leader>h :noh<CR>

" Mit <silent>: lautlos
nnoremap <silent> <leader>h :noh<CR>

<buffer> ist der übliche Weg, filetype-spezifische Mappings nur in einem bestimmten Buffer aktiv zu haben:

vim ftplugin/go.vim
" Gilt NUR in Go-Buffern. Global wäre <leader>r für andere
" Sprachen oder generische Befehle verfügbar.
nnoremap <buffer> <localleader>r :!go run %<CR>
nnoremap <buffer> <localleader>t :!go test ./...<CR>

<expr> ist eine sehr mächtige, aber selten gebrauchte Variante. Sie erlaubt, dass die <rhs> zur Laufzeit aus einer Vimscript-Funktion berechnet wird:

vim <expr>-Beispiel
" Beim Druck von <CR> in der Command-Line: wenn die Auto-Completion
" ein Popup zeigt, akzeptiere die Auswahl. Sonst normales <CR>.
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<CR>"

<expr> ist die Grundlage für viele Completion- und Snippet-Plugins.

Mappings inspizieren — :map und :verbose

Wer wissen will, welche Mappings aktiv sind, hat zwei Werkzeuge:

:map und Modus-Varianten

text Mappings auflisten
:map                  " alle Mappings (alle Modi)
:nmap                 " nur Normal-Mode-Mappings
:imap                 " nur Insert-Mode
:nmap <leader>        " alle Normal-Mappings, die mit <leader> beginnen
:nmap <leader>w       " konkretes Mapping anzeigen

Bei der Ausgabe steht ganz links ein Modus-Buchstaben-Code: n = normal, i = insert, v = visual+select, x = visual, s = select, o = operator-pending, c = command, ! = insert+command, ein Leerzeichen = normal+visual+operator-pending (die :map-Sammelvariante).

:verbose zeigt den Ursprung

Der eigentliche Diagnose-Befehl bei Mapping-Konflikten:

text :verbose in Aktion
:verbose nmap <leader>w
" Ausgabe:
" n  <leader>w   * :write<CR>
"         Last set from ~/.vimrc line 42

:verbose nmap <C-p>
" Ausgabe:
" n  <C-p>       * :Files<CR>
"         Last set from .../plugged/fzf.vim/plugin/fzf.vim line 18

:verbose ergänzt jede Mapping-Ausgabe um die Datei und Zeilennummer, in der das Mapping zuletzt definiert wurde. Bei „diese Taste tut nicht das, was sie sollte" ist das der schnellste Weg zur Wahrheit: oft ist die Antwort ein Plugin, das stillschweigend ein eigenes Mapping eingerichtet hat.

:verbose funktioniert übrigens für viele Vim-Strukturen — :verbose set <option>? zeigt den Ursprung einer Option, :verbose autocmd FileType go zeigt die Quelle eines Autocmds. Eines der besten Vim-Debug-Werkzeuge.

Mappings entfernen

Drei Befehle zum Aufräumen:

BefehlWirkung
:nunmap <lhs>entfernt das angegebene Normal-Mode-Mapping
:nmapclearentfernt alle Normal-Mode-Mappings (Vorsicht — auch eigene!)
:mapclear <buffer>entfernt nur die buffer-lokalen Mappings

:nunmap (und Modus-Geschwister wie :iunmap, :vunmap) ist im Alltag der häufigste Befehl: ein Plugin oder eine vergessene Konfig-Zeile hat ein Mapping eingerichtet, das stört — :nunmap <key> macht es weg, bis Vim neu gestartet wird oder die Definition erneut geladen wird.

Eine Sonderform: :silent! nunmap <key> — das silent! unterdrückt den Fehler, falls das Mapping nicht existiert. Sinnvoll in Konfig-Zeilen, die idempotent ausführbar sein sollen.

Häufige Konflikt-Muster

Vier Mapping-Probleme, die in der Praxis am häufigsten auftauchen:

Konflikt zwischen <leader>x und einem Plugin-Default

Ein Plugin definiert <leader>g für seine Hauptfunktion, du nutzt <leader>g schon für Git. Lösung: dein Mapping mit nnoremap <leader>g … nach dem Plugin-Laden in der .vimrc definieren. Wer Plugin-Manager wie vim-plug nutzt, packt die eigenen Mappings ans Ende der Konfig — sie überschreiben dann die Plugin-Defaults.

Mapping wirkt, aber Vim wartet kurz

Wer nnoremap <leader>w :w<CR> und nnoremap <leader>wa :wa<CR> definiert, hat zwei Mappings mit gemeinsamem Präfix. Vim wartet nach <leader>w eine kurze Zeit (Default timeoutlen=1000, also 1 Sekunde) ab, ob noch ein a folgt. Während dieser Wartezeit fühlt sich die Eingabe träge an.

Lösung A: <nowait> als Flag bei der kurzen Variante. Lösung B: kürzere timeoutlen setzen, z. B. set timeoutlen=300. Lösung C: die längere Variante umbenennen.

jk/jj für Esc — kurze Verzögerung beim echten j

Wer inoremap jk <Esc> definiert, hat das Problem, dass jedes einzelne j in Insert eine kurze Wartezeit hat — Vim wartet, ob ein k folgt. Bei deutschem oder englischem Text mit j in normalen Wörtern (selten, aber kommt vor) fühlt sich das holprig an.

Lösung: set timeoutlen=200 reduziert die Wartezeit auf 200 ms — kaum spürbar bei normalem Tippen, aber genug Zeit für das absichtliche jk.

„Mein Mapping tut nichts!"

Häufigste Ursachen, in der Reihenfolge der Wahrscheinlichkeit:

  • Modus falsch: nmap definiert, aber im Insert-Mode gedrückt. Mit :verbose imap <key> prüfen.
  • <leader> zur falschen Zeit gesetzt: let mapleader = ... muss vor den Mappings stehen.
  • <CR> vergessen: ein :-Befehl im Mapping ohne <CR> am Ende landet nur in der Command-Line, ohne ausgeführt zu werden.
  • Terminal-Emulator schluckt die Taste: <C-S-Tab>, <C-;> und ähnliche kommen oft gar nicht erst bei Vim an. Mit <C-v> + Taste im Insert-Mode prüfen, was Vim wirklich sieht.

Eine kleine Sammlung nützlicher Mappings

Zum Abschluss eine Auswahl von Mappings, die in vielen Konfigurations-Anleitungen auftauchen und im Alltag tatsächlich Zeit sparen:

vim ~/.vimrc — Mapping-Sammlung
" Speichern mit <leader>w
nnoremap <silent> <leader>w :update<CR>

" Search-Highlight ausschalten
nnoremap <silent> <leader>h :noh<CR>

" Buffer-Wechsel
nnoremap <silent> <leader>n :bnext<CR>
nnoremap <silent> <leader>p :bprevious<CR>

" Quick-Edit der Konfig
nnoremap <silent> <leader>v :e $MYVIMRC<CR>
nnoremap <silent> <leader>V :source $MYVIMRC<CR>

" jk als Esc-Alternative im Insert-Mode
inoremap jk <Esc>

" Y wie D und C — bis Zeilenende (in modernem Vim Default)
nnoremap Y y$

" Center-Cursor beim Scrollen
nnoremap <C-d> <C-d>zz
nnoremap <C-u> <C-u>zz
nnoremap n nzz
nnoremap N Nzz

" Visual-Mode: Einrückung bleibt aktiv
vnoremap > >gv
vnoremap < <gv

" Verschieben markierter Zeilen
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv

Jedes dieser Mappings ist non-recursive (nnoremap/inoremap/vnoremap), <silent> wo sinnvoll, und gehört in die Phase nach der Leader-Setzung in der .vimrc. Sie sind ein guter Startpunkt — und ein Beispiel, dass viele „kleine Komfort-Mappings" zusammen einen erheblichen Workflow-Gewinn bringen.

Interessantes

Modus-Buchstaben in `:map`-Ausgabe lesen

Der Buchstabe ganz links zeigt den Modus: n (normal), i (insert), v (visual+select), x (visual), s (select), o (operator-pending), c (command), t (terminal), ! (insert+command), und ein Leerzeichen für die Sammel-Variante :map. Wer Mappings debuggt, sollte den Buchstaben immer mitlesen.

`` schützt vor versehentlichem Überschreiben

Plugin-Autoren nutzen <unique>, um sicherzustellen, dass sie keine existierenden User-Mappings überschreiben. nnoremap <unique> <leader>x :MyCmd<CR> schlägt fehl, wenn <leader>x schon belegt ist — statt still zu überschreiben. Sehr nützlich beim Plugin-Schreiben.

`` als Alternative zu `:` plus ``

Seit Vim 8.2 / Neovim 0.7 gibt es <Cmd>. Statt :do_something<CR> schreibt man <Cmd>do_something<CR>. Vorteil: kein Modus-Wechsel in den Command-Line-Mode, kein Side-Effekt auf Visual-Selektionen, kein Echo. In Neovim-Konfigs Standard, in Vim 9-Konfigs zunehmend.

`:map` ohne Argument zeigt nur „interessante" Mappings

Vims :map-Auflistung filtert Default-Mappings raus und zeigt nur die User- und Plugin-Mappings. Wer wirklich alle Mappings sehen will, nutzt :map! (Insert+Command-Line) bzw. die Sammel-Varianten — und scrollt durch.

`:redir` für Mapping-Sammlung in eine Datei

Wer alle Mappings für eine Doku oder einen Backup sammeln will: :redir > mappings.txt | silent map | redir END. Damit landet die komplette Mapping-Tabelle in einer Datei zum Durchsuchen.

`` ist nicht durch Tastatur erreichbar

<Plug>(Name) wird niemals von einer echten Taste gesendet. Es ist ein interner Mapping-Anker, der nur über andere Mappings auflösbar ist. Daher der konflikt-freie Namespace: kein User kann „aus Versehen" ein <Plug>-Mapping triggern, das er nicht auch explizit auf eine Taste gelegt hat.

Weiterführende Ressourcen

Externe Quellen

Verwandte Artikel

/ Weiter

Zurück zu Modi

Zur Übersicht