Skip to content

M-C-% /la_[0-9] RET /la_\,(+ 2 \#) RET

Was aussieht wie ein Kraftausdruck aus einem Manga, ist der Einstieg in eine Welt der turingvollständigen (ich weiß, ich weiß) Textersetzungen (in Emacs).

Die Aufgabenstellung

Ich wollte in einem Dokument an verschiedenen Stellen einen größeren Textblock einfügen. An jeder Stelle muss der eingefügte Text ein wenig abgeändert werden, d.h. er ist variabel und zwar in einer Zahl, die im Endeffekt für eine Indizierung hochgezählt werden musste.

verschiedene Lösungsmöglichkeiten

Zunächst dachte ich an Texttemplates ala Yasnippet. Da hätte ich dann eine Schabone definiert und das Hochzählen quasi sebst (manuell) erledigt. Allerdings stört mich an allen Texttemplate-Lösungen, die ich mir bisher angeschaut habe immer, dass es relativ umständlich ist, Schablonen zu definieren. Ich suche sowas wie eine ad-hoc-Eingabemögichkeit, habe sie aber noch nicht gefunden. Wahrscheinlich gibt es keine andere Möglichkeit als ein wenig EmacsLisp in *scratch* zusammenzuschreiben.

Auf er Suche nach einer kürzeren Lösung habe ich noch kurz an Abbrev gedacht, bin aber zum Schluß gekommen, dass ich am Ende noch Nacharbeit leisten müsste (nämlich den variablen Teil anpassen). Daher schieden auch die Abkürzungen für mich aus.

Schließlich habe ich die Aufgabe gelöst, indem ich den Textblock händisch an die Stellen kopiert habe, wo er hin sollte. Für den variablen Teil habe ich eine für mich neue -Technik eingesetzt: Suchen-und-Ersetzen mit regulären Ausdrücken (bis hierhin nichts Neues) unter Verwendung von EmacsLisp Ausdrücken (neu!).

Die Eingabe M-C-% /la_[0-9] RET /la_\,(+ 2 \#) RET erledigt nämlich folgendes: Die Vorkommen des RegEx /la_[0-9] werden ersetzt mit /la_\,(+ 2 \#), wobei \, einen EmacsLisp-Ausdruck einläutet.

Genauer: Die Ersetzung findet Zeichenfolgen /la_ mit einer angehängten Ziffer und ersetzt sie mit der Zeichenfolge /la_ und der Anzahl der bisherigen Ersetzungen + 2.

Die Anzahl der bisherigen Ersetzungen startet bei 0. Da ich die Indizierung mit 2 anfangen musste, habe ich einfach zwei hinzuaddiert: (+ 2 \#).

Konkrete Beispiele für Ersetzungen sind daher:

/la_2 -> /la_2
/la_2 -> /la_3
/la_2 -> /la_4
/la_2 -> /la_5

Fertig.

Mit EmacsLisp-Ausdrücken im Ersetzungsteil kann man aber noch mehr machen. Neben \# (Ersetzungszähler) gibt es weitere Kürzel zum Zugriff auf den zu ersetzenden Text oder Teile davon: \1 liefert die erste Capturing Group als Text, \2 die zweite, etc. \#1 liefert die erste Gruppe als Zahlenwert, \#2 die zweite. \& liefert den gesamten String. Alle Kürzel sind im Emacs-Handbuch unter 19.9.2 Regexp Replacement beschrieben.

Ein weiteres Beispiel mit neuer Aufgabenstellung

Angenommen, man müsste die oben durchgeführte Indizierung korrigieren, weil der Offset sich geändert hat. D.h. im obigen Beispiel hätten eigentlich folgende Ersetzungen durchgeführt werden müssen:

/la_2 -> /la_4
/la_2 -> /la_5
/la_2 -> /la_6
/la_2 -> /la_7

Man könnte jetzt – das Dokument auf einen früheren Stand (überall /la_2) zurücksetzen und – die oben verwendete Ersetzungsvorschrift M-C-% /la_[0-9] RET /la_\,(+ 2 \#) RET in M-C-% /la_[0-9] RET /la_\,(+ 4 \#) RET abändern.
Diese Ersetzung ist durch das \# aber Reihenfolgeabhängig – und man müsste an den frühern Stand kommen (ich kenne und kann da zwei Möglichkeiten ans Herz legen: Versionierungssystem, incomprehensible Undo). Es geht unter Verwendung des oben bereits beschriebenen \#1 und einer Capturing Group auch anders:

M-C-% /la_\([0-9]\) RET /la_\,(+ 2 \#1) RET

Zunächst mag man sich vielleicht wundern, warum so viele Escapes im Suchausdruck /la_\([0-9]\) vorkommen – gewohnterweise würde man /la_([0-9]) schreiben. Nun – Emacs ist primär ein Betriebssystem Texteditor und es wird davon ausgegangen, dass häufiger nach Text gesucht wird, der Zeichen wie ( oder ) enthält, als dass reguläre Ausdrücke z.B. Capturing Groups (eingefasst mit ( und )) in Suchanfragen verwendet werden. Daher hat das textliche Zeichen erstmal Vorrang – die Metazeichen des RegEx müssen mit Escapes kenntlich gemacht werden.

Auf den Inhalt der Capturing Group kann in der Ersetzung mit \1, etc. zugegriffen werden. Das liefert einen String. Wir brauch aber eine Zahl – und die liefert \#1. Und mit dieser Zahl kann man dann – wie im Beispiel – rechnen.

Statt ’nur‘ zu rechnen, können im EmacsLisp-Ausdruck auch komplexere Funktionen verwendet werden. Natürlich auch selbst definierte. Man könnte Ersetzungen abhängig von anderen Dateien – auf anderen Rechnern machen, Internetabfragen starten, usw. usv..

Thermonuklear!

Post a Comment

Your email is never published nor shared. Required fields are marked *