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.