Como herdo do prog-mode, enquanto ainda suporta o emacsen mais antigo?

10

Estou escrevendo um modo principal para uma linguagem de programação, mas quero oferecer suporte a versões mais antigas do Emacs. prog-modeé relativamente novo. Quero herdar de prog-modese estiver definido, mas ainda assim fazer algo sensato.

Qual é a melhor abordagem? Devo defalias prog-modeno Emacsen mais antigo, ou isso interferirá em outros modos se eles fizerem a mesma coisa?

Wilfred Hughes
fonte
Eu recomendaria simplesmente abandonar o suporte ao Emacs <24. Na minha opinião, não vale mais a pena o esforço, e você terá que renunciar a recursos mais importantes do que prog-mode. Notavelmente, você sofrerá com a falta de vinculação lexical.
Lunaryorn 9/10
Estou contribuindo para o modo julia, e alguns membros da equipe principal usam o Emacsen mais antigo e preferem que o apoiem.
Wilfred Hughes
11
@lunaryorn O Emacs 24 ainda é bem novo. O Emacs 23 é a versão atual em muitos sistemas operacionais. O Emacs 22 ainda está atualizado em alguns. Nem todo mundo atualiza seu software como um louco. Desistir do suporte ao Emacs 23 limitaria você aos poucos usuários que desejam o que há de mais atual.
Gilles 'SO- stop be evil'
11
Existem muitas razões para usar versões mais antigas do Emacs. Por exemplo, no Windows, o Emacs 23 ficou muito lento, por isso optei por manter o Emacs 22 lá.
27614 Lindydancer #
@ Gilles duvido que seja apenas alguns "usuários". Flycheck nunca suportou o Emacs 23 em primeiro lugar e se tornou um dos pacotes mais populares no MELPA, no entanto ...
lunaryorn

Respostas:

11

À custa de uma ligação extra de símbolo de nível superior, há uma solução muito elegante que evita repetir o define-derived-modeformulário:

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

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

Funciona bem em qualquer Emacs> = 23. Eu vim com isso há haml-modealguns anos atrás, o IIRC, e parece que ele se espalhou de lá para vários outros modos principais. A principal coisa que a define-derived-modemacro faz com o símbolo do modo pai é gerar um código que chama sua função: nesse sentido, defaliastorna a nova variável exatamente equivalente à função com alias.

Uma ressalva é que isso pode confundir derived-mode-p, portanto, o código que verifica se seu modo é derivado prog-modepode não funcionar corretamente. Na prática, não encontrei nenhum problema: é mais comum o uso desse código prog-mode-hook, que ainda é executado.

(Como Jorgen aponta nos comentários, define-derived-modetambém usa a mode-classpropriedade do símbolo do modo pai e defaliasnão a copia. No momento da redação, esta propriedade parece ser usada apenasspecial-mode .)

Atualização: hoje em dia eu simplesmente sugeriria exigir pelo menos o Emacs 24, já que versões mais antigas são obsoletas há muito tempo.

sanityinc
fonte
2
Ótima solução! Apenas uma ressalva: Isso funciona para prog-mode, mas não funciona para todos os modos. define-derived-modecopia a mode-classpropriedade do símbolo para o modo filho. O defaliasvão não transferir essa propriedade. Se mode-classfor relevante para o seu caso de uso, você precisará copiá-lo / configurá-lo manualmente.
Jorgen Schäfer
Obrigado por entender isso, Jorgen - vou ter que procurar e aprender mais sobre o que a mode-classpropriedade indica.
Sanityinc 10/10
3

tl; dr: Use ife sua própria função 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)))

Em seguida, faça toda a inicialização do modo em your-cool-init.

Explicação mais longa:

O problema é que a maneira oficial de escrever um modo principal derivado é usar a define-derived-modemacro:

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

No Emacsen mais antigo (pré-24), isso quebra quando prog-mode. E você não pode usá (if (fboundp 'prog-mode) ...)-lo porque a macro espera um símbolo literal e o citará na expansão.

define-derived-modeusa o pai de várias maneiras. Você precisaria copiar todos aqueles em sua própria definição de modo para usá-los, e isso é tedioso e propenso a erros.

Portanto, a única maneira é usar duas define-derived-modeinstruções diferentes , dependendo da existência prog-modeou não. Isso deixa você com o problema de escrever seu código de inicialização duas vezes. O que é obviamente ruim, então você extrai isso para sua própria função, conforme descrito acima.

(É claro que a melhor solução é largar o suporte para 23.xe usar o escopo lexical. Mas acho que você já considerou e largou essa opção. :-))

Jorgen Schäfer
fonte
Qual é o equivalente mais próximo do prog-modeEmacsen mais antigo? Faria sentido derivar text-modeou fundamental-modese prog-modenão estiver disponível?
Wilfred Hughes
@ Jorgen Ou podemos derivar um modo intermediário usando fboundpprimeiro, apenas com a define-derived-modedeclaração? Então o modo atual com definição completa pode ser derivado desse modo intermediário? Dessa forma, o modo inteiro não precisa ser definido duas vezes.
precisa saber é o seguinte
11
@WilfredHughes, não há nenhum. Derivar fundamental-modeé equivalente a derivar de nil(e de fato define-derived-modesubstitui fundamental-modepor nil), embora text-modenão seja apropriado, pois o código do programa não é texto. A maioria das configurações padrão text-modenão faz sentido nos modos de programação fora dos comentários. É por isso que prog-modefoi introduzido em Emacs 24.
Jorgen Schäfer
@kaushalmodi, você pode derivar um modo intermediário, mas isso ainda exigiria duas define-derived-modedefinições em um ifformulário, apenas para o modo intermediário, em vez do modo final. Você estaria substituindo a defunfunção for init por uma define-derived-modepara o modo final. Eu não acho que isso seja particularmente preferível. Você também pode definir um prog-modevocê mesmo, como sugere a pergunta original, mas isso pode facilmente confundir outros modos que dependem fboundppara verificar a presença desse modo.
Jorgen Schäfer
Não acredito que duas define-derived-modedeclarações diferentes sejam necessárias. Alguns anos atrás, eu vim com a solução que eu publiquei como uma resposta separada, e parece funcionar bem nos Emacs 23 e 24. Código como ele é usado em vários modos populares populares.
Sanityinc
0

Eu acho que testar usando fboundpfaz mais sentido.

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

Você pode definir uma macro de wrapper para define-derived-modeavaliar seus 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))

(Aviso: apenas minimamente testado.)

Gilles 'SO- parar de ser mau'
fonte