Scrittura portatile Elisp


8

Idealmente, vorrei essere in grado di memorizzare l'intero contenuto della mia .emacs.ddirectory e farlo "funzionare" su qualsiasi Emacs in cui lo carico, ma sfruttare comunque tutte le funzionalità dell'ambiente specifico, come i sistemi di finestre GUI.

Non sto cercando un'enciclopedia di caratteristiche incompatibili. Vorrei solo sapere come verificare funzionalità, sistema operativo, versione, grafica, ecc. E come trarne vantaggio senza rompere il codice per altre configurazioni.

Quali tecniche di base posso usare per scrivere Elisp che funziona in più versioni di Emacs comuni in natura (ad es. 22.x +) e su più piattaforme sottostanti (ad es. OSX, Linux, Windows e altri * nix), sfruttando al contempo la piattaforma e le funzionalità specifiche della versione dove applicabile?


1
@Malabarba Se la domanda viene interpretata come "elenca tutte le differenze tra versioni e piattaforme diverse", sì, è troppo ampia. Ma le tecniche di base: testare se gli identificatori sono vincolati, recuperare da errori, test window-system, ecc. Possono essere ragionevolmente risolti qui.
Gilles 'SO- smetti di essere malvagio'

1
Emacs 22 non è più "recente". Anche Emacs 23 è datato.

1
Ho incluso emacs 22 perché è comune in natura. Ad esempio, è in bundle con OSX 10.9.
Paul Miller,

Qualsiasi utente serio di Emacs su un Mac scaricherà una versione recente. Emacs 22 è incluso solo in OS X perché è l'ultima versione che è stata concessa in licenza in GPLv2 (e IMO dovrebbero semplicemente abbandonarlo completamente). Penso che 23+ sia molto più utile (IIRC Debian stable usa ancora 23).
shosti,

2
Sheesh. La persona che pone la domanda fa domande su Emacs 22+ e vuoi "correggere" quella richiesta per insistere su una versione più recente? Cosa succederà dopo: qualcuno ti chiede forward-chare vuoi invece che la domanda venga modificata scroll-up? Se l'OP vuole la compatibilità con Emacs 22+, lascialo in pace. E no, la domanda posta non riguarda solo OS X. E sì, ci sono ancora molte persone che usano versioni precedenti di Emacs (alcune anche più vecchie di 22, FWIW).
Disegnò il

Risposte:


10

Elisp è un linguaggio interpretato. Puoi inserire codice specifico della versione nel tuo .emacs, ma proteggerlo testando al momento del caricamento che sta funzionando con la versione corretta.

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

Questo codice funzionerà in tutte le versioni perché (shiny-new-feature)viene valutato solo quando (is-new-feature-available)restituisce true. Gran parte di questa risposta è dedicata a come implementare (is-new-feature-available).

Affrontare diversi set di funzionalità

È meglio verificare se una funzionalità è disponibile piuttosto che testare la versione di Emacs. A volte la funzione potrebbe essere disponibile come pacchetto opzionale. Se vuoi eseguire il codice in XEmacs o altra variante di Emacs, potrebbe aver acquisito le stesse funzionalità in versioni diverse. Utilizzare la funzione boundpper verificare se è disponibile una variabile e fboundpper verificare se è disponibile una funzione.

Ad esempio, il frammento seguente associa una chiave per attivare visual-line-modeo disattivare la disponibilità, e in caso longlines-modecontrario.

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

A volte, piuttosto che testare la funzionalità, è più semplice eseguire un piccolo pezzo di codice e ignorare eventuali errori dovuti a funzioni indefinite, argomenti non validi, ecc. Non farlo per grandi quantità di codice, poiché ciò renderà il codice molto difficile da eseguire il debug.

Ad esempio, non voglio vedere una barra degli strumenti. Le versioni precedenti di Emacs non li avevano affatto. GNU Emacs e XEmacs hanno aggiunto quella caratteristica in diversi modi e lo hanno reso predefinito. Ecco come li spengo. La set-specifierfunzione è specifica per XEmacs ed default-toolbar-visible-pè specifica per le versioni abbastanza recenti di Emacs; l'utilizzo condition-casesoddisfa entrambi i requisiti. GNU Emacs fornisce una funzione dedicata, quindi ho semplicemente testato se quella funzione è disponibile.

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

Alcuni nomi di volti cambiano rispetto alle versioni. Utilizzare facepper testare la disponibilità di un nome volto.

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

A volte potresti voler caricare un bel pacchetto se presente e non fare nulla se il pacchetto non è disponibile. requireha un argomento opzionale per questo.

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

Questo argomento è stato introdotto in GNU Emacs 20.4 e non è disponibile in XEmacs, quindi se vuoi andare così indietro, dovrai avvolgerlo condition-caseo usarlo load(che non controlla le librerie già caricate) .

Limitare le dipendenze della versione alle funzionalità a livello di utente. Non utilizzare le funzioni di programmazione più recenti che non sono disponibili in tutte le versioni che desideri supportare: dovrai fornire una versione di compatibilità per le versioni precedenti ed è più facile mantenere una singola versione.

A volte hai bisogno di una funzionalità in molti luoghi ed è disponibile su tutte le implementazioni che ti interessano, ma in modo diverso. Questo è principalmente il caso se si desidera supportare sia XEmacs che GNU Emacs: avevano una tendenza frustrante a copiare le reciproche funzionalità ma non la loro interfaccia. In questo caso, la definizione di una funzione di compatibilità è più conveniente del test nel punto di utilizzo.

Ad esempio, il codice seguente definisce una funzione che restituisce il sistema di finestre del frame corrente, il modo GNU moderno, il modo XEmac moderno e il modo vecchio stile quando non è possibile combinare i frame terminale e GUI nella stessa istanza.

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

Dipendenze dall'ambiente

Non c'è molto codice che deve essere dipendente dalla piattaforma. La variabile system-typeindica il sistema operativo. Lo uso esclusivamente per attivare alcuni hack per ms-dos(sì, i miei file sono così vecchi) e windows-nt.

Potresti voler aggiungere directory al tuo percorso di ricerca eseguibile ( PATH), ma di solito è meglio farlo al di fuori di Emacs, nel tuo .profilesistema per Unix e attraverso il pannello di controllo in Windows. Per verificare se è disponibile un programma esterno, chiamare executable-find.

Per il codice che deve agire in modo diverso a seconda del tipo di GUI, se presente, controlla window-typeo i suoi successori (vedi sopra).

File di inizializzazione

Per la massima compatibilità, inserisci il tuo codice ~/.emacs. GNU Emacs ha iniziato a cercare nella ~/emacs.dversione 22. XEmacs ha iniziato a cercare ~/.xemacsnella versione 21.4. Un approccio alternativo è inserire ~/.emacse completare il codice di compatibilità caricando il file principale. Metti (setq load-home-init-file t)da qualche parte per evitare che le recenti versioni¹ di XEmac ti chiedano se vuoi spostarti .emacsnella posizione solo per XEmacs.

Versioni diverse di Emacs possono presentare espansioni diverse e incompatibili per alcune macro. Quindi non condividere i file compilati in byte tra le versioni, compilare i file su ogni macchina.

A volte una funzione è obsoleta, ma vuoi comunque usarla perché è tutto ciò che c'è in qualche altra versione che vuoi supportare. Gli avvisi del compilatore di byte provengono dalla byte-obsolete-variableproprietà.

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

¹ Relativamente parlando, rispetto agli XEmac più vecchi.


6

(Community wiki. Per favore, aggiungi il tuo!)

  • Se esiste una funzione, aggiunta in una versione più recente di Emacs, che desideri utilizzare, controlla se è stata definita fboundpe, se non è definita , definisci una funzione di compatibilità.

    È considerata una cattiva idea assegnare alla funzione di compatibilità lo stesso nome della funzione reale, poiché altri codici Elisp potrebbero utilizzare lo stesso fboundptrucco. Pertanto, utilizzare un prefisso per la funzione di compatibilità e utilizzare defaliascon lo stesso nome se è definito. Per esempio:

    (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)))
    
  • Se una parte della configurazione deve applicarsi solo a un determinato sistema operativo, ci sono alcune diverse possibilità. È possibile controllare la system-typevariabile, che restituisce gnu/linux, darwin, windows-nte pochi altri (vedi la docstring).

    Potresti essere tentato di usare window-system, anche se la sua documentazione afferma che "L'uso di questa variabile come booleano è deprecato" e consiglia display-graphic-pinvece di utilizzarlo . Si noti che Emacs è in grado di utilizzare diversi tipi di display per diversi frame al giorno d'oggi (ad esempio un frame su un terminale e un altro in una finestra "corretta"), quindi questo può causare sorprese. Usa current-frame-configurationo get-buffer-window-listnel tuo elisp per fare la scelta giusta.

  • Potresti voler controllare se stai funzionando con il giusto sapore di emacs. Usa featurep per verificare la variante. Per esempio:

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

    Puoi anche usarlo per verificare che siano caricati moduli specifici. Ad esempio, se si utilizza solo un valore predefinito piccolo e facile da definire di common-lisp, è possibile optare per la definizione anziché la richiesta. Per esempio:

    (unless (featurep 'cl)
       (defun caaar (x)
          "Return the `car' of the `car' of the `car' of X."
          (car (car (car x)))))
    
  • Evitare di archiviare i file .elc. Non sono compatibili con le versioni precedenti o successive di alcune versioni di Emacs.


0

Se si utilizzano funzioni da cl-libma non si desidera utilizzare le versioni non spaziate obsolete, rendere la libreria di compatibilità cl-lib una dipendenza del proprio progetto. Ciò ti consentirà di utilizzare le cl-funzioni spaziate dal nome ma di mantenere la compatibilità con le versioni precedenti.


load-library: Impossibile aprire il file di caricamento: cl-lib - Perché dovrei volerlo cl-libcomunque? Che vantaggio ha cl, che esiste da almeno 20 anni?
Gilles 'SO- smetti di essere malvagio' il

1
clè obsoleto e probabilmente verrà rimosso alla fine. L'utilizzo nel codice ha causato avvisi del compilatore di byte per un periodo piuttosto lungo. Penso che il motivo sia che vogliono riutilizzare alcuni dei clnomi (come dolist) con una semantica diversa.
shosti,
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.