Aus Linux-Magazin 08/2007

Workshop: Major Modes für (X)Emacs

© photocase.com, anmanie

Die Anpassung an spezielle Dateiformate führt beim Editor-Urgestein Emacs über so genannten Major Modes in der Programmiersprache Elisp. Dieser Workshop demonstriert die Technik am Beispiel eines Mode zum Schreiben von Manuskripten für das Linux-Magazin.

Als kürzlich sage und schreibe die Version 22 des Editors GNU Emacs erschien, kursierten in den Internetforen der News-Seiten die altbekannten Witze mit langem grauen Bart: Es handle sich ja gar nicht um einen bloßen Editor, sondern um ein ganzes Betriebssystem und so fort. Ganz grundlos sind die Seitenhiebe nicht, bringt Emacs 22 doch als eines seiner Highlights einen eingebauten Chat-Client mit.

Die Erweiterbarkeit des Editors gehörte von Anfang an zu seinen Grundtugenden. In der ihm eigenen Programmiersprache Elisp, einer Lisp-Variante, lassen sich vom Syntax-Highlighting bis zum Mail- und Newsreader alle denkbaren Funktionen implementieren.

Dieser Artikel bereichert Ihren Emacs um die Fähigkeit, das auf [1] beschriebene Autorenformat des Linux-Magazins zu verstehen und dafür Syntax-Highlighting und andere Nettigkeiten zur Verfügung zu stellen. Solche Funktionen, die sich mit einem spezifischen Dateiformat beschäftigen, werden durch so genannte Major Modes bereitgestellt, von denen es schon zahlreiche fertige gibt: für diverse Programmier- (C, C++, Perl, Python, Java, Emacs Lisp …) und Markup-Sprachen (HTML, XML, Latex …), aber auch fürs Lesen und Schreiben von Mails oder News und vieles mehr.

Emacs mit oder ohne X

Beim Schreiben von Code in Emacs Lisp müssen Sie daran denken, dass es zwei Varianten des Emacs gibt, deren eingebauter Sprachumfang sich an einigen Stellen unterscheidet: den klassischen Emacs, auch GNU Emacs genannt [2], sowie den Xemacs [3]. Die Trennung hat historische Gründe, wer die Nutzer beider Varianten als Zielgruppe vor Augen hat, muss an einigen Stellen für Kompatibilität sorgen. Was die Ankunft einer weiteren Variante (Sxemacs, [4]) für die Zukunft bedeuten wird, bleibt abzuwarten. Der hier entstehende Code wurde unter Xemacs entwickelt und zum Schreiben dieses Artikels mit GNU Emacs verwendet, ist also kompatibel.

Interaktive Entwicklung

In den meisten Programmiersprachen läuft es so, dass der Entwickler mit einem Editor oder einer IDE das Programm schreibt, danach eventuell einen Compiler hinzuzieht und schließlich das Programm über Mausklick oder in einer Shell startet. Bei der Entwicklung mit Emacs Lisp funktioniert das grundlegend anders: Ist der Editor einmal gestartet, läuft der Interpreter bereits und die neuen Funktionen sind ihm nur noch bekannt zu machen. Das passiert, indem Sie den Cursor ans Ende eines Ausdrucks stellen und die Tastenkombination [Strg] +[X] und dann [Strg]+[E] drücken.

Als Einstieg soll ein ganz einfaches Hello-World-Beispiel dienen, dessen Code Sie im »*scratch*«-Buffer einfügen können, der für solche Arbeiten zur Verfügung steht:

(defun simple-hello-world ()
  "Write "hello world" to the echo area."
  (interactive)
  (message "Hello World"))

Platzieren Sie den Cursor wie in Abbildung 1 hinter die letzte schließende Klammer. Evaluieren Sie diesen Emacs-Lisp-Ausdruck, der die interaktive Funktion »simple-hello-world« erzeugt, durch die erwähnte Tastenkombination. Danach steht die Funktion im Editor zur Verfügung und Sie können sie dank des »(interactive)« mit »M-x simple-hello-world« ausführen. Die folgende Entwicklung des Linmag-Mode verläuft ebenfalls interaktiv.

Abbildung 1: Zum Ausführen von Hello World im »*scratch*«-Buffer stellen Sie den Cursor hinter die Funktion.

Abbildung 1: Zum Ausführen von Hello World im »*scratch*«-Buffer stellen Sie den Cursor hinter die Funktion.

