Wie erbe ich vom Prog-Modus, während ich ältere Emacsen unterstütze?

10

Ich schreibe einen Hauptmodus für eine Programmiersprache, möchte aber ältere Emacs-Versionen unterstützen. prog-modeist relativ neu. Ich möchte von erben, prog-modewenn es definiert ist, aber trotzdem etwas Vernünftiges tun.

Was ist der beste Ansatz? Sollte ich defalias prog-modeauf älteren Emacsen, oder wird das andere Modi stören, wenn sie das gleiche tun?

Wilfred Hughes
quelle
Ich würde raten, die Unterstützung für Emacs <24 einfach einzustellen. Meiner Meinung nach lohnt sich die Mühe nicht mehr, und Sie müssen auf wichtigere Funktionen verzichten als prog-mode. Insbesondere leiden Sie unter dem Mangel an lexikalischer Bindung.
Mondhorn
Ich trage zum Julia-Modus bei, und einige Mitglieder des Kernteams verwenden ältere Emacsen und würden es vorziehen, wenn wir sie unterstützen.
Wilfred Hughes
1
@lunaryorn Emacs 24 ist noch ziemlich neu. Emacs 23 ist die aktuelle Version unter vielen Betriebssystemen. Emacs 22 ist bei einigen noch aktuell. Nicht jeder aktualisiert seine Software wie verrückt. Wenn Sie die Unterstützung für Emacs 23 einstellen, sind Sie auf die wenigen Benutzer beschränkt, die sich nach der Blutung sehnen.
Gilles 'SO - hör auf böse zu sein'
1
Es gibt viele Gründe, ältere Emacs-Versionen zu verwenden. Unter Windows wurde Emacs 23 beispielsweise sehr träge, daher habe ich mich entschieden, dort bei Emacs 22 zu bleiben.
Lindydancer
@ Gilles Ich bezweifle, dass es nur "wenige Benutzer" sind. Flycheck unterstützte Emacs 23 überhaupt nicht und wurde dennoch zu einem der beliebtesten Pakete auf MELPA…
Lunaryorn

Antworten:

11

Auf Kosten einer zusätzlichen Symbolbindung der obersten Ebene gibt es eine sehr übersichtliche Lösung, bei der das define-derived-modeFormular nicht wiederholt wird:

(defalias 'my-fancy-parent-mode
  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))

(define-derived-mode my-fancy-mode my-fancy-parent-mode
   ...)

Funktioniert gut in jedem Emacs> = 23. Ich habe mir das vor haml-modeein paar Jahren IIRC ausgedacht und es scheint sich von dort auf mehrere andere Hauptmodi ausgeweitet zu haben. Das define-derived-modeMakro generiert hauptsächlich mit dem übergeordneten Modus-Symbol Code, der seine Funktion aufruft: In diesem Sinne entspricht defaliasdie neue Variable genau der Alias-Funktion.

Eine Einschränkung ist, dass dies verwirrend sein kann. Daher funktioniert derived-mode-pCode, der überprüft, ob Ihr Modus abgeleitet ist, prog-modemöglicherweise nicht richtig. In der Praxis sind keine Probleme aufgetreten: Es ist üblicher, dass sich ein solcher Code einhakt prog-mode-hook, der immer noch ausgeführt wird.

(Wie Jorgen in den Kommentaren ausführt, wird define-derived-modeauch die mode-classEigenschaft aus dem übergeordneten Modus-Symbol verwendet und defaliasnicht kopiert. Zum Zeitpunkt des Schreibens scheintspecial-mode diese Eigenschaft nur für verwendet zu werden .)

Update: Heutzutage würde ich einfach vorschlagen, mindestens Emacs 24 zu benötigen, da ältere Versionen längst veraltet sind.

sanityinc
quelle
2
Schöne Lösung! Nur eine Einschränkung: Dies funktioniert prog-mode, funktioniert aber nicht für jeden Modus. define-derived-modekopiert die mode-classSymboleigenschaft in den untergeordneten Modus. Das defaliaswird diese Eigenschaft nicht übertragen. Wenn dies mode-classfür Ihren Anwendungsfall relevant ist, müssen Sie es manuell kopieren / einstellen.
Jorgen Schäfer
Danke, dass du das verstanden hast, Jorgen - ich muss mich umsehen und mehr darüber erfahren, was die mode-classImmobilie bedeutet.
Sanityinc
3

tl; dr: Verwenden Sie ifund Ihre eigene Init-Funktion:

