¿Cómo heredo de prog-mode, sin dejar de admitir emacsen anterior?

10

Estoy escribiendo un modo principal para un lenguaje de programación, pero quiero admitir versiones anteriores de Emacs. prog-modeEs relativamente nuevo. Quiero heredar de prog-modesi está definido, pero de lo contrario seguiré haciendo algo sensato.

¿Cuál es el mejor enfoque? ¿Debo defalias prog-modeusar Emacsen más antiguo, o eso interferirá con otros modos si hacen lo mismo?

Wilfred Hughes
fuente
Aconsejaría simplemente dejar el soporte para Emacs <24. En mi opinión, ya no vale la pena el esfuerzo, y tendrá que renunciar a funciones más importantes que prog-mode. En particular, sufrirá la falta de unión léxica.
lunaryorn
Estoy contribuyendo al modo julia, y algunos miembros del equipo central usan Emacsen anteriores y preferirían que lo admitiéramos.
Wilfred Hughes
1
@lunaryorn Emacs 24 sigue siendo bastante nuevo. Emacs 23 es la versión actual en muchos sistemas operativos. Emacs 22 todavía está actualizado en algunos. No todos actualizan su software como locos. Dejar caer el soporte para Emacs 23 lo limitaría a los pocos usuarios que anhelan la vanguardia.
Gilles 'SO- deja de ser malvado'
1
Hay muchas razones para usar versiones anteriores de Emacs. Por ejemplo, en Windows, Emacs 23 se volvió realmente lento, por lo que he optado por seguir con Emacs 22 allí.
Lindydancer
@Gilles Dudo que sean solo unos "pocos usuarios". Flycheck nunca admitió Emacs 23 en primer lugar y, sin embargo, se convirtió en uno de los paquetes más populares en MELPA ...
lunaryorn

Respuestas:

11

A costa de un enlace de símbolo de nivel superior adicional, hay una solución muy clara que evita repetir el define-derived-modeformulario:

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

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

Funciona bien en cualquier Emacs> = 23. Se me ocurrió esto hace haml-modeun par de años IIRC, y parece haberse extendido desde allí a varios otros modos principales. Lo principal que hace la define-derived-modemacro con el símbolo del modo padre es generar código que llama a su función: en este sentido, defaliashace que la nueva variable sea exactamente equivalente a la función con alias.

Una advertencia es que esto puede confundir derived-mode-p, por lo que el código que verifica si su modo se deriva prog-modepuede no funcionar correctamente. En la práctica, no he encontrado ningún problema: es más habitual que se conecte dicho código prog-mode-hook, que aún se ejecuta.

(Como Jorgen señala en los comentarios, define-derived-modetambién usa la mode-classpropiedad del símbolo del modo principal y defaliasno la copia. Al momento de escribir, esta propiedad solo parece ser utilizada paraspecial-mode ).

Actualización: en estos días, simplemente sugiero que se requiera al menos Emacs 24, ya que las versiones anteriores son obsoletas durante mucho tiempo.

sanityinc
fuente
2
Buena solución! Solo una advertencia: esto funciona prog-mode, pero no funcionará para todos los modos. define-derived-modecopia la mode-classpropiedad del símbolo en el modo secundario. El defaliasserá no transferir esta propiedad. Si mode-classes relevante para su caso de uso, debe copiarlo / configurarlo manualmente.
Jorgen Schäfer
Gracias por entender eso, Jorgen. Tendré que buscar y aprender más sobre lo que mode-classdenota la propiedad.
sanityinc
3

tl; dr: Use ify su propia función 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)))

Luego haga toda la inicialización del modo en your-cool-init.

Explicación más larga:

El problema es que la forma oficial de escribir un modo principal derivado es usar la define-derived-modemacro:

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

En Emacsen anterior (anterior a 24), esto se rompe cuando prog-mode. Y no puede usarlo (if (fboundp 'prog-mode) ...)allí porque la macro espera un símbolo literal y lo citará en la expansión.

define-derived-modeusa el padre de muchas maneras. Debería copiarlos en su propia definición de modo para utilizarlos, y eso es tedioso y propenso a errores.

Entonces, la única forma es usar dos define-derived-modedeclaraciones diferentes , dependiendo de si prog-modeexiste o no. Eso te deja con el problema de escribir tu código de inicialización dos veces. Lo cual, por supuesto, es malo, por lo que extrae eso en su propia función, como se describe anteriormente.

(La mejor solución es, por supuesto, abandonar el soporte para 23.xy usar el alcance léxico. Pero supongo que ya consideró y eliminó esa opción. :-))

Jorgen Schäfer
fuente
¿Cuál es el equivalente más cercano a prog-modeEmacsen anterior? ¿Tendría sentido derivar de text-modeo fundamental-modesi prog-modeno está disponible?
Wilfred Hughes
@Jorgen ¿O podemos derivar un modo intermedio usando fboundpprimero, solo con la define-derived-modedeclaración? Entonces, ¿el modo real con definición completa puede derivarse de ese modo intermedio? De esta forma, no es necesario definir todo el modo dos veces.
Kaushal Modi
1
@WilfredHughes, no hay ninguno. Derivar de fundamental-modees equivalente a derivar de nil(y de hecho, define-derived-modereemplaza fundamental-modecon nil), aunque text-modeno es apropiado, ya que el código del programa no es texto. La mayoría de las configuraciones predeterminadas text-modeno tienen sentido en los modos de programación fuera de los comentarios. Por eso prog-modese introdujo en Emacs 24.
Jorgen Schäfer
@kaushalmodi, podría derivar un modo intermedio, pero eso aún requeriría dos define-derived-modedefiniciones en una ifforma, solo para el modo intermedio en lugar del modo final. Reemplazaría la defunfunción de inicio por una define-derived-modepara el modo final. No creo que esto sea particularmente preferible. También podría definir prog-modeuno, como sugiere la pregunta original, pero eso puede confundir fácilmente a otros modos en los que confían fboundppara verificar la presencia de ese modo.
Jorgen Schäfer
No creo que define-derived-modesean necesarias dos declaraciones diferentes . Hace un par de años se me ocurrió la solución que publiqué como respuesta por separado, y parece funcionar bien en Emacs 23 y 24. El código como se usa en varios modos principales populares.
sanityinc
0

Creo que probar usando fboundptiene más sentido.

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

Puede definir una macro de contenedor para define-derived-modeque evalúe sus argumentos.

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

(Advertencia: solo mínimamente probado).

Gilles 'SO- deja de ser malvado'
fuente