Emacs-Workshop
Table of Contents
1 2010-04-19 – Extensions nutzen
1.1 Emacs Lisp – Nachtrag
1.1.1 Lokale Variablen
Lokale Variablen kann man mit let erzeugen. Sie verdecken globale
Variablen gleichen Namens.
(setq foo 'bar) (let (foo) (setq foo 'quux) (message "local foo is %s" foo)) ;; gibt "local foo is quux" aus
Lokale Variablen koennen mit let initialisiert werden:
(let ((foo 'quux) bar) (message "foo is %s" foo)) ;; gibt "foo is quux" aus
Die Gueltigkeit von lokalen Variablen wird zur Laufzeit bestimmt
(“dynamic scope”), dies kann zu unvorhergesehenen Effekten fuehren und
erlaubt leider keine “closures”. In diesem Beispiel wurde das let
zum Zeitpunkt des Funktionsaufrufes verlassen und wirkt daher nicht
mehr:
(setq foo 'bar) (fset 'my-function (let ((foo 'quux)) (lambda () (message "foo ist %s" foo)))) (my-function) ; => "foo ist bar"
Hier ist das let Teil des Funktionskoerpers, so funktioniert’s:
(fset 'my-other-function (lambda () (let ((foo 'quux)) (message "foo ist %s" foo)))) (my-other-function) ; => "foo ist quux"
Das Makro lexical-let aus dem Paket “cl” simuliert einen “lexical
scope” wie man ihn aus Common Lisp, Scheme oder JavaScript kennt:
(fset 'my-lexical-function (lexical-let ((foo 'quux)) (lambda () (message "foo ist %s" foo)))) (my-lexical-function) ; => "foo ist quux"
Dynamischer Scope hat den Vorteil, dass man globale Variablen leicht
voruebergehend auf einen neuen Wert setzen kann, ohne dass man sich
den alten Wert merken und wieder zuruecksetzen muss. Dies wird in
Emacs an vielen Stellen genutzt und ist ein uebliches Idiom.
Als praktisches Beispiel hier eine bessere Version von
fix-c-base-spelling, die beim suchen und ersetzen Gross- und
Kleinschreibung beachtet. Entscheidend ist hier die Variable
case-fold-search, die hier voruebergehend auf nil gesetzt wird:
(defun fix-c-base-spelling () (save-excursion (goto-char (point-min)) (let ((case-fold-search nil)) (while (re-search-forward "C-Base" nil t) (replace-match "c-base" t)))))
1.1.2 buffer-local Scope
case-fold-search ist ausserdem ein Beispiel fuer eine Variable die
“buffer-local” ist. Eine solche Variable hat einen globalen
“default”-Wert der in einem Buffer ueberschrieben werden kann. Diesen
“default” kann man mit setq-default setzen:
(setq-default case-fold-search nil)
Ein einfaches setq setzt die Variable nur fuer den aktuellen Buffer.
1.1.3 Schleifen
Fuer Iteration bietet Emacs Lisp einige Konstrukte. Die einfachste ist while:
(setq i 0) (while (< i 10) (insert "I will read M-x info before asking stupid questions.\n") (setq i (1+ i)))
Fuer einen einfachen Fall wie diesen ist dotimes praegnanter:
(dotimes (i 10) (insert "I will read M-x info before asking stupid questions.\n"))
Um ueber eine Liste zu iterieren benutzt man mapcar:
(mapcar '1+ '(1 2 3)) ;; => (2 3 4) (mapcar (lambda (i) (* i 2)) '(1 2 3)) ;; => (2 4 6) (apply 'string (mapcar '1+ "abc")) ;; => "bcd"
mapc ist sehr aehnlich, gibt aber als Rueckgabewert die Liste
unveraendert zurueck. Man verwendet mapc um deutlich zu machen, dass
man nur an den Seiteneffekten, in diesem Fall der Textausgabe,
interessiert ist.
(mapc (lambda (i) (insert (number-to-string (* i 2)))) '(1 2 3)) ;; => (1 2 3)
1.1.4 Bedingte Verzweigung
“if” wurde von Lisp erfunden. So sieht es in der Praxis aus:
(if (eq major-mode 'org-mode) (message "yay!") (message "nay."))
Es gibt keine syntaktischen Schluesselwoerter, die anzeigen muessten,
wo die Bedingung oder der THEN-Zweig enden, es ergibt sich aus der
Listenstruktur. Hier ein Beispiel aus der Praxis:
(add-hook 'ruby-mode-hook (lambda () (if (fboundp 'subword-mode) (subword-mode 1) (c-subword-mode 1))))
Ein “if” hat auch immer einen Rueckgabewert und ist so dem ternaeren
Operator “?” in C und verwandten Sprachen vergleichbar:
(setq browse-url-browser-function
(if window-system 'browse-url-mozilla 'w3m-browse-url))
if ist eine special form, wenn wir versuchen es als Funktion zu
schreiben, sehen wir, dass die Argumente evaluiert werden, bevor die
Funktion laeuft:
(defun my-if (cond then else) (if cond then else)) (my-if t (insert "foo") (insert "bar")) ;; fuegt "foobar" in den aktuellen Buffer ein
Der ELSE-Zweig kann mehrere Befehle (“forms”) hintereinander enthalten
(“implicit progn”):
(if (eq major-mode 'lisp-mode) (message "yay!") (setq my-message "What a pity! I love lisp!") (message my-message))
Fuer den THEN-Zweig braeuchte man hier ein progn:
(if (eq major-mode 'lisp-mode) (progn (set my-message "Happy happy, joy joy!") (message my-message)) (setq my-message "What a pity! I love lisp!") (message my-message))
Wenn es nur den THEN-Zweig oder den ELSE-Zweig gibt, kann man
stattdessen die Funktionen “when” und “unless” verwenden:
(when window-system
(color-theme-charcoal-black))
1.1.5 Praktisch: Zeitstempel einfuegen
(global-set-key (kbd "C-c m") (lambda () (interactive) (insert (format "%s <%s>" (format-time-string "%Y-%m-%d") user-mail-address))))
1.2 Was ist eine Extension?
Eine Emacs Extension fuegt neue Funktionalitaet hinzu oder
veraendert bestehende. Der Begriff Library wird ebenso benutzt und
bedeutet dasselbe. Beispiele:
- erlang-mode
- AucTeX
- magit
- w3m, jabber-el
- nav, tabbar
Viele Extensions sind bereits Teil von Emacs, und es werden immer
neue aufgenommen, z.B. CEDET in Emacs 23.2, der bald (TM) erscheinen
wird. Dieser Prozess ist nicht so einfach, da dafuer – aus politischen
Gruenden – das Copyright der FSF ueberschrieben werden muss.
1.3 Extensions installieren
1.3.1 Extension mit einer Datei am Beispiel “Visible Bookmarks”
- http://www.emacswiki.org/emacs/VisibleBookmarks
- http://www.nongnu.org/bm/
- http://mirrors.zerg.biz/nongnu/bm/bm-1.43.el
Erst mal sollten wir einen Blick auf die Datei werfen, die wir
heruntergeladen haben. Man beachte den standardisierten Dateikopf und
die Dokumentation darin. Fuer kleinere Pakete ist das auch die
Regel. Auch interessant ist das Ende der Datei.
Zum testen tut es ein
M-x load-file
Genauso gut ist
M-x eval-buffer
wenn man die Datei schon offen hat.
- Laden einer Extension mit load und require
Um diese Funktion permanent zur Verfuegung zu haben, muss sie in
“bm.el” umbenannt und in ein bekanntes Verzeichnis verschoben
werden. Der Name ist Geschmackssache: “.elisp”, “.emacs-lisp”,
“.emacs-lisp.d” oder “.local-lisp” sind eine gute Wahl. Das
Verzeichnis “.emacs.d” wird von verschiedenen Emacs-Paketen benutzt,
abgesehen davon, dass es unuebersichtlich werden kann, spricht auch
nichts dagegen, sein Elisp dort abzulegen.(add-to-list 'load-path "~/.elisp") (load "bm")
Das funktioniert, hat aber den Nachteil, dass “bm.el” zwei mal geladen
wird, wenn dieser Code noch einmal evaluiert wird. Besser ist es
daher, die Funktion require zu benutzen, wie es der Kommentar auch
empfiehlt:(require 'bm)
require funktioniert nur, wenn die Datei mit provide endet. Das
Symbol bm wird dadurch in die Liste features aufgenommen.features ;; => (tooltip ediff-hook vc-hooks lisp-float-type mwheel...) (featurep 'bm) ;; => t, wenn bm geladen wurde
- Grundkonfiguration
Tastensequenzen der Form “C-c <Buchstabe>” und die Funktionstasten
F5-F9 sind fuer den Benutzer reserviert. Natuerlich kann man sich auch
dafuer entscheiden, bereits vorhandene Tastenbindungen zu
ueberschreiben, wie es “bm.el” auch vorschlaegt:(global-set-key (kbd "<C-f2>") 'bm-toggle) (global-set-key (kbd "<f2>") 'bm-next) (global-set-key (kbd "<S-f2>") 'bm-previous)
Die kbd-Funktion hilft uns dabei, die Tastenbindungen leserlich und
portabel zu definieren. - Alternative zum Laden: autoload
Wenn man viele Extensions in seiner .emacs laedt, verlaengert sich der
Startprozess und der Speicherverbrauch. Dabei ist es unter Umstaenden
gar nicht so entscheidend, ob all die Funktionalitaet schon von Anfang
geladen ist, sie muss nur zum richtigen Zeitpunkt verfuegbar
sein. Dabei hilft uns die Funktion autoload:(autoload FUNCTION FILE &optional DOCSTRING INTERACTIVE TYPE)
In unserem Fall saehe dies dann so aus:
(autoload 'bm-toggle "bm" "Toggle bookmark in current buffer." t)
Ein weiterer Vorteil: wenn wir unsere Konfiguration mitnehmen und
“bm.el” noch nicht installiert haben, bekommen wir beim Starten keinen
Fehler. - Bytecompile
Elisp-Code laedt und laeuft schneller, wenn er compiliert ist. Mit
M-x byte-compile-file
erzeugt man eine
.elc
-Datei, die dann von require und load
bevorzugt geladen wird. - Bedingte Konfiguration
Wenn die Extension staendig aktiv sein soll (z.B. persistente
Bookmarks bei “bm”), hilft uns autoloading nicht. Wenn wir trotzdem
eine universell einsetzbare .emacs haben wollen, koennen wir uns einen
Parameter von require zunutze machen und ein wenig Logik schreiben:(if (require 'bm nil t) (progn (global-set-key (kbd "<C-f2>") 'bm-toggle) (global-set-key (kbd "<f2>") 'bm-next) (global-set-key (kbd "<S-f2>") 'bm-previous)))
Das progn ist noetig, um die Anweisungen fuer den THEN-Zweig zu
gruppieren, sonst wuerde die zweite und alle folgenden “Forms” dem
ELSE-Zweig zugeordnet. when hat keine ELSE-Zweig und ermoeglicht uns
eine etwas elegantere Schreibweise:(when (require 'bm nil t) (global-set-key (kbd "<C-f2>") 'bm-toggle) (global-set-key (kbd "<f2>") 'bm-next) (global-set-key (kbd "<S-f2>") 'bm-previous))
- Verzoegerte Konfiguration
Manche Einstellungen koennen erst vorgenommen werden, wenn ein Paket
geladen ist: ein Hook ist vorher nicht definiert oder eine Liste, zu
der wir ein Element hinzufuegen wollen, existiert noch nicht. Dies
tritt natuerlich vor allem bei “autoloading” auf, wie es auch Emacs
selbst benutzt, z.B. fuer die grep-Extension. Wenn ich z.B. nicht
moechte, dassM-x rgrep
Verzeichnisse ignoriert, dielog
heissen,
kann ich das so konfigurieren:(eval-after-load "grep" '(add-to-list 'grep-find-ignored-directories "log"))
Wichtig: ich uebergebe eine zitierte Liste, der Code wird erst spaeter
(mit eval) ausgefuehrt. Wenn ich mehrere Kommandos ausfuehren will,
brauche ich wieder ein progn:(eval-after-load "grep" '(progn (add-to-list 'grep-find-ignored-directories "log") (add-to-list 'grep-files-aliases ("r" . "*[.e]r[bh]*"))))
Manchmal erledigt sich das Problem, wenn die Konfiguration waechst,
manchmal nicht:(eval-after-load "grep" '(progn (mapc (lambda (i) (add-to-list 'grep-find-ignored-directories i)) '("log" "downloads" "demo_content")) (mapc (lambda (e) (add-to-list 'grep-files-aliases e)) '(("js" . "*.js") ("css" . "*.css*") ("r" . "*[.e]r[bh]*")))))
- Befehle zum Umgang mit Libraries
locate-library gibt den Pfad zu einer Library aus:
M-x locate-library
find-library oeffnet die Datei mit dem Quellcode:
M-x find-library
list-load-path-shadows zeigt, welche Libraries mehrfach vorhanden
sind:M-x list-load-path-shadows
1.3.2 Extension mit vielen Dateien: Emacs-Jabber
Der Jabber-Client kann hier heruntergeladen werden: http://emacs-jabber.sourceforge.net/
Er wird standardmaessig mittels autoconf installiert, z.B.
./configure --prefix=/home/anselm/.local
Es ist in der Regel aber auch moeglich, ein groesseres Paket von Hand
zu installieren, in diesem Fall sind hierzu folgende Schritte notwendig:
- die Elisp-Dateien in einen Ordner kopieren
- diesen Ordner zum load-path hinzufuegen
- die Dateien “byte-compilen”
-
optional die Dokumentation mit
makeinfo
aus Texinfo-Quellen
uebersetzen, in das info-Verzeichnis kopieren, komprimieren und mit
install-info
in die Datei “dir” eintragen
1.3.3 Hilfe (?) bei der Konfiguration: customize
Das customize-System ist der Versuch, ein benutzerfreundliches
Konfigurationsinterface fuer Emacs zu schaffen. Ob man es mag oder
nicht, um sich einen Ueberblick ueber die vorhandenen
Konfigurationsmoeglichkeiten zu verschaffen, reicht es allemal. Die
volle Flexibilitaet erreicht man aber nur mit Elisp. Hier die
wichtigsten Befehle:
M-x customize M-x customize-group M-x customize-variable M-x customize-saved
Customize speichert seine Daten direkt in der .emacs, kann aber auch
dazu angewiesen werden, sie in einer anderen Datei abzulegen. In
diesem Fall sind wir selbst dafuer verwantwortlich, diese auch zu
laden:
(setq custom-file "~/.emacs-custom.el") (load (expand-file-name "~/.emacs-custom.el") t)
1.3.4 Hilfe bei der Installation: auto-install und ELPA
- auto-install
http://www.emacswiki.org/emacs/AutoInstall
http://www.emacswiki.org/emacs/download/auto-install.el(add-to-list 'load-path "~/.emacs.d/auto-install") (mapc (lambda (source) (autoload (intern (format "auto-install-from-%s" source)) "auto-install" nil t)) '(buffer url emacswiki gist library directory dired))
Oder einfach
(require 'auto-install)
.Installiert doch mal
anything
aus dem EmacsWiki:M-x auto-install-from-emacswiki
- ELPA
ELPA ist ein Paketmanager fuer Emacs.
In der naechsten Version von Emacs ist er moeglicherweise
enthalten. XEmacs hat schon lange einen eigenen Paketmanager. - Distribution
Linux-Distributionen bieten natuerlich auch Pakete von
Emacs-Extensions an. Diese landen dann ueblicherweise in
/usr/share/emacs/site-lisp
.Bei Debian/Ubuntu landet die Konfiguration in
/etc/emacs
. Ebenfalls
interessant sind die Skripte unter/var/lib/emacsen/