Die wohl wichtigste Funktion bei der Unterstützung eines Dateiformats ist die syntaktische Einfärbung von Schlüsselwörtern und anderen Sprachkonstrukten. Der Mechanismus dazu trägt im Emacs den Namen Font-Lock und wird über Variablen gesteuert, die für den jeweiligen Buffer lokal gelten. Dazu erzeugen Sie zunächst eine Liste jener Schlüsselwörter, die das Dateiformat definiert (Listing 1). Im Fall dieses Beispiels sind dies die beim Linux-Magazin verwendeten Markup-Tags, die mit einem @-Zeichen beginnen.

Listing 1: Variable
definieren

01 (defvar linmag-commands
02   '("@R:"  "@SW:" "@D:"
03     "@T:"  "@V:"  "@A:"
04     "@L:"  "@ZT:" "@LI:"
05     "@B:"  "@Bi:" "@#:"
06     "@IT:" "@IL:" "@IE:"
07     "@KT:" "@KL:" "@KE:"
08     "@TT:" "@TH:" "@TL:"
09     "@TE:")
10   "List of available markup commands for linmag-mode.")

Hilfetexte nicht vergessen

Der Befehl »defvar« legt Variablen an, hier die Variable »linmag-commands«. Sie erhält als Wert eine Liste von Strings mit den Markup-Befehlen. Das einfache Anführungszeichen sorgt dafür, dass Emacs den nachfolgende Ausdruck nicht nach den üblichen Regeln evaluiert (dann müsste das erste Element der geklammerten Liste der Name einer Funktion sein).

Die Variable bekommt zudem noch einen Hilfetext, den so genannten Docstring. Ist dieser Ausdruck einmal evaluiert – mit [Strg]+[X], [Strg]+[E] hinter der letzten schließenden Klammer -, können Sie Wert und Dokumentation der Variablen durch die Tastenkombination [Strg]+[H], dann [V] und die Eingabe »linmag-commands« ansehen.

Zusätzlich erzeugen Sie noch eine Liste der verfügbaren Formatierungsanweisungen, die Emacs später in einer anderen Farbe darstellen soll:

(defvar linmag-formatter
  '("<I>" "<C>" "<B>"
    "<+>" "<->" "<U>")
  "List of formatting instructions for linmag-mode.")

Schließlich erzeugen Sie hieraus einen regulären Ausdruck, der später zum Einfärben des Textes dient (Listing 2).

Listing 2: Regulärer
Ausdruck

01 (defvar linmag-font-lock-keywords
02   (list
03    (cons (regexp-opt linmag-commands)
04          font-lock-keyword-face)
05    (cons (regexp-opt linmag-formatter)
06          font-lock-variable-name-face))
07   "Rules for highlighting in linmag mode.")

Dabei erzeugt der Befehl »regexp-opt« anhand einer Liste von Strings einen regulären Ausdruck (Regexp, Regular Expression). Der Befehl »cons« generiert Pärchen aus der jeweiligen Regexp und einem Face, in der Emacs-Sprache also aus einer Schrift. Listing 2 fasst die beiden Pärchen in einer Liste zusammen und speichert sie in einer (dokumentierten) Variablen.

Schritte zum Major Mode

Für einen echten Major Mode fehlt noch einiges an Framework-Arbeiten. Diese sollten Sie gleich erledigen, um den bisherigen Code zu testen. Dazu gilt es, einige Standardvariablen und Funktionen zu definieren. Zentral ist dabei die Funktion, die den Major Mode startet und den Namen des Mode trägt: »linmag-mode«. Listing 3 zeigt die notwendigen Schritte zum Einrichten eines Major Mode. Zunächst erzeugt es die Variable »linmag-mode-hook« ohne Inhalt (»nil«), aber mit Dokumentation. Die Zeilen 4 bis 6 hängen den Linmag-Mode an die spezielle Emacs-Variable »auto-mode-alist« an, die anhand des Dateinamens einen Major Mode startet. Im Beispiel heißt die Datei-Endung ».linmag«.

Listing 3:
»linmag-mode.el«

