Schreiben tragbarer Elisp

8

Im Idealfall möchte ich in der Lage sein, den gesamten Inhalt meines .emacs.dVerzeichnisses zu speichern und es "nur auf allen Emacs funktionieren zu lassen, in die ich es lade", aber dennoch alle Funktionen der spezifischen Umgebung nutzen, wie z. B. GUI-Fenstersysteme.

Ich suche keine Enzyklopädie mit inkompatiblen Funktionen. Ich möchte nur wissen, wie man nach Funktionen, Betriebssystemen, Versionen, Grafiken usw. sucht und wie man diese nutzt, ohne den Code für andere Konfigurationen zu beschädigen.

Welche grundlegenden Techniken kann ich verwenden, um Elisp zu schreiben, das in mehreren Versionen von Emacs funktioniert, die in freier Wildbahn verbreitet sind (z. B. 22.x +) und auf mehreren zugrunde liegenden Plattformen (z. B. OSX, Linux, Windows und anderen * nix), während ich die Plattform nutze und gegebenenfalls versionenspezifische Funktionen?

Paul Miller
quelle
1
@Malabarba Wenn die Frage als "Liste aller Unterschiede zwischen verschiedenen Versionen und Plattformen" interpretiert wird, ist sie viel zu weit gefasst. Aber die grundlegenden Techniken - Testen, ob Bezeichner gebunden sind, Beheben von Fehlern, Testen window-systemusw. können hier angemessen beantwortet werden.
Gilles 'SO - hör auf böse zu sein'
1
Emacs 22 ist nicht mehr "neu". Sogar Emacs 23 ist datiert.
Mondhorn
1
Ich habe Emacs 22 aufgenommen, weil es in freier Wildbahn üblich ist. Zum Beispiel ist es mit OSX 10.9 gebündelt.
Paul Miller
Jeder ernsthafte Emacs-Benutzer auf einem Mac wird eine aktuelle Version herunterladen. Emacs 22 ist nur in OS X enthalten, da es die letzte Version ist, die unter GPLv2 lizenziert wurde (und IMO sollten sie es einfach ganz löschen). Ich denke, 23+ ist viel nützlicher (IIRC Debian Stable verwendet immer noch 23).
Shosti
2
Meine Güte. Die Person, die die Frage stellt , fragt nach Emacs 22+ und Sie möchten diese Anfrage "korrigieren", um auf einer neueren Version zu bestehen? Was kommt als nächstes - jemand fragt nach forward-charund Sie möchten, dass die Frage scroll-upstattdessen geändert wird ? Wenn das OP die Kompatibilität mit Emacs 22+ wünscht, lassen Sie es in Ruhe. Und nein, die gestellte Frage betrifft nicht nur OS X. Und ja, es gibt immer noch viele Leute, die ältere Emacs-Versionen verwenden (einige sogar älter als 22, FWIW).
Drew

Antworten:

10

Elisp ist eine interpretierte Sprache. Sie können versionenspezifischen Code in Ihren Code einfügen .emacs, ihn jedoch schützen, indem Sie beim Laden testen, ob er mit der richtigen Version ausgeführt wird.

(if (is-new-feature-available)
    (shiny-new-feature)
  (old-less-nifty-feature))

Dieser Code funktioniert in allen Versionen, da er (shiny-new-feature)nur ausgewertet wird, wenn (is-new-feature-available)true zurückgegeben wird. Ein Großteil dieser Antwort ist der Implementierung gewidmet (is-new-feature-available).

Umgang mit verschiedenen Funktionen

Es ist besser zu testen, ob eine Funktion verfügbar ist, als die Emacs-Version zu testen. Manchmal ist die Funktion als optionales Paket verfügbar. Wenn Sie Code in XEmacs oder einer anderen Emacs-Variante ausführen möchten, wurden möglicherweise dieselben Funktionen in verschiedenen Versionen erworben. Verwenden Sie die Funktion boundp, um zu testen, ob eine Variable verfügbar ist, und um fboundpzu testen, ob eine Funktion verfügbar ist.

Das folgende Snippet bindet beispielsweise einen Schlüssel zum Umschalten, visual-line-modefalls verfügbar, und longlines-modeansonsten.