(if (fboundp 'prog-mode)
    (define-derived-mode your-cool-mode prog-mode "Cool"
      "Docstring"
      (your-cool--init))
  (define-derived-mode your-cool-mode nil "Cool"
    "Docstring"
    (your-cool--init)))

Führen Sie dann die gesamte Initialisierung des Modus in aus your-cool-init.

Längere Erklärung:

Das Problem ist, dass die offizielle Art, einen abgeleiteten Hauptmodus zu schreiben, darin besteht, das define-derived-modeMakro zu verwenden:

(define-derived-mode your-cool-mode prog-mode ...)

Bei älteren Emacsen (vor 24) bricht dies ab, wenn prog-mode. Und Sie können es dort nicht verwenden, (if (fboundp 'prog-mode) ...)da das Makro ein Literalsymbol erwartet und es in der Erweiterung für Sie zitiert.

define-derived-modeverwendet das übergeordnete Element auf vielfältige Weise. Sie müssten alle in Ihrer eigenen Modusdefinition kopieren, um sie verwenden zu können, und das ist sowohl mühsam als auch fehleranfällig.

Die einzige Möglichkeit besteht darin, zwei verschiedene define-derived-modeAnweisungen zu verwenden, je nachdem, ob sie prog-modevorhanden sind oder nicht. Dadurch haben Sie das Problem, Ihren Initialisierungscode zweimal zu schreiben. Was natürlich schlecht ist, also extrahieren Sie das in seine eigene Funktion, wie oben beschrieben.

(Die beste Lösung ist natürlich, die Unterstützung für 23.x einzustellen und das lexikalische Scoping zu verwenden. Aber ich denke, Sie haben diese Option bereits in Betracht gezogen und fallen gelassen. :-))

Jorgen Schäfer
quelle
Was ist das nächste Äquivalent zu prog-modeälteren Emacsen? Wäre es sinnvoll, von abzuleiten text-modeoder fundamental-modewenn prog-modenicht verfügbar?
Wilfred Hughes
@Jorgen Oder können wir einen Zwischenmodus ableiten, indem wir fboundpzuerst nur die define-derived-modeAnweisung verwenden? Dann kann der tatsächliche Modus mit vollständiger Definition aus diesem Zwischenmodus abgeleitet werden? Auf diese Weise muss der gesamte Modus nicht zweimal definiert werden.
Kaushal Modi
1
@ WilfredHughes, es gibt keine. Herleiten aus fundamental-modeentspricht Ableiten aus nil(und in der Tat, define-derived-modeersetzt fundamental-modemit nil), während der text-modenicht geeignet ist, als Programmcode kein Text ist. Die meisten Standardeinstellungen in text-modesind in Programmiermodi außerhalb von Kommentaren nicht sinnvoll. Aus diesem Grund prog-modewurde in Emacs 24 eingeführt.
Jorgen Schäfer
@kaushalmodi, Sie könnten einen Zwischenmodus ableiten, aber das würde immer noch zwei define-derived-modeDefinitionen in einer ifForm erfordern , nur für den Zwischenmodus anstelle des endgültigen Modus. Sie würden defundie Funktion für die Init-Funktion durch eine define-derived-modefür den Endmodus ersetzen . Ich denke nicht, dass dies besonders vorzuziehen ist. Sie können auch ein prog-modeSelbst definieren , wie aus der ursprünglichen Frage hervorgeht, aber dies kann andere Modi leicht verwirren, die darauf angewiesen sind fboundp, das Vorhandensein dieses Modus zu überprüfen.
Jorgen Schäfer
Ich glaube nicht, dass zwei unterschiedliche define-derived-modeAussagen notwendig sind. Vor ein paar Jahren habe ich die Lösung gefunden, die ich als separate Antwort veröffentlicht habe, und sie scheint in beiden Emacs 23 und 24 gut zu funktionieren. Code, wie er in einer Reihe beliebter Hauptmodi verwendet wird.
Sanityinc
0

Ich denke, das Testen mit fboundpmacht mehr Sinn.

(if (fboundp 'prog-mode)
    ...
   ...)
Alex Schröder
quelle
0

Sie können ein Wrapper-Makro definieren define-derived-mode, das seine Argumente auswertet.

(defmacro define-derived-mode* (child parent name &optional docstring &rest body)
  (macroexpand `(define-derived-mode ,(eval child) ,(eval parent) ,(eval name)
                                     ,(eval docstring) . ,body)))
(define-derived-mode* 'toy-mode
  (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)
  "Toy"
  "Major mode for my favorite toy language"
  (toy-mode-setup))

(Warnung: nur minimal getestet.)

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