Come posso ereditare da prog-mode, pur supportando emacsen più vecchi?


10

Sto scrivendo una modalità principale per un linguaggio di programmazione, ma voglio supportare le versioni precedenti di Emacs. prog-modeè relativamente nuovo. Voglio ereditare da prog-modese è definito, ma fare comunque qualcosa di sensato altrimenti.

Qual è l'approccio migliore? Dovrei defalias prog-modeusare Emacsen più vecchio, o interferire con altre modalità se fanno la stessa cosa?


Consiglio di abbandonare semplicemente il supporto per Emacs <24. A mio avviso, non vale più la pena, e dovrete rinunciare a funzionalità più importanti di prog-mode. In particolare, soffrirai della mancanza di legame lessicale.
lunaryorn,

Sto contribuendo alla modalità julia e alcuni membri del team centrale usano Emacsen più vecchio e preferirei che lo supportassimo.
Wilfred Hughes,

1
@lunaryorn Emacs 24 è ancora abbastanza nuovo. Emacs 23 è la versione corrente su molti sistemi operativi. Emacs 22 è ancora attuale su alcuni. Non tutti aggiornano il loro software come matti. Eliminare il supporto per Emacs 23 ti limiterebbe ai pochi utenti che bramano il limite.
Gilles 'SO- smetti di essere malvagio'

1
Ci sono molti motivi per usare le versioni precedenti di Emacs. Ad esempio, su Windows, Emacs 23 è diventato molto lento, quindi ho optato per Emacs 22 lì.
Lindydancer,

@Gilles Dubito che siano solo "pochi utenti". Flycheck non ha mai supportato Emacs 23 in primo luogo, e tuttavia è diventato uno dei pacchetti più popolari su MELPA ...
lunaryorn,

Risposte:


11

Al costo di un'associazione di simboli di livello superiore extra, esiste una soluzione molto accurata che evita di ripetere il define-derived-modemodulo:

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

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

Funziona bene in qualsiasi Emacs> = 23. Ho pensato a questo per haml-modeun paio d'anni fa IIRC, e sembra essersi diffuso da lì a molte altre modalità principali. La cosa principale che la define-derived-modemacro fa con il simbolo della modalità genitore è generare codice che chiama la sua funzione: in questo senso, defaliasrende la nuova variabile esattamente equivalente alla funzione con alias.

Un avvertimento è che questo può confondere derived-mode-p, quindi il codice che controlla se la tua modalità è derivata prog-modepotrebbe non funzionare correttamente. In pratica non ho riscontrato alcun problema: è più normale che tale codice si agganci prog-mode-hook, che viene comunque eseguito.

(Come sottolinea Jorgen nei commenti, define-derived-modeusa anche la mode-classproprietà dal simbolo della modalità padre e defaliasnon la copia. Al momento in cui scrivo, questa proprietà sembra essere usata solo perspecial-mode .)

Aggiornamento: al giorno d'oggi suggerirei semplicemente di richiedere almeno Emacs 24, poiché le versioni precedenti sono obsolete da tempo.


2
Bella soluzione! Solo un avvertimento: funziona per prog-mode, ma non funzionerà per tutte le modalità. define-derived-modecopia la mode-classproprietà del simbolo in modalità figlio. Il defaliassi non trasferire questa proprietà. Se mode-classè rilevante per il tuo caso d'uso, devi copiarlo / impostarlo manualmente.
Jorgen Schäfer,

Grazie per averlo scoperto, Jorgen - Dovrò scavare e imparare di più su ciò che la mode-classproprietà indica.
sanityinc

3

tl; dr: utilizzare ife la propria funzione init:

(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)))

Quindi esegui tutte le inizializzazioni della modalità in your-cool-init.

Spiegazione più lunga:

Il problema è che il modo ufficiale di scrivere una modalità principale derivata è usare la define-derived-modemacro:

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

Su Emacsen più vecchio (pre-24), questo si interrompe quando prog-mode. E non puoi usarlo (if (fboundp 'prog-mode) ...)perché la macro si aspetta un simbolo letterale e lo citerà per te nell'espansione.

define-derived-modeusa il genitore in molti modi. Dovresti copiarli tutti nella tua definizione di modalità per usarli, ed è sia noioso che soggetto a errori.

Quindi l'unico modo è usare due diverse define-derived-modeaffermazioni, a seconda che prog-modeesista o meno. Questo ti lascia con il problema di scrivere il tuo codice di inizializzazione due volte. Il che è ovviamente negativo, quindi lo estrai nella sua stessa funzione, come descritto sopra.

(La soluzione migliore è ovviamente quella di eliminare il supporto per 23.xe utilizzare l'ambito lessicale. Ma immagino che tu abbia già preso in considerazione e lasciato cadere questa opzione. :-))


Qual è l'equivalente più vicino al prog-modevecchio Emacsen? Avrebbe senso derivare text-modeo fundamental-modese prog-modenon fosse disponibile?
Wilfred Hughes,

@Jorgen Oppure possiamo derivare una modalità intermedia usando fboundpprima, con solo la define-derived-modefrase? Quindi la modalità effettiva con la definizione completa può essere derivata da quella modalità intermedia? In questo modo l'intera modalità non deve essere definita due volte.
Kaushal Modi,

1
@WilfredHughes, non ce n'è. Derivare da fundamental-modeequivale a derivare da nil(e in effetti define-derived-modesostituisce fundamental-modecon nil), sebbene text-modenon sia appropriato, poiché il codice del programma non è testo. La maggior parte delle impostazioni predefinite text-modenon ha senso nelle modalità di programmazione al di fuori dei commenti. Questo è il motivo per cui è prog-modestato introdotto in Emacs 24.
Jorgen Schäfer,

@kaushalmodi, potresti derivare una modalità intermedia, ma ciò richiederebbe comunque due define-derived-modedefinizioni in una ifforma, solo per la modalità intermedia anziché la modalità finale. Dovresti sostituire la funzione defunfor init con una define-derived-modemodalità finale. Non credo sia particolarmente preferibile. Potresti anche definire un prog-modete stesso, come suggerisce la domanda originale, ma ciò può facilmente confondere altre modalità su cui fare affidamento fboundpper verificare la presenza di quella modalità.
Jorgen Schäfer,

Non credo che define-derived-modesiano necessarie due diverse dichiarazioni. Un paio di anni fa ho trovato la soluzione che ho pubblicato come risposta separata, e sembra funzionare bene in entrambi i codici Emacs 23 e 24. Come se fosse usato in una serie di popolari modalità principali.
sanityinc

0

Penso che test usando fboundpabbia più senso.

(if (fboundp 'prog-mode)
    ...
   ...)

0

È possibile definire una macro wrapper per la define-derived-modevalutazione dei suoi argomenti.

(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))

(Attenzione: testato solo in minima parte.)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.