(global-set-key "\eml" (if (fboundp 'visual-line-mode)
                           'visual-line-mode
                         'longlines-mode))

Anstatt die Funktion zu testen, ist es manchmal einfacher, einen kleinen Code auszuführen und Fehler aufgrund undefinierter Funktionen, ungültiger Argumente usw. zu ignorieren . Tun Sie dies nicht für große Codemengen, da dies Ihren Code sehr stark macht schwer zu debuggen.

Zum Beispiel möchte ich keine Symbolleiste sehen. Ältere Versionen von Emacs hatten sie überhaupt nicht. GNU Emacs und XEmacs haben diese Funktion auf unterschiedliche Weise hinzugefügt und zur Standardeinstellung gemacht. So schalte ich sie aus. Die set-specifierFunktion ist spezifisch für XEmacs und default-toolbar-visible-pspezifisch für ausreichend aktuelle Versionen von Emacs. using condition-casekümmert sich um beide Anforderungen. GNU Emacs bietet eine spezielle Funktion, daher teste ich lediglich, ob diese Funktion verfügbar ist.

;; For XEmacs
(condition-case nil
    (set-specifier default-toolbar-visible-p nil)
  (error nil))
;; For GNU Emacs
(if (fboundp 'tool-bar-mode)
    (tool-bar-mode 0))

Einige Gesichtsnamen ändern sich über Versionen. Verwenden Sie facepdiese Option , um die Verfügbarkeit eines Gesichtsnamens zu testen.

(let ((face (if (facep 'mode-line) 'mode-line 'modeline)))
  (set-face-background face …))

Manchmal möchten Sie vielleicht ein schönes Paket laden, falls vorhanden, und nichts tun, wenn das Paket nicht verfügbar ist. requirehat ein optionales Argument dafür.

(require 'tex-site nil t) ;; Load AUCTeX if available

Dieses Argument wurde in GNU Emacs 20.4 eingeführt und ist in XEmacs nicht verfügbar. Wenn Sie also so weit zurückgehen möchten, müssen Sie es entweder einschließen condition-caseoder loadstattdessen verwenden (was nicht nach bereits geladenen Bibliotheken sucht). .

Beschränken Sie die Versionsabhängigkeiten auf Funktionen auf Benutzerebene. Verwenden Sie keine neueren Programmierfunktionen, die nicht in allen Versionen verfügbar sind, die Sie unterstützen möchten: Sie müssen eine Kompatibilitätsversion für ältere Versionen bereitstellen, und es ist einfacher, eine einzelne Version zu verwalten.

Manchmal benötigen Sie an vielen Stellen eine Funktion, die für alle Implementierungen verfügbar ist, die Sie interessieren, jedoch auf andere Weise. Dies ist meistens der Fall, wenn Sie sowohl XEmacs als auch GNU Emacs unterstützen möchten: Sie hatten die frustrierende Tendenz, die Funktionen des anderen zu kopieren, nicht jedoch die Benutzeroberfläche. In diesem Fall ist das Definieren einer Kompatibilitätsfunktion bequemer als das Testen am Verwendungsort.

Der folgende Code definiert beispielsweise eine Funktion, die das Fenstersystem des aktuellen Frames, die moderne GNU-Methode, die moderne XEmacs-Methode und die alte Methode zurückgibt, wenn Sie Terminal- und GUI-Frames nicht in derselben Instanz kombinieren konnten.

(defalias 'compat-window-system
  (cond
   ((fboundp 'window-system) #'window-system)
   ((fboundp 'device-type)
    (lambda (&optional frame)
      (device-type (frame-device frame))))
   (t
    (lambda (&optional frame) window-system))))

Umgebungsabhängigkeiten

Es gibt nicht viel Code, der plattformabhängig sein muss. Die Variable system-typegibt das Betriebssystem an. Ich benutze es ausschließlich, um ein paar Hacks für ms-dos(ja, meine Dateien sind so alt) und zu aktivieren windows-nt.

Möglicherweise möchten Sie Ihrem ausführbaren Suchpfad ( PATH) Verzeichnisse hinzufügen. Dies geschieht jedoch normalerweise am besten außerhalb von Emacs, in Ihrem .profilefür Unix-ähnlichen System und über die Systemsteuerung in Windows. Um zu testen, ob ein externes Programm verfügbar ist, rufen Sie auf executable-find.

Für Code, der je nach Art der GUI unterschiedlich handeln muss, überprüfen Sie window-typeoder dessen Nachfolger (siehe oben).

Initialisierungsdateien

Geben Sie Ihren Code für maximale Kompatibilität ein ~/.emacs. GNU Emacs suchte ab ~/emacs.dVersion 22. XEmacs suchte ~/.xemacsab Version 21.4. Ein alternativer Ansatz besteht darin, Kompatibilitätscode einzugeben ~/.emacsund zum Abschluss Ihre Hauptdatei zu laden. Setzen Sie (setq load-home-init-file t)irgendwo zu vermeiden, dass recent¹ Versionen von XEmacs Sie fragen , ob Sie verschieben möchten , .emacsum die XEmacs-only Lage.

Verschiedene Versionen von Emacs können für einige Makros unterschiedliche und inkompatible Erweiterungen aufweisen. Teilen Sie Ihre bytekompilierten Dateien also nicht zwischen Versionen, sondern kompilieren Sie die Dateien auf jedem Computer.

Manchmal ist eine Funktion veraltet, aber Sie möchten sie trotzdem verwenden, da dies alles ist, was in einer anderen Version vorhanden ist, die Sie unterstützen möchten. Die Byte-Compiler-Warnungen stammen aus der byte-obsolete-variableEigenschaft.

(cond
 ((not (boundp 'desktop-enable))
  (defvaralias 'desktop-enable 'desktop-save-mode))
 ((get 'desktop-enable 'byte-obsolete-variable)
  (put 'desktop-enable 'byte-obsolete-variable nil)))

¹ Relativ gesehen im Vergleich zu älteren XEmacs.

Gilles 'SO - hör auf böse zu sein'
quelle
6

(Community-Wiki. Bitte füge deins hinzu!)

  • Wenn es eine Funktion gibt, die in einer neueren Emacs-Version hinzugefügt wurde und die Sie verwenden möchten, überprüfen Sie, ob sie mit definiert ist fboundp, und definieren Sie eine Kompatibilitätsfunktion, wenn sie nicht definiert ist.

    Es wird als schlechte Idee angesehen, der Kompatibilitätsfunktion den gleichen Namen wie der eigentlichen Funktion zu geben, da andere Elisp-Codes möglicherweise denselben fboundpTrick verwenden. Verwenden Sie daher ein Präfix für die Kompatibilitätsfunktion und verwenden Sie es defaliasfür denselben Namen, wenn es definiert ist. Z.B:

    (if (fboundp 'propertize)
        (defalias 'my-propertize 'propertize)
      (defun my-propertize (string &rest properties)
        "Return a copy of STRING with text properties added.
    
     [Note: this docstring has been copied from the Emacs 21 version]
    
    First argument is the string to copy.
    Remaining arguments form a sequence of PROPERTY VALUE pairs for text
    properties to add to the result."
        (let ((str (copy-sequence string)))
          (add-text-properties 0 (length str)
                               properties
                               str)
          str)))
    
  • Wenn eine Konfiguration nur für ein bestimmtes Betriebssystem gelten soll, gibt es verschiedene Möglichkeiten. Sie können die system-typeKontrollvariable, die zurückkehrt gnu/linux, darwin, windows-ntund ein paar andere (siehe docstring).

    Sie könnten versucht sein, zu verwenden window-system, obwohl in der Dokumentzeichenfolge angegeben ist, dass "die Verwendung dieser Variablen als Boolescher Wert veraltet ist", und stattdessen die Verwendung empfohlen wird display-graphic-p. Beachten Sie, dass Emacs heutzutage verschiedene Arten von Anzeigen für verschiedene Frames verwenden kann (z. B. einen Frame auf einem Terminal und einen anderen in einem "richtigen" Fenster), sodass dies zu Überraschungen führen kann. Verwenden Sie current-frame-configurationoder get-buffer-window-listin Ihrem Elisp, um die richtige Wahl zu treffen.

  • Vielleicht möchten Sie überprüfen, ob Sie unter dem richtigen Geschmack von Emacs laufen. Verwenden Sie featurep, um die Variante zu überprüfen. Z.B:

    (when (featurep 'xemacs)
       (require 'fsf-compat))
    

    Sie können damit auch überprüfen, ob bestimmte Module geladen sind. Wenn Sie beispielsweise nur eine kleine und einfach zu definierende Definition von Common-Lisp verwenden, können Sie sich für die Definition entscheiden, anstatt sie zu benötigen. Z.B:

    (unless (featurep 'cl)
       (defun caaar (x)
          "Return the `car' of the `car' of the `car' of X."
          (car (car (car x)))))
    
  • Vermeiden Sie das Speichern von .elc-Dateien. Diese sind zwischen einigen Emacs-Versionen nicht vorwärts oder rückwärtskompatibel.

Legoscia
quelle
0

Wenn Sie Funktionen von verwenden, cl-libaber die veralteten Versionen ohne Namespace nicht verwenden möchten, machen Sie die cl-lib-Kompatibilitätsbibliothek zu einer Abhängigkeit Ihres Projekts. Auf diese Weise können Sie die Namespace- cl-Funktionen verwenden, aber die Abwärtskompatibilität beibehalten.

shosti
quelle
Ladebibliothek: Ladedatei kann nicht geöffnet werden: cl-lib - Warum sollte ich überhaupt wollen cl-lib? Welchen Vorteil hat es gegenüber dem cl, der es seit mindestens 20 Jahren gibt?
Gilles 'SO - hör auf böse zu sein'
1
clist veraltet und wird wahrscheinlich irgendwann entfernt. Die Verwendung im Code hat lange Zeit zu Byte-Compiler-Warnungen geführt. Ich denke, der Grund ist, dass sie einige der clNamen (wie dolist) mit unterschiedlicher Semantik wiederverwenden möchten .
Shosti