Über relative Bewegung (h, j, w, f) hinaus hat Vim eine vollständige Familie absoluter Navigation: direkte Zeilen-Sprünge mit gg und G, Screen-Anker wie H/M/L, benannte Marks (lokal ma-mz, global mA-mZ), und zwei automatische Positions-Listen — die Jumplist (jeder größere Sprung wird gespeichert, Ctrl-o führt zurück) und die Changelist (jeder Edit wird gespeichert, g; führt zurück). Diese vier Familien zusammen sind Vims „Zeitreisemaschine": man kann sich beliebig durch eine Datei bewegen und mit einer Taste an die letzte Edit-Stelle, die letzte Sprung-Stelle oder ein benanntes Lesezeichen zurückkehren. Dieser Artikel deckt alle vier ab und zeigt, wie sie zusammen einen Workflow ergeben, der reines Maus-Scrolling weit hinter sich lässt.
Direkte Zeilen-Sprünge
Die einfachsten absoluten Bewegungen:
| Befehl | Bewegung |
|---|---|
gg | zur ersten Zeile im Buffer |
G | zur letzten Zeile im Buffer |
NG oder Ngg | zur Zeile N (z. B. 42G) |
:N | Ex-Variante zur Zeile N (z. B. :42 plus Enter) |
:$ | zur letzten Zeile (Ex-Variante) |
N% | zu N Prozent der Datei (z. B. 50% = Mitte) |
42G und :42<CR> sind funktional identisch — beide springen zur Zeile 42. Wer die Zeile aus einem Compiler-Fehler oder einer Linter-Meldung kennt, ist mit der direkten Eingabe schneller als mit gg plus 41j.
Eine kleine Eigenheit: 0G springt zur letzten Zeile (nicht zu Zeile 0), weil Vim Count 0 nicht als „springe zur Zeile 0" interpretiert. Konsistent mit der Tatsache, dass Zeile 1 die erste ist.
Screen-Anker: H, M, L
Drei Befehle springen zu sichtbaren Positionen auf dem aktuellen Bildschirm:
| Taste | Bewegung |
|---|---|
| H | High — zur obersten sichtbaren Zeile |
| M | Middle — zur mittleren sichtbaren Zeile |
| L | Low — zur untersten sichtbaren Zeile |
Mit Count: 5H springt zur fünften Zeile von oben, 5L zur fünften von unten. H/M/L arbeiten relativ zum Bildschirm-Inhalt, nicht zur Datei — was sie zur idealen Wahl macht, wenn man eine Stelle ungefähr sieht, aber nicht die genaue Zeilennummer kennt.
Bildschirm zeigt Zeilen 50-100 (aktuelle Ansicht):
50 ── erste sichtbare Zeile ← H springt hierhin
...
75 ── mittlere sichtbare Zeile ← M springt hierhin
...
100 ── letzte sichtbare Zeile ← L springt hierhinEine Plus-Empfehlung: nach Sprüngen mit H/M/L mit zz den Cursor wieder vertikal zentrieren — sonst springt der Bildschirm-Inhalt nicht mit dem Cursor.
Marks: benannte Positionen
Vims Marks sind benannte Cursor-Positionen, die du an beliebige Stellen setzen und später anspringen kannst. Wie kleine Lesezeichen im Buffer.
| Befehl | Aktion |
|---|---|
m + Kleinbuchstabe | Mark in aktuellem Buffer setzen (z. B. ma, mb, mc) |
m + Großbuchstabe | globale Mark setzen (z. B. mA, mB — File-übergreifend) |
` + Buchstabe | zur exakten Position (Zeile + Spalte) der Mark springen |
' + Buchstabe | zur Zeile der Mark springen (Anfang der Zeile) |
:marks | alle aktiven Marks auflisten |
:delmarks a b c | spezifische Marks löschen |
:delmarks! | alle lokalen Marks (a-z) im aktuellen Buffer löschen |
Der wichtige Unterschied: lokale Marks (a-z) gelten nur im aktuellen Buffer und werden in der viminfo pro Buffer gespeichert. Globale Marks (A-Z) gelten datei-übergreifend — mA setzt eine Mark, 'A springt von überall dorthin, auch wenn die Datei aktuell nicht geöffnet ist (Vim öffnet sie automatisch).
` vs. ' — Spalte oder nur Zeile
Buffer:
line 1
line 2 X mark gesetzt mit "ma" — Cursor war an Spalte 8 (X)
line 3
line 4
" Nach Navigation zu line 4, Cursor an Spalte 1:
`a → Sprung zu line 2, Spalte 8 (exakte Position)
'a → Sprung zu line 2, Spalte 1 (Anfang der Mark-Zeile)Im Alltag ist ` (Backtick) meist die richtige Wahl — Spalte plus Zeile. ' (Apostroph) ist gelegentlich nützlich, wenn die Spalte irrelevant ist (z. B. wenn man am Zeilenanfang des Lesezeichens anfangen will, egal wo der Cursor beim Setzen war).
Setzen und Springen — typischer Workflow
" In Funktion foo() arbeiten, kurz Abstecher in bar() machen wollen
ma " Mark "a" setzen (aktuelle Position)
/bar<CR> " zu bar() suchen
" ... etwas anschauen ...
`a " zurück zur Mark, exakte PositionFür File-übergreifend:
" Im wichtigen File arbeiten:
mF " globale Mark "F" setzen
" ... weit später, evtl. in einer anderen Datei, anderem Tab ...
'F " Vim öffnet das gemarkte File und springt zur ZeileGlobale Marks sind die schnellste Variante, „Lieblings-Stellen" im Projekt zu markieren — z. B. die Datei mit der Konfiguration, die Hauptfunktion, das Routing-Modul.
Spezial-Marks — automatisch gesetzt
Neben den selbst-gesetzten Marks pflegt Vim eine Reihe automatischer Marks, die du nicht setzen musst — sie entstehen durch Editor-Aktionen:
| Mark | Bedeutung |
|---|---|
`` | Position vor dem letzten Sprung (Two-Backtick) |
`. | Position des letzten Edits |
`^ | Position, an der zuletzt der Insert-Mode verlassen wurde |
`[ | Anfang des letzten yank, delete oder change |
`] | Ende des letzten yank, delete oder change |
`< | Anfang der letzten Visual-Selektion |
`> | Ende der letzten Visual-Selektion |
(zwei Backticks) ist im Alltag besonders nützlich: nach jedem größeren Sprung führt es zurück zur letzten Position. Wer mit `/pattern` woandershin gesprungen ist, kommt mit zurück.
`. springt zur letzten Edit-Position — was bei „wo war ich gerade beim Editieren?"-Workflows den entscheidenden Schritt spart.
" Edit in foo.go, dann Suche im selben Buffer
ciwneuer<Esc> " Wort ändern
/someOtherWord<CR> " woanders hin suchen
`. " zurück zum letzten Edit
`` " zurück zur letzten Sprung-Position (vor /)`[ und `] sind nützlich, um die Grenzen des letzten Yanks oder Delete wieder anzusteuern — etwa um den eben kopierten Bereich erneut zu markieren mit ` [v`].
Die Jumplist
Vims Jumplist ist eine automatisch gepflegte Liste aller größeren Sprünge. Jeder Befehl, der den Cursor um mehr als ein paar Zeichen bewegt, fügt einen Eintrag hinzu. Damit gibt es eine Browser-ähnliche „Zurück"-Funktion durch die Navigation der aktuellen Session.
| Taste | Aktion |
|---|---|
| Ctrl-o | eine Position zurück in der Jumplist |
| Ctrl-i oder Tab | eine Position vorwärts |
:jumps | alle Einträge der Jumplist anzeigen |
:clearjumps | Jumplist leeren |
Welche Befehle einen Jumplist-Eintrag erzeugen:
- Zeilen-Sprünge:
gg,G,NG,:N - Suche:
/,?,n,N,*,# - Marks:
`,' - Semantische Motions:
(,),{,},[[,]],[m,]m - Tag-Sprünge:
<C-]>,<C-t> - Buffer-Wechsel:
<C-^>,:b
Nicht in der Jumplist (zu klein-skalig):
- Zeichen-Motions:
h,j,k,l,w,b,e - Insert-Mode-Eintritt
" Verschiedene Sprünge im Buffer
gg " Position A (Buffer-Anfang) — in Jumplist
/foo<CR> " Position B (erstes "foo") — in Jumplist
} " Position C (nächster Absatz) — in Jumplist
50G " Position D (Zeile 50) — in Jumplist
<C-o> " zurück nach C
<C-o> " zurück nach B
<C-o> " zurück nach A
<C-i> " wieder vorwärts nach B:jumps zeigt die Liste mit Position und Datei:
:jumps jump line col file/text
4 42 0 src/foo.go
3 158 7 var name = getName()
2 77 0 src/bar.go
1 23 15 } else if (...)
> 0 50 0 src/baz.goDas > markiert die aktuelle Position. Negative Indices waren noch nicht gesprungen, positive sind bereits per Ctrl-o zurückgekehrte Positionen.
Die Jumplist überdauert die Session nicht automatisch — sie ist Per-Window und wird beim Vim-Beenden geleert (es sei denn, viminfo wurde explizit dafür konfiguriert).
Die Changelist
Parallel zur Jumplist führt Vim eine Changelist — eine Liste aller Edit-Positionen.
| Taste | Aktion |
|---|---|
g; | eine Edit-Position zurück in der Changelist |
g, | eine Edit-Position vorwärts |
:changes | alle Edit-Positionen anzeigen |
Der Unterschied zur Jumplist:
- Jumplist speichert Sprung-Ursprünge (wo war ich, bevor ich gesprungen bin?).
- Changelist speichert Edit-Ziele (wo habe ich was geändert?).
g; ist die Antwort auf „wo habe ich gerade nochmal was geändert?". Bei drei verstreuten Edits im selben Buffer führt g; jeweils einen Schritt zurück:
" Edits an drei verschiedenen Stellen:
50G ciw NEU<Esc> " Edit 1 in Zeile 50
100G ciw NEU<Esc> " Edit 2 in Zeile 100
200G ciw NEU<Esc> " Edit 3 in Zeile 200
" Jetzt: wo war der erste Edit?
g; " zurück zu Edit 2 (Zeile 100)
g; " zurück zu Edit 1 (Zeile 50)
g, " vorwärts zu Edit 2Anders als die Jumplist ist die Changelist per Buffer — jeder Buffer hat seine eigene.
Zusammenspiel: vier Navigations-Schichten
Vim hat damit vier komplementäre absolute Navigations-Schichten:
| Schicht | Wie gepflegt | Womit zurück | Reichweite |
|---|---|---|---|
| Zeilen-Sprünge | manuell mit :N, NG | nichts spezifisch | aktueller Buffer |
| Marks | manuell mit m{a-z} | ` + Buchstabe | Buffer oder global |
| Jumplist | automatisch bei Sprüngen | <C-o> / <C-i> | Window (mehrere Buffer) |
| Changelist | automatisch bei Edits | g; / g, | Buffer |
In der Praxis nutzt man alle vier Schichten je nach Kontext:
- Bekannte Zielzeile →
42Goder:42 - Mehrere semantisch wichtige Stellen → globale Marks
mA/mB - „Wo war ich gerade?" →
<C-o>(Sprung-zurück) - „Wo hatte ich gerade was geändert?" →
g;(Edit-zurück)
Wer diese vier Reflexe verinnerlicht hat, navigiert in großen Codebases ohne Maus, ohne Tab-Wechsel und ohne Page Up/Page Down.
Praxis-Workflows
Drei typische Anwendungs-Szenarien:
Workflow 1: Refactoring an drei Stellen
" Drei Stellen sollen koordiniert geändert werden
ma " Mark "a" am ersten Edit-Punkt
" ... navigieren zum zweiten ...
mb " Mark "b"
" ... navigieren zum dritten ...
" Edit hier ausführen
`b " zurück zur zweiten Mark
" Edit
`a " zurück zur ersten
" EditWorkflow 2: Kurz nachschlagen, dann weiter
" Mitten in der Arbeit, kurz Funktion-Signatur prüfen
" (Cursor steht in Zeile 200, prüft Funktion in Zeile 50)
/functionFoo<CR> " zur Funktion springen (Jumplist-Eintrag)
" ... Signatur lesen ...
<C-o> " zurück zur Arbeit (Zeile 200)Schneller als :set number + manuell zur Zeile springen.
Workflow 3: Globale Marks für Projekt-Anker
" In jeder wichtigen Datei einmalig eine globale Mark setzen
:e src/main.go
mM " M = Main
:e src/router.go
mR " R = Router
:e src/config.go
mC " C = Config
" Im Alltag, von beliebiger Stelle aus:
'M " sofort in src/main.go
'R " sofort in src/router.go
'C " sofort in src/config.goGlobale Marks sind in der viminfo persistent — sie überdauern Vim-Restarts und sind nach dem Neuöffnen wieder verfügbar.
Besonderheiten
`` `` `` ist die wichtigste Spezial-Mark
Zwei Backticks springen zur letzten Position vor dem letzten Sprung. Damit hat man eine „Undo"-Variante für jede //G/}-Bewegung — ein einzelner Tastendruck zurück zur vorherigen Position. Sehr häufig der genau richtige Befehl im Alltag.
`` und `` `` `` sind ähnlich, aber nicht identisch
<C-o> geht eine Position zurück in der Jumplist — kann je nach Historie verschiedene Sprünge umfassen. `` springt genau zur Position vor dem letzten Sprung. Bei einer einzigen Sprung-Aktion sind beide identisch; bei mehreren hintereinander divergieren sie.
`H`/`M`/`L` plus `zz` als Reflex
Nach einem H/M/L-Sprung bleibt der Cursor an der Bildschirm-Position — sinnvoll, weil man die Stelle eh sah. Wer den Cursor nach dem Sprung vertikal zentriert haben will, hängt zz an: Hzz, Mzz, Lzz.
Globale Marks (A-Z) überdauern Vim-Restarts
Über viminfo werden globale Marks gespeichert. Nach mF und Vim-Neustart funktioniert 'F weiterhin — Vim öffnet die markierte Datei und springt zur Position. Praktisch für ein paar Hand-voll wichtiger Projekt-Anker.
`:delmarks!` betrifft NUR lokale Marks
Die Variante mit Bang löscht alle a-z-Marks im aktuellen Buffer. Globale Marks (A-Z) bleiben erhalten — dafür :delmarks A B C einzeln aufrufen oder :delmarks A-Z für alle.
Jumplist ist Window-spezifisch, nicht Buffer-spezifisch
Jeder Window hat seine eigene Jumplist. Bei :split erbt das neue Fenster die Liste vom alten, ab dann pflegen beide unabhängig. Die Changelist hingegen ist Buffer-spezifisch — gilt überall, wo der Buffer angezeigt wird.
Weiterführende Ressourcen
Externe Quellen
- Vim Help: jumps — Jumplist im Detail.
- Vim Help: mark — alle Mark-Operationen.
- Vim Help: change-list — Changelist und g;/g,.
- Vim Help: H — Screen-Anker H/M/L.
- Vim Help: viminfo-file-marks — Persistenz globaler Marks.