Comment puis-je hériter du mode prog, tout en prenant en charge les anciens emacsen?

10

J'écris un mode majeur pour un langage de programmation, mais je veux prendre en charge les anciennes versions d'Emacs. prog-modeest relativement nouveau. Je veux hériter de prog-modesi c'est défini, mais toujours faire quelque chose de sensé autrement.

Quelle est la meilleure approche? Dois-je defalias prog-modeutiliser un Emacsen plus ancien, ou cela interférera-t-il avec d'autres modes s'ils font la même chose?

Wilfred Hughes
la source
Je conseillerais de simplement abandonner le support pour Emacs <24. À mon avis, cela ne vaut plus l'effort, et vous devrez renoncer à des fonctionnalités plus importantes que prog-mode. Notamment, vous souffrirez du manque de liaison lexicale.
lunaryorn
Je contribue au mode julia, et une partie de l'équipe principale utilise des Emacsen plus anciens et préférerait que nous le supportions.
Wilfred Hughes
1
@lunaryorn Emacs 24 est encore assez nouveau. Emacs 23 est la version actuelle sur de nombreux systèmes d'exploitation. Emacs 22 est toujours d'actualité sur quelques-uns. Tout le monde ne met pas à jour son logiciel comme un fou. La suppression de la prise en charge d'Emacs 23 vous limiterait aux quelques utilisateurs qui recherchent le bord saignant.
Gilles 'SO- arrête d'être méchant'
1
Il existe de nombreuses raisons d'utiliser les anciennes versions d'Emacs. Par exemple, sous Windows, Emacs 23 est devenu très lent, j'ai donc choisi de m'en tenir à Emacs 22.
Lindydancer
@Gilles Je doute que ce soit juste "quelques utilisateurs". Flycheck n'a jamais pris en charge Emacs 23 en premier lieu et est néanmoins devenu l'un des packages les plus populaires sur MELPA…
lunaryorn

Réponses:

11

Au prix d'une liaison de symboles de niveau supérieur supplémentaire, il existe une solution très soignée qui évite de répéter le define-derived-modeformulaire:

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

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

Fonctionne très bien dans n'importe quel Emacs> = 23. J'ai proposé cela haml-modeil y a quelques années à l'IIRC, et il semble s'être propagé de là à plusieurs autres modes majeurs. La principale chose que la define-derived-modemacro fait avec le symbole du mode parent est de générer du code qui appelle sa fonction: dans ce sens, defaliasrend la nouvelle variable exactement équivalente à la fonction aliasée.

Une mise en garde est que cela peut prêter à confusion derived-mode-p, donc le code qui vérifie si votre mode est dérivé prog-modepeut ne pas fonctionner correctement. En pratique, je n'ai rencontré aucun problème: il est plus courant qu'un tel code s'y accroche prog-mode-hook, qui est toujours exécuté.

(Comme Jorgen le souligne dans les commentaires, define-derived-modeutilise également la mode-classpropriété du symbole du mode parent et defaliasne la copiera pas. Au moment de la rédaction, cette propriété ne semble être utilisée que pourspecial-mode .)

Mise à jour: ces jours-ci, je suggère simplement d'exiger au moins Emacs 24, car les anciennes versions sont obsolètes depuis longtemps.

sanityinc
la source
2
Bonne solution! Juste une mise en garde: cela fonctionne pour prog-mode, mais ne fonctionnera pas pour tous les modes. define-derived-modecopie la mode-classpropriété de symbole dans le mode enfant. Le defaliasva pas transférer cette propriété. Si cela mode-classcorrespond à votre cas d'utilisation, vous devez le copier / définir manuellement.
Jorgen Schäfer
Merci d'avoir attrapé cela, Jorgen - je vais devoir fouiller et en savoir plus sur ce que la mode-classpropriété dénote.
sanityinc
3

tl; dr: utilisez ifet votre propre fonction 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)))

Effectuez ensuite toute l'initialisation du mode your-cool-init.

Explication plus longue:

Le problème est que la manière officielle d'écrire un mode majeur dérivé est d'utiliser la define-derived-modemacro:

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

Sur les anciens Emacsen (pré-24), cela se brise quand prog-mode. Et vous ne pouvez pas l'utiliser (if (fboundp 'prog-mode) ...)parce que la macro attend un symbole littéral et le citera pour vous dans l'extension.

define-derived-modeutilise le parent de multiples façons. Vous devez copier tous ceux-ci dans votre propre définition de mode pour les utiliser, et c'est à la fois fastidieux et sujet aux erreurs.

La seule façon est donc d'utiliser deux define-derived-modeinstructions différentes , selon qu'elles prog-modeexistent ou non. Cela vous pose le problème d'écrire deux fois votre code d'initialisation. Ce qui est bien sûr mauvais, alors vous extrayez cela dans sa propre fonction, comme décrit ci-dessus.

(La meilleure solution est bien sûr de supprimer le support pour 23.x et d'utiliser la portée lexicale. Mais je suppose que vous avez déjà envisagé et abandonné cette option. :-))

Jorgen Schäfer
la source
Quel est l'équivalent le plus proche d' prog-modeEmacsen plus ancien? Serait-il sensé de dériver text-modeou fundamental-modes'il prog-moden'est pas disponible?
Wilfred Hughes,
@Jorgen Ou peut-on dériver un mode intermédiaire en utilisant d' fboundpabord, avec juste l' define-derived-modeinstruction? Ensuite, le mode réel avec définition complète peut être dérivé de ce mode intermédiaire? De cette façon, le mode entier n'a pas à être défini deux fois.
Kaushal Modi
1
@WilfredHughes, il n'y en a pas. Dériver de fundamental-modeéquivaut à dériver de nil(et en fait, define-derived-moderemplace fundamental-modepar nil), bien qu'il text-modene soit pas approprié, car le code de programme n'est pas du texte. La plupart des paramètres par défaut dans text-moden'ont pas de sens dans les modes de programmation en dehors des commentaires. C'est pourquoi a prog-modeété introduit dans Emacs 24.
Jorgen Schäfer
@kaushalmodi, vous pourriez dériver un mode intermédiaire, mais cela nécessiterait toujours deux define-derived-modedéfinitions dans un ifformulaire, juste pour le mode intermédiaire au lieu du mode final. Vous remplaceriez le defunpour la fonction init par un define-derived-modepour le mode final. Je ne pense pas que ce soit particulièrement préférable. Vous pouvez également définir un prog-modevous - même, comme le suggère la question d'origine, mais cela peut facilement confondre d'autres modes qui comptent fboundppour vérifier la présence de ce mode.
Jorgen Schäfer
Je ne pense pas que deux define-derived-modedéclarations différentes soient nécessaires. Il y a quelques années, j'ai trouvé la solution que j'ai publiée en tant que réponse distincte, et cela semble fonctionner correctement dans les deux Emacs 23 et 24. Code comme il est utilisé dans un certain nombre de modes principaux populaires.
sanityinc
0

Je pense que les tests utilisant ont fboundpplus de sens.

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

Vous pouvez définir une macro wrapper pour define-derived-modeévaluer ses arguments.

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

(Avertissement: seulement testé de manière minimale.)

Gilles 'SO- arrête d'être méchant'
la source