Escribir portátil Elisp

8

Idealmente, me gustaría poder almacenar todo el contenido de mi .emacs.ddirectorio y hacer que "funcione" en cualquier Emacs en el que lo cargue, pero aún así aprovechar las características del entorno específico, como los sistemas de ventanas GUI.

No estoy buscando una enciclopedia de características incompatibles. Solo me gustaría saber cómo verificar las características, el sistema operativo, la versión, los gráficos, etc., y cómo aprovecharlos sin romper el código para otras configuraciones.

¿Qué técnicas básicas puedo usar para escribir Elisp que funcione en múltiples versiones de Emacs comunes en la naturaleza (por ejemplo, 22.x +) y en múltiples plataformas subyacentes (por ejemplo, OSX, Linux, Windows y otras * nix), mientras aprovecho la plataforma y características específicas de la versión donde corresponda?

Paul Miller
fuente
1
@Malabarba Si la pregunta se interpreta como "enumerar todas las diferencias entre diferentes versiones y plataformas", sí, es demasiado amplia. Pero las técnicas básicas: probar si los identificadores están vinculados, recuperarse de errores, probar window-system, etc., se pueden responder razonablemente aquí.
Gilles 'SO- deja de ser malvado'
1
Emacs 22 ya no es "reciente". Incluso Emacs 23 tiene fecha.
lunaryorn
1
Incluí emacs 22 porque es común en la naturaleza. Por ejemplo, se incluye con OSX 10.9.
Paul Miller
Cualquier usuario serio de Emacs en una Mac descargará una versión reciente. Emacs 22 solo se incluye en OS X porque es la última versión con licencia de GPLv2 (y en mi opinión, deberían dejarlo por completo). Creo que 23+ es mucho más útil (IIRC Debian estable todavía usa 23).
shosti
2
Sheesh La persona que hace la pregunta pregunta sobre Emacs 22+ y desea "corregir" esa solicitud para insistir en una versión más reciente. ¿Qué sigue? Alguien pregunta forward-chary quiere que se cambie la pregunta para preguntar en su scroll-uplugar. Si el OP quiere compatibilidad con Emacs 22+, déjelo en paz. Y no, la pregunta planteada no es solo acerca de OS X. Y sí, todavía hay muchas personas que usan versiones anteriores de Emacs (algunas incluso mayores de 22 años, FWIW).
Dibujó el

Respuestas:

10

Elisp es un lenguaje interpretado. Puede poner código específico de la versión en su .emacs, pero protegerlo probando en el momento de la carga que está funcionando en la versión correcta.

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

Este código funcionará en todas las versiones porque (shiny-new-feature)solo se evalúa cuando (is-new-feature-available)devuelve verdadero. Gran parte de esta respuesta está dedicada a cómo implementarla (is-new-feature-available).

Lidiando con diferentes conjuntos de características

Es mejor probar si hay una función disponible que probar la versión de Emacs. A veces, la función puede estar disponible como un paquete opcional. Si desea ejecutar código en XEmacs u otra variante de Emacs, es posible que haya adquirido las mismas características en diferentes versiones. Use la función boundppara probar si una variable está disponible y fboundppara probar si una función está disponible.

Por ejemplo, el siguiente fragmento vincula una tecla para alternar visual-line-modesi está disponible, y de lo longlines-modecontrario.

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

A veces, en lugar de probar la función, es más fácil ejecutar un pequeño fragmento de código e ignorar cualquier error debido a funciones indefinidas, argumentos no válidos, etc. No haga esto para grandes cantidades de código, ya que esto hará que su código sea muy Difícil de depurar.

Por ejemplo, no quiero ver una barra de herramientas. Las versiones anteriores de Emacs no los tenían en absoluto. GNU Emacs y XEmacs agregaron esa característica de diferentes maneras y la convirtieron en la predeterminada. Así es como los apago. La set-specifierfunción es específica de XEmacs y default-toolbar-visible-pespecífica de las versiones recientes de Emacs; el uso condition-casese encarga de ambos requisitos. GNU Emacs proporciona una función dedicada, así que simplemente pruebo si esa función está disponible.

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

Algunos nombres de caras cambian sobre las versiones. Use faceppara probar la disponibilidad de un nombre de rostro.

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

A veces es posible que desee cargar un paquete agradable si está presente, y no hacer nada si el paquete no está disponible. requiretiene un argumento opcional para eso.

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

Este argumento se introdujo en GNU Emacs 20.4 y no está disponible en XEmacs, por lo que si desea retroceder tanto, tendrá que envolverlo condition-caseo usarlo load(que no verifica las bibliotecas ya cargadas) .

Limite las dependencias de la versión a las características de nivel de usuario. No utilice las funciones de programación más nuevas que no están disponibles en todas las versiones que desea admitir: tendrá que proporcionar una versión de compatibilidad para versiones anteriores, y es más fácil mantener una versión única.