01 (defvar linmag-mode-hook nil
02   "Hook to run when entering linmag-mode.")
03 
04 (setq auto-mode-alist
05        (append '(("\.linmag$" . linmag-mode))
06                 auto-mode-alist))
07 
08 (defvar linmag-mode-map ()
09   "Keymap used in linmag-mode.
10 
11 Stripped for the article.")
12 (when (not linmag-mode-map)
13   (setq linmag-mode-map (make-sparse-keymap))
14   (define-key linmag-mode-map
15     [(control c) (control l)]
16     'linmag-insert-listing)
17 
18   (define-key linmag-mode-map
19     [(control c) (control t)]
20     'linmag-insert-text))
21 
22 (defun linmag-mode ()
23   "Major mode for writing articles for the German Linux Magazin.
24 
25 \{linmag-mode-map}"
26   (interactive)
27   ;; Initializing
28   (kill-all-local-variables)
29 
30   ;; Setting up font-locking
31   (make-local-variable 'font-lock-defaults)
32   (setq font-lock-defaults
33         '(linmag-font-lock-keywords nil t nil nil))
34 
35   (use-local-map linmag-mode-map)
36 
37   (setq mode-name "LinMag"
38         major-mode 'linmag-mode)
39 
40   (run-hooks 'linmag-mode-hook)) 

In Zeile 8 beginnt die Definition der Tastenbelegungen für den neuen Mode. Tastenbelegungen erfolgen im Emacs durch Maps, »linmag-mode-map« entsteht zunächst ohne spezielle Belegungen (»make-sparse-keymap«). Danach binden die Zeilen 14 bis 16 und 18 bis 20 zwei Tastenkombinationen an zwei Funktionen. Sie dienen dem raschen Einfügen von Formatierungen für Listings und normalen Fließtext.

In Zeile 22 folgt die Hauptfunktion »linmag-mode« des Mode. Diese Funktion ist interaktiv, damit der Benutzer sie bei Bedarf, wenn die automatische Aktivierung des Mode nicht geklappt hat, manuell ausführen kann. Den Docstring der Funktion zeigt Emacs an, wenn der Benutzer bei aktivem Linmag-Mode die Tastenkombination [Strg]+[H] und dann [M] drückt. Dort erfährt er auch die Tastenbelegung des Mode.

Abbildung 2: Der Linmag-Mode in Aktion beim Schreiben dieses Artikels.

Abbildung 2: Der Linmag-Mode in Aktion beim Schreiben dieses Artikels.

Font-Lock

Zeile 28 in Listing 3 löscht alle eventuell vorhandenen lokalen Variablen, also solche, die nur für den aktuellen Buffer gelten. Dann erzeugt Zeile 31 die wichtige Variable »font-lock-defaults« mit lokalem Geltungsbereich neu und weist ihr die zuvor erzeugte Liste als Wert zu. Dadurch weiß der Font-Lock-Mechanismus, was er zu tun hat. Es gibt noch andere Möglichkeiten, diese Variable aufzubauen, die der ausführlichen Dokumentation aus dem Emacs-Info-System zu entnehmen sind (siehe Kasten “Hilfe”). Danach installiert Zeile 35 die neu erzeugte Keymap lokal, Zeile 37 setzt den Namen des Mode, den Emacs unten in der Mode Line anzeigt.

Hilfe

Emacs enthält ein eingebautes Dokumentationssytem namens Info. Sie starten es mit der Tastenkombination [Strg]+[H] und [I]. Die Abschnitte »Elisp« und »Lispref» beschreiben die Sprache Emacs Lisp für GNU Emacs und Xemacs. Dort finden sich auch Anleitungen zum Selberschreiben von Modes.

Darüber hinaus bietet Emacs Lisp die Möglichkeit, jede Funktion und jede Variable mit einem Hilfetext zu versehen, der sich mit [Strg]+[H] und [V] beziehungsweise [Strg]+[H] und [F] und dem jeweiligen Variablen- oder Funktionsnamen erreichen lässt.

Eine große Hilfe beim Finden von interessanten Variablen und Funktionen ist »apropos«, das die Namen aller Variablen und Funktionen mit einer Regexp durchsucht. Schließlich können Sie auch aus dem umfangreichen Vorrat an bereits geschriebenem Emacs-Lisp-Code schöpfen und sich die ».el«-Files direkt anschauen. Auch Neulinge werden erstaunt sein, wie gut lesbar Lisp ist.

Schließlich führt der Mode noch alle Funktionen aus, die der Benutzer eventuell an den Mode-Aufruf hängt, die so genannten Hooks (Haken). Diese Praxis ermöglicht den Anwendern die Konfiguration eines Mode nach ihren Wünschen. Beispielsweise könnten sie hier eine Funktion anhängen, die weitere Tasten belegt oder auch andere Funktionen aktiviert:

(add-hook 'linmag-mode-hook
          '(lambda ()
            (turn-on-auto-fill)
            (turn-on-font-lock)
            (local-set-key
             '[(control c) (control w)]
             'my-linmag-boxlisting)))

Den Code aus Listing 3 können Sie nun entweder sukzessive im »*scratch«-Buffer evaluieren oder zusammen mit den bisherigen Bestandteilen in einer Datei mit dem Namen »linmag-mode.el« speichern, um dann mit [Meta]+[X] und dem Eintrag »eval-current-buffer« den kompletten Code auszuführen. Ein [Meta]+[X] mit »linmag-mode« schaltet den aktuellen Buffer in den Linmag-Mode um. Das sollte man nur in Buffern tun, die auch wirklich einen passenden Text enthalten, und nicht etwa in dem soeben angelegten »linmag-mode.el«.

Skelette

Bleibt noch die Frage, wie die Funktionen aussehen, die die Formatierungsanweisungen einfügen sollen. Dazu bedient sich der Linmag-Mode des Befehls »define-skeleton«:

(define-skeleton linmag-insert-text
    "Inserts the command for normal text."
  nil "@L: ")

Was anderswo Template heißt, nennt Emacs Skeleton. Der Befehl »define-skeleton« nimmt zuerst den Namen des Skeletons entgegen. Dieser Name benennt dann eine Funktion, die interaktiv aufrufbar ist. Es folgen ein Docstring und eine optionale Abfrage mit einem Prompt für den Benutzer. Der Beispielcode verzichtet darauf und schreibt hier einfach »nil«. Danach folgen beliebig viele Textbausteine (im Beispiel nur einer). Selbst das Ausführen von beliebigem Code ist dort möglich.

Listing 4 zeigt ein etwas aufwändigeres Skeleton, das mit dem Prompt »Name:« den Benutzer nach dem Namen eines einzufügenden Kastens fragt. Nach der Abfrage steht die Benutzereingabe dann in der Variablen »str«. Erwähnenswert an diesem Skeleton ist noch die Verwendung von »_«, um die Cursorposition zu speichern, an der sich nach dem Einfügen der Cursor befindet.

Listing 4: Skeleton

01 (define-skeleton linmag-insert-boxlisting
02     "Inserts markup for a listing in a separate box.
03 Actually this should count the already defined listings and number
04 them automagically."
05   "Name: "
06   "@KT: Listing " _ ": " str
07   "nn"
08   "@LI: n"
09   _
10   "n@KE:n"
11   )

Mehr mit Modes

Ein Major Mode kann noch weitaus mehr enthalten. So fehlen in diesem Mode zum Beispiel Syntaxregeln, die Kommentare und Stringkonstanten erkennen. Strukturierte Sprachen verlangen häufig eine korrekte Einrückung, die berechnet werden muss und vielleicht sogar konfigurierbar sein soll. Mit dem Customize-System steht Ihnen als Programmierer ein Weg offen, den Benutzer Variablen einstellen zu lassen, die das Verhalten eines Mode beeinflussen. Das Imenu-Framework könnten Sie beispielsweise verwenden, um zu Zwischentiteln zu springen.

Das sind nur einige Möglichkeiten von vielen. Mit dem Shellbefehl »locate — -mode.el | xargs ls -lS« finden Sie auf der lokalen Festplatte bei installiertem (X)Emacs eine ganze Menge Modes – nach Größe sortiert, damit Sie sich erst einmal die kleineren anschauen können. Sie enthalten viele Anregungen für eigene Emacs-Programmierungen.

Wichtig ist schließlich, den eigenen Mode auch wirklich zu benutzen, merken Sie so doch am besten, was gut funktioniert oder was Sie noch verbessern sollten. Was fehlt, können Sie im Emacs mit geringem Aufwand ergänzen, und das gehört ganz bestimmt zu den ganz großen Stärken von Emacs.

Abschluss

Die Datei des Emacs-Mode muss abschließend noch die Zeile »(provide \’linmag-mode)« enthalten, damit der Editor den Mode auch unter dem Namen kennt und in dieser Datei findet.

Ein Manuskript für das Linux-Magazin sieht mit dem hier entwickelten Mode so aus wie in Abbildung 2. Schließlich noch eine Lizenz an den Kopf gesetzt und einige spezielle Formatierungen in den Kommentaren am Beginn der Datei, auf die dieser Artikel nicht weiter eingeht – schon erblickt der Mode das Licht der Welt. So geschehen auf der Website des Autors [5]. (ofr)

Infos

[1] Artikel-Format des Linux-Magazins: [http://linux-magazin.de/heft_abo/autor_werden/format]

[2] GNU Emacs: [http://www.gnu.org/software/emacs]

[3] Xemacs: [http://www.xemacs.org]

[4] Sxemacs: [http://www.sxemacs.org]

[5] Linmag-Mode im Netz: [http://www.skamphausen.de/cgi-bin/ska/linmag-mode]

Der Autor

Stefan Kamphausen ist gelernter Physiker und wie viele dieser Zunft auch in der Computerei in vielfältiger Form tätig.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben