Einrückung in Emacs ist nicht „X Spaces tippen", sondern eine Funktion. Die TAB-Taste hat in den meisten Modes eine eigene Bedeutung — sie indentiert kontextabhängig, je nach Major-Mode. Dazu kommen electric-indent, aggressive-indent und ein paar Hilfen rund um Trailing-Whitespace, Tabs vs. Spaces und Spalten-Ausrichtung, die saubere Quellcode-Formatierung in Emacs praktisch ohne Mühe machen. Dieser Artikel zeigt das Gesamtbild — vom „Was tut TAB wirklich?" bis zum produktiven Save-Hook-Setup.
TAB als kontextabhängiger Befehl
Der vielleicht überraschendste Punkt für Umsteiger: In Code-Modes ist TAB nicht an „Tab-Zeichen einfügen" gebunden, sondern an indent-for-tab-command. Emacs interpretiert die Taste, je nachdem, wo Point steht, sehr unterschiedlich:
- Point am Zeilenanfang oder im führenden Whitespace — die Zeile wird auf die kontextuell korrekte Einrückung gebracht. Was „korrekt" heißt, definiert der Major-Mode (z. B. in
python-modeüber Blocktiefe, inc-modeüber das aktuellec-style). - Point mitten im Code (auf einem Symbol) — viele moderne Modes mappen TAB hier auf
completion-at-point, also den Standard-Vervollständigungs-Mechanismus von Emacs. - Mit aktiver Region — die ganze Region wird zeilenweise re-indentiert (analog zu
indent-region, siehe nächste Sektion).
Ein echtes Tab-Zeichen einfügen
Wer wirklich ein literales TAB-Zeichen in den Buffer schreiben muss — etwa in einem Makefile oder einer TSV-Datei — nutzt quoted-insert:
- C-q TAB fügt das nächste Zeichen wörtlich ein, ohne dass Emacs es als Befehl interpretiert.
- M-x quoted-insert RET TAB ist der vollständige Befehlsname.
Was dahintersteht
Die Funktion indent-for-tab-command ruft intern den Mode-spezifischen Indent-Funktion auf, hinterlegt in der Buffer-lokalen Variable indent-line-function. In fundamental-mode zeigt diese auf indent-relative (orientiert sich an der vorherigen Zeile); in python-mode auf python-indent-line-function; in emacs-lisp-mode auf lisp-indent-line — usw. Folge: Du musst in Emacs selten selbst Spalten zählen — TAB macht es. Das ist die Logik, die zu Recht „smart indent" heißt und die in fast jedem Programmier-Workflow zentral ist.
indent-region: ganze Region einrücken
Was TAB für eine Zeile macht, macht indent-region für viele Zeilen auf einmal. Beide Wege:
- C-M-\ — direkt an
indent-regiongebunden. - M-x indent-region RET — der Befehlsname für den Minibuffer-Weg.
Wirkung: Jede Zeile der aktiven Region wird so behandelt, als hättest du TAB auf ihr ausgelöst. Klassischer Use-Case: Code aus Browser, Slack oder einer PDF kopieren — die Einrückung ist meist Müll. In Emacs einfügen, Region mit C-x h (mark-whole-buffer) oder selektiv setzen, dann C-M-\ — die Datei sieht sofort aus, wie sie soll.
Die Backslash-Falle auf deutschen Tastaturen
Auf DE-Layout sitzt \ auf AltGr+ß, also auf einem Modifier, der intern auf C-M- mappt. Der Default-Shortcut C-M-\ bedeutet damit auf DE quasi „C-M-(C-M-ß)" — was zwar funktioniert, aber fingerakrobatisch wirkt. Wer den Befehl täglich braucht, bindet ihn auf eine bequemere Stelle im reservierten C-c <buchstabe>-Raum:
;; indent-region auf C-c i legen (DE-tauglicher)
(global-set-key (kbd "C-c i") #'indent-region)Wo dieser Code hingehört
Das Snippet kommt in deine init.el. Wenn du noch keine hast oder den Lade-Mechanismus genauer verstehen willst, siehe den Artikel zur Konfiguration mit init.el und early-init.el.
electric-indent-mode: automatische Neueinrückung
electric-indent-mode ist seit Emacs 24.4 built-in und seit Emacs 25 als globaler Modus per Default aktiv (electric-indent-mode 1). Effekt: Wenn du RET drückst, wird die neue Zeile automatisch eingerückt — und in vielen Modes auch die gerade verlassene Zeile ggf. korrigiert. Bei manchen Zeichen (z. B. } in C-artigen Sprachen) feuert der Re-Indent ebenfalls.
Welche Zeichen den Re-Indent triggern
Die Variable electric-indent-chars listet die Trigger-Zeichen. Default ist '(?\n) — also nur das Newline-Zeichen. Major-Modes erweitern die Liste pro Buffer (c-mode fügt z. B. ?}, ?; hinzu).
Abschalten — pro Mode oder global
Manche Modes vertragen electric-indent schlecht. In org-mode etwa wird automatisches Re-Indent oft als störend empfunden; in markdown-mode ähnlich. org-mode deaktiviert ihn von sich aus pro Buffer. Wer den Modus global ausschalten möchte, schreibt:
;; Komplett aus (sehr selten sinnvoll)
(electric-indent-mode -1)
;; Oder nur in bestimmten Modes (häufiger Use-Case)
(add-hook 'markdown-mode-hook
(lambda () (electric-indent-local-mode -1)))Wo dieser Code hingehört
Wie alle Hook- und Mode-Konfigurationen gehört das in deine init.el (siehe init.el und early-init.el). electric-indent-local-mode ist die buffer-lokale Variante — sie ist die richtige Wahl in Hooks, weil sie nur den betroffenen Buffer beeinflusst, nicht alle.
aggressive-indent-mode: noch eine Stufe schärfer
aggressive-indent-mode ist ein externes Paket (Malabarba) und geht über electric-indent hinaus: Nach jeder Änderung wird die aktuelle Zeile — und in Lisp-Modes das umgebende S-Expression — sofort neu eingerückt. Das wirkt magisch in Klammer-Sprachen, weil dort jede Klammer-Verschiebung sofort sauber dargestellt wird.
(use-package aggressive-indent
:ensure t
:hook ((emacs-lisp-mode . aggressive-indent-mode)
(lisp-mode . aggressive-indent-mode)
(clojure-mode . aggressive-indent-mode)
(scheme-mode . aggressive-indent-mode)))Wo dieser Code hingehört
Auch dieses Snippet kommt in init.el — es setzt use-package voraus, das in Emacs 29+ built-in ist. Detail-Setup zur Paketverwaltung im Artikel zur Konfiguration mit init.el und early-init.el.
Bewusst nicht überall
Für JS, TS, Python ist aggressive-indent meist hinderlich, weil es feuert, während du noch tippst, und die Zeile dabei mehrmals umbricht. In diesen Modes ist die Kombination aus electric-indent plus manuellem C-M-\ oder einer LSP/Format-on-Save-Pipeline (siehe Sektion 08) deutlich angenehmer. Die Faustregel: aggressive-indent in Klammer-Sprachen, electric-indent + Formatter in Whitespace-Sprachen.
Tabs vs. Spaces: tabify und untabify
Zwei Konfigurations-Variablen entscheiden, wie Emacs Einrückung intern speichert und visuell darstellt:
| Variable | Default | Bedeutung |
|---|---|---|
indent-tabs-mode | t | bei Einrückung werden TAB-Zeichen verwendet, wo es geht; mit nil nur Spaces |
tab-width | 8 | wie viele Spalten ein TAB-Zeichen visuell breit dargestellt wird |
indent-tabs-mode ist buffer-lokal — Major-Modes überschreiben den Default oft. python-mode setzt ihn z. B. von sich aus auf nil (Spaces erzwungen, PEP 8), während makefile-mode und go-mode ihn auf t lassen (Tabs sind dort Pflicht).
Konvertieren zwischen Tabs und Spaces
Beide Befehle arbeiten auf der aktiven Region — ohne Region tun sie nichts:
- M-x tabify RET — wandelt Folgen von Spaces, die ein Vielfaches von
tab-widthergeben, in TAB-Zeichen um. - M-x untabify RET — wandelt alle TAB-Zeichen in entsprechend viele Spaces um.
untabify auf den gesamten Buffer (Region mit C-x h aufspannen, dann M-x untabify) ist die Default-Empfehlung für moderne Projekte, in denen „Spaces only" Konvention ist.
Empfehlung für moderne Projekte
(setq-default indent-tabs-mode nil
tab-width 4)Pro-Mode lässt sich das überschreiben, falls nötig:
(add-hook 'makefile-mode-hook
(lambda () (setq indent-tabs-mode t)))Wo dieser Code hingehört
Beide Snippets gehören in init.el. setq-default ist hier wichtig, weil indent-tabs-mode buffer-lokal ist — setq allein würde nur den Wert im aktuell aktiven Buffer ändern. Mehr zur Logik von Buffer-lokalen Variablen im Konfigurations-Kapitel.
Trailing-Whitespace automatisch entfernen
„Trailing Whitespace" ist Whitespace am Zeilenende, das niemand will und das in Git-Diffs für Lärm sorgt. Emacs hat dafür drei Bausteine:
Manuell aufräumen
- M-x delete-trailing-whitespace RET — entfernt Trailing-Whitespace im gesamten Buffer; mit aktiver Region nur dort.
Automatisch beim Speichern
Der populärste Ein-Zeiler in Emacs-Setups überhaupt:
(add-hook 'before-save-hook #'delete-trailing-whitespace)Vor jedem Speichern wird der Buffer einmal durchgekämmt. Für reine Code-Workflows ist das pures Glück; in gemischten Setups (z. B. mit Markdown, wo Trailing-Whitespace <br>-Semantik hat) ist eine selektivere Variante besser:
;; Nur in Modes, die von prog-mode abgeleitet sind
(add-hook 'prog-mode-hook
(lambda ()
(add-hook 'before-save-hook
#'delete-trailing-whitespace nil t)))Das t als drittes Argument macht den Hook buffer-lokal — er feuert wirklich nur in prog-mode-Buffern.
Sichtbar machen
Wer Trailing-Whitespace zuerst sehen will, bevor er ihn löscht, aktiviert die visuelle Markierung:
(setq-default show-trailing-whitespace t)Resultat: Spaces/Tabs am Zeilenende werden im Buffer rot hinterlegt (Face trailing-whitespace). Praktisch wäre auch whitespace-mode als kompletter Visualizer für alle Whitespace-Arten — Detail im Verweis am Kapitelende.
Wo dieser Code hingehört
Alle drei Snippets sind klassische init.el-Einträge — siehe init.el und early-init.el.
align-regexp: Spalten ausrichten
Eines der meistunterschätzten Bordmittel — align-regexp richtet einen Bereich an einem regulären Ausdruck aus. Beide Wege:
- M-x align-regexp RET — Region markieren, dann den Befehl rufen; Prompt fragt nach der Regex.
- C-u M-x align-regexp RET — mit Prefix-Argument; bietet Detail-Optionen (Gruppen-Index, Whitespace-Steuerung, Wiederholung).
Beispiel: Variable-Deklarationen auf = ausrichten
Region markieren, M-x align-regexp RET, als Regex = eingeben:
vorher: nachher (align-regexp auf =):
var foo = 1; var foo = 1;
var hello = 2; var hello = 2;
var x = 3; var x = 3;Mit Prefix-Argument lassen sich auch komplexere Tabellen ausrichten — etwa Doku-Listen mit mehreren Spalten oder Lua-Tables mit Komma-getrennten Werten. Drei Klicks für eine optisch saubere Spalten-Liste, ohne ein einziges Leerzeichen selbst zu tippen.
Externe Formatter und prettify-symbols-mode
Was Emacs selbst nicht macht — sprachspezifische Formatter wie Prettier, Black, gofmt, rustfmt — lässt sich nahtlos einbinden:
- Manuell pro Buffer: M-x shell-command-on-region mit der jeweiligen Formatter-CLI als Befehl.
- Automatisch beim Speichern: über
before-save-hookund die jeweilige Mode-Integration. Saubere Lösung sind Pakete wieapheleia(asynchron, kein Cursor-Sprung) oderformat-all(Single-Setup für viele Formatter). - Über LSP: Wer ohnehin Eglot oder
lsp-modenutzt, bekommt Format-on-Save vom Sprachserver — Prettier/Black laufen dann gar nicht direkt aus Emacs, sondern über das LSP-Protokoll. Detail im verlinkten Artikel.
prettify-symbols-mode: kosmetische Symbol-Anzeige
Built-in und buffer-lokal: Ersetzt definierte Symbole nur zur Anzeige durch andere Glyphen — der Buffer-Inhalt bleibt unverändert. Klassisches Beispiel: lambda in Elisp wird als λ angezeigt.
(add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)Pro Mode lässt sich prettify-symbols-alist mit eigenen Mappings füttern (("->" . ?→) etc.). Hübsch, aber rein visuell — der Datei-Inhalt bleibt ASCII.
Häufige Stolperfallen
`TAB` ist kein Tab-Zeichen
In Code-Modes ist TAB an indent-for-tab-command gebunden — also „indentiere kontextabhängig", nicht „füge ein Tab-Zeichen ein". Wer wirklich ein literales TAB braucht (Makefile, TSV), nutzt C-q TAB (quoted-insert). Diese Verwechslung kostet Neulinge regelmäßig Stunden.
`indent-tabs-mode` ist standardmäßig `t`
Default-Emacs erzeugt bei Einrückung TAB-Zeichen — die in anderen Tools (Editor, Terminal, Browser) unterschiedlich breit dargestellt werden und so Diff-Lärm produzieren. Moderne Projekte setzen (setq-default indent-tabs-mode nil) und sind damit auf der sicheren Seite. Achtung: setq statt setq-default setzt nur den aktuellen Buffer-Wert.
`electric-indent-mode` kann Modes brechen
In org-mode, markdown-mode und manchen Plaintext-Workflows ist automatisches Re-Indent beim Drücken von RET störend — Listen, Tabellen oder Zitate werden „zurechtgerückt", obwohl sie korrekt waren. Lösung: pro Mode mit electric-indent-local-mode -1 deaktivieren statt global abschalten.
`aggressive-indent` ist nicht für jede Sprache
In Klammer-Sprachen (Elisp, Common Lisp, Clojure, Scheme) ist aggressive-indent-mode herrlich. In JS, TS und Python feuert er zu früh und schiebt die noch unvollständige Zeile herum — Tippgefühl wird zäh. Selektiv per Hook aktivieren, nicht global.
`delete-trailing-whitespace` als Save-Hook erzeugt Riesendiffs in Altprojekten
Wenn du den Hook in einem bestehenden Repo aktivierst, bei dem niemand zuvor aufgeräumt hat, produziert dein erster Commit pro berührter Datei eine Wand aus Whitespace-Änderungen — und die Code-Review wird unlesbar. Entweder einmalig „big sweep" als eigener Commit oder den Hook nur in prog-mode-Buffern aktivieren.
`C-M-\` ist auf DE-Tastatur fingerakrobatisch
Der Default-Shortcut für indent-region liegt auf C-M-\. Auf DE-Layout sitzt \ auf AltGr+ß, AltGr ist intern oft C-M- — der Default ist auf DE praktisch eine Modifier-Kollision. Eigene Binding in C-c <buchstabe> lohnt sich, z. B. C-c i.
`tabify` und `untabify` ohne Region machen nichts
Beide Befehle arbeiten ausschließlich auf der aktiven Region. Wer sie ohne Region aufruft, erlebt: Nichts passiert. Vorher mit C-x h (mark-whole-buffer) den gesamten Buffer markieren oder die gewünschte Region setzen — dann wirken sie.
`align-regexp` wird konstant unterschätzt
Drei Klicks für visuell saubere Variable-Listen, JSON-Properties, Tabellen — und doch greifen die meisten zur Maus oder tippen Leerzeichen von Hand. Einmal in die Finger bekommen, ersetzt der Befehl alles, was sonst manueller Spalten-Frickelei wäre.
Weiterführende Ressourcen
Externe Quellen
- GNU Emacs Manual — Indentation — offizielle Übersicht zu
indent-for-tab-command,indent-tabs-modeundtab-width. - GNU Emacs Manual — Indentation Commands — alle Built-in-Indent-Befehle (
indent-region,indent-rigidly,indent-relative). - GNU Emacs Manual — Tabs —
tabify,untabify,tab-stop-list. - GNU Emacs Manual — Useless Whitespace —
delete-trailing-whitespace,show-trailing-whitespace,whitespace-mode. - aggressive-indent-mode — externes Paket für sofortiges Re-Indent in Lisp-Modes.
- apheleia — asynchroner Format-on-Save-Runner für Prettier, Black, gofmt, rustfmt & Co.