A veces necesita una función en muchos lugares, y está disponible en todas las implementaciones que le interesan, pero de una manera diferente. Este es principalmente el caso si desea admitir XEmacs y GNU Emacs: tenían una tendencia frustrante de copiar las características de los demás, pero no su interfaz. En este caso, definir una función de compatibilidad es más conveniente que probarlo en el punto de uso.

Por ejemplo, el siguiente código define una función que devuelve el sistema de ventanas del marco actual, la forma moderna de GNU, la forma moderna de XEmacs y la forma antigua cuando no se pueden combinar marcos de terminal y GUI en la misma instancia.

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

Dependencias del entorno

No hay mucho código que deba depender de la plataforma. La variable system-typeindica el sistema operativo. Lo uso exclusivamente para activar algunos hacks para ms-dos(sí, mis archivos son tan viejos) y windows-nt.

Es posible que desee agregar directorios a su ruta de búsqueda ejecutable ( PATH), pero eso suele hacerse mejor fuera de Emacs, en su .profilesistema tipo Unix y a través del panel de control en Windows. Para probar si hay un programa externo disponible, llame al executable-find.

Para el código que necesita actuar de manera diferente dependiendo del tipo de GUI, si lo hay, verifique window-typeo sus sucesores (ver arriba).

Archivos de inicialización

Para obtener la máxima compatibilidad, ingrese su código ~/.emacs. GNU Emacs comenzó a buscar en la ~/emacs.dversión 22. XEmacs comenzó a buscar ~/.xemacsen la versión 21.4. Un enfoque alternativo es colocar ~/.emacsy finalizar el código de compatibilidad cargando su archivo principal. Colóquelo en (setq load-home-init-file t)algún lugar para evitar que las versiones recientes de XEmacs le pregunten si desea moverlo .emacsa la ubicación exclusiva de XEmacs.

Las diferentes versiones de Emacs pueden tener una expansión diferente e incompatible para algunas macros. Por lo tanto, no comparta sus archivos compilados por bytes entre versiones, compile los archivos en cada máquina.

A veces, una característica está en desuso, pero aún así desea usarla porque eso es todo lo que hay en alguna otra versión que desea admitir. Las advertencias del compilador de bytes provienen de la byte-obsolete-variablepropiedad.

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

Speaking Relativamente hablando, en comparación con XEmacs anteriores.

Gilles 'SO- deja de ser malvado'
fuente
6

(Wiki de la comunidad. ¡Agregue el suyo!)

  • Si hay una función, agregada en una versión más nueva de Emacs, que le gustaría usar, verifique si está definida fboundpy defina una función de compatibilidad si no está definida.

    Se considera una mala idea dar a la función de compatibilidad el mismo nombre que la función real, ya que otro código de Elisp puede estar usando el mismo fboundptruco. Por lo tanto, use un prefijo para la función de compatibilidad y use defaliasel mismo nombre si está definido. P.ej:

    (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)))
    
  • Si alguna configuración solo se aplica a un determinado sistema operativo, hay algunas posibilidades diferentes. Puede comprobar la system-typevariable, lo que vuelve gnu/linux, darwin, windows-nty algunas otras (véase la cadena de documentación).

    Es posible que tenga la tentación de usarlo window-system, aunque su cadena de documentación indica que "El uso de esta variable como booleano está en desuso", y recomienda usarlo en su display-graphic-plugar. Tenga en cuenta que Emacs puede usar diferentes tipos de pantallas para diferentes marcos en la actualidad (por ejemplo, un marco en un terminal y otro en una ventana "adecuada"), por lo que esto puede causar sorpresas. Use current-frame-configurationo get-buffer-window-listen su elisp para tomar la decisión correcta.

  • Es posible que desee comprobar si se está ejecutando bajo el sabor correcto de emacs. Use featurep para verificar la variante. P.ej:

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

    También puede usarlo para verificar que módulos específicos estén cargados. Por ejemplo, si solo usa un defun de common-lisp pequeño y fácil de definir, puede optar por definir en lugar de requerir. P.ej:

    (unless (featurep 'cl)
       (defun caaar (x)
          "Return the `car' of the `car' of the `car' of X."
          (car (car (car x)))))
    
  • Evite almacenar archivos .elc. Estos no son compatibles con versiones anteriores o posteriores de algunas versiones de Emacs.

legoscia
fuente
0

Si está utilizando funciones de cl-libpero no desea utilizar versiones desaprovechadas sin espacios de nombres, haga que la biblioteca de compatibilidad cl-lib sea una dependencia de su proyecto. Eso le permitirá usar las cl-funciones de espacio de nombres pero conservará la compatibilidad con versiones anteriores.

shosti
fuente
load-library: No se puede abrir el archivo de carga: cl-lib - ¿Por qué querría de cl-libtodos modos? ¿Qué ventaja tiene sobre la clque existe desde hace al menos 20 años?
Gilles 'SO- deja de ser malvado'
1
clestá en desuso y probablemente se eliminará eventualmente. Usarlo en código ha provocado advertencias de compilación de bytes durante bastante tiempo. Creo que la razón es que quieren reutilizar algunos de los clnombres (como dolist) con diferentes semánticas.
shosti