Escrevendo Elisp portátil

8

Idealmente, eu gostaria de poder armazenar todo o conteúdo do meu .emacs.ddiretório e fazê-lo "simplesmente funcionar" em qualquer Emacs em que o carregue, mas ainda assim tirar proveito de todos os recursos do ambiente específico, como os sistemas de janelas da GUI.

Não estou procurando uma enciclopédia de recursos incompatíveis. Eu gostaria de saber como verificar os recursos, SO, versão, gráficos, etc., e como tirar proveito deles sem quebrar o código para outras configurações.

Que técnicas básicas posso usar para escrever o Elisp que funciona em várias versões do Emacs comuns em estado selvagem (por exemplo, 22.x +) e em várias plataformas subjacentes (por exemplo, OSX, Linux, Windows e outro * nix), enquanto aproveita a plataforma e recursos específicos da versão, onde aplicável?

Paul Miller
fonte
1
@ Malabarba Se a pergunta for interpretada como “listar todas as diferenças entre diferentes versões e plataformas”, sim, é muito ampla. Mas as técnicas básicas - teste de identificação de identificadores, recuperação de erros, teste window-systemetc. - podem ser respondidas com razoabilidade aqui.
Gilles 'SO- stop be evil'
1
O Emacs 22 não é mais "recente". Até o Emacs 23 está datado.
Lunaryorn 9/10
1
Eu incluí o emacs 22 porque é comum na natureza. Por exemplo, ele vem com o OSX 10.9.
Paul Miller
Qualquer usuário sério do Emacs em um Mac fará o download de uma versão recente. O Emacs 22 está incluído apenas no OS X porque é a última versão licenciada sob a GPLv2 (e a IMO deve descartá-la totalmente). Eu acho que 23+ é muito mais útil (o IIRC Debian stable ainda usa 23).
Shosti
2
Sheesh. A pessoa que faz a pergunta pergunta sobre o Emacs 22+ e você deseja "corrigir" essa solicitação para insistir em uma versão mais recente? O que vem a seguir - alguém pergunta forward-chare você quer que a pergunta seja alterada scroll-up? Se o OP deseja compatibilidade com o Emacs 22+, deixe-o em paz. E não, a questão colocada não é apenas sobre o OS X. E sim, ainda existem muitas pessoas usando versões mais antigas do Emacs (algumas até com mais de 22 anos, FWIW).
Tirou

Respostas:

10

Elisp é uma linguagem interpretada. Você pode colocar o código específico da versão no seu .emacs, mas proteja-o testando no tempo de carregamento que está operando na versão correta.

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

Este código funcionará em todas as versões porque (shiny-new-feature)é avaliado apenas quando (is-new-feature-available)retorna true. Grande parte dessa resposta é dedicada a como implementar (is-new-feature-available).

Lidando com diferentes conjuntos de recursos

É melhor testar se um recurso está disponível do que testar a versão do Emacs. Às vezes, o recurso pode estar disponível como um pacote opcional. Se você deseja executar o código no XEmacs ou em outra variante do Emacs, ele pode ter adquirido os mesmos recursos em versões diferentes. Use a função boundppara testar se uma variável está disponível e fboundppara testar se uma função está disponível.

Por exemplo, o seguinte trecho vincula uma chave para alternar, visual-line-modese disponível, e longlines-modecaso contrário.

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

Às vezes, em vez de testar o recurso, é mais fácil executar um pequeno pedaço de código e ignorar qualquer erro devido a funções indefinidas, argumentos inválidos etc. Não faça isso para grandes quantidades de código, pois isso tornará seu código muito difícil de depurar.

Por exemplo, não quero ver uma barra de ferramentas. As versões mais antigas do Emacs não as possuíam. O GNU Emacs e o XEmacs adicionaram esse recurso de maneiras diferentes e o tornaram o padrão. Aqui está como eu desligá-los. A set-specifierfunção é específica para o XEmacs e default-toolbar-visible-pespecífica para versões recentes o suficiente do Emacs; usando condition-casecuida de ambos os requisitos. O GNU Emacs fornece uma função dedicada, então apenas testo se essa função está disponível.

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

Alguns nomes de rosto mudam ao longo das versões. Use faceppara testar a disponibilidade de um nome de rosto.

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

Às vezes, você pode querer carregar um pacote legal, se presente, e não fazer nada se o pacote não estiver disponível. requiretem um argumento opcional para isso.

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

Esse argumento foi introduzido no GNU Emacs 20.4 e não está disponível no XEmacs; portanto, se você quiser ir tão longe, precisará envolvê-lo condition-caseou usá-lo load(o que não verifica as bibliotecas já carregadas) .

Limite as dependências da versão aos recursos no nível do usuário. Não use recursos de programação mais recentes que não estejam disponíveis em todas as versões que você deseja oferecer suporte: você precisará fornecer uma versão de compatibilidade para versões mais antigas, e é mais fácil manter uma única versão.

Às vezes, você precisa de um recurso em muitos lugares e está disponível em todas as implementações de que você gosta, mas de uma maneira diferente. Esse é o caso principalmente se você deseja suportar o XEmacs e o GNU Emacs: eles tinham uma tendência frustrante de copiar os recursos um do outro, mas não a interface. Nesse caso, definir uma função de compatibilidade é mais conveniente do que testar no ponto de uso.

Por exemplo, o código a seguir define uma função que retorna o sistema de janelas do quadro atual, a maneira moderna GNU, a maneira moderna do XEmacs e a maneira antiga quando você não podia combinar quadros de terminal e GUI na mesma instância.

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

Dependências do ambiente

Não há muito código que precise depender da plataforma. A variável system-typeindica o sistema operacional. Eu o uso exclusivamente para ativar alguns hacks para ms-dos(sim, meus arquivos são antigos) e windows-nt.

Você pode adicionar diretórios ao seu caminho de pesquisa executável ( PATH), mas geralmente é melhor fazê-lo fora do Emacs, no seu .profilesistema para Unix e no painel de controle do Windows. Para testar se um programa externo está disponível, ligue executable-find.

Para código que precisa agir de maneira diferente, dependendo do tipo de GUI, se houver, verifique window-typeou seus sucessores (veja acima).

Arquivos de inicialização

Para máxima compatibilidade, insira seu código ~/.emacs. O GNU Emacs começou a procurar na ~/emacs.dversão 22. O XEmacs começou a procurar ~/.xemacsna versão 21.4. Uma abordagem alternativa é ~/.emacsinserir e concluir o código de compatibilidade carregando o arquivo principal. Coloque um (setq load-home-init-file t)lugar para evitar que as versões recentes do XEmacs perguntem se você deseja movê-lo .emacspara o local exclusivo do XEmacs.

Versões diferentes do Emacs podem ter expansão diferente e incompatível para algumas macros. Portanto, não compartilhe seus arquivos compilados em bytes entre versões, compile os arquivos em cada máquina.

Às vezes, um recurso está obsoleto, mas você ainda deseja usá-lo, porque é tudo o que existe em outra versão que você deseja oferecer suporte. Os avisos do compilador de bytes vêm da byte-obsolete-variablepropriedade

(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 falando, comparado com os XEmacs mais antigos.

Gilles 'SO- parar de ser mau'
fonte
6

(Wiki da comunidade. Adicione o seu!)

  • Se houver uma função, adicionada em uma versão mais recente do Emacs, que você gostaria de usar, verifique se está definida com fboundpe defina uma função de compatibilidade se não estiver definida.

    É uma má idéia atribuir à função de compatibilidade o mesmo nome que a função real, pois outro código Elisp pode estar usando o mesmo fboundptruque. Portanto, use um prefixo para a função de compatibilidade e use defaliaso mesmo nome, se estiver definido. Por exemplo:

    (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 alguma parte da configuração se aplicar apenas a um determinado sistema operacional, existem algumas possibilidades diferentes. Você pode verificar o system-typevariável, que retorna gnu/linux, darwin, windows-nte alguns outros (veja a docstring).

    Você pode ser tentado a usar window-system, apesar de sua doutrina declarar que "O uso dessa variável como booleano está obsoleto" e recomenda o uso display-graphic-p. Observe que o Emacs pode usar diferentes tipos de telas para quadros diferentes atualmente (por exemplo, um quadro em um terminal e outro em uma janela "adequada"), portanto, isso pode causar surpresas. Use current-frame-configurationou get-buffer-window-listem seu elisp para fazer a escolha certa.

  • Convém verificar se você está executando o sabor certo do emacs. Use featurep para verificar a variante. Por exemplo:

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

    Você também pode usá-lo para verificar se módulos específicos estão carregados. Por exemplo, se você usar apenas um pequeno e fácil de definir defun do common-lisp, poderá optar por definir em vez de exigir. Por exemplo:

    (unless (featurep 'cl)
       (defun caaar (x)
          "Return the `car' of the `car' of the `car' of X."
          (car (car (car x)))))
    
  • Evite armazenar arquivos .elc. Eles não são compatíveis com versões anteriores ou anteriores com algumas versões do Emacs.

legoscia
fonte
0

Se você estiver usando funções, cl-libmas não quiser usar as versões descontinuadas sem espaço para nome, torne a biblioteca de compatibilidade cl-lib uma dependência do seu projeto. Isso permitirá que você use as cl-funções com espaço para nome , mas mantenha a compatibilidade com versões anteriores.

shosti
fonte
load-library: Não é possível abrir o arquivo de carregamento: cl-lib - Por que eu iria querer cl-libassim mesmo? Que vantagem tem sobre isso cl, que existe desde pelo menos 20 anos atrás?
Gilles 'SO- stop be evil'
1
clestá obsoleto e provavelmente será removido eventualmente. O uso no código causou avisos de compilador de bytes por um longo tempo. Eu acho que o motivo é que eles querem reutilizar alguns dos clnomes (como dolist) com diferentes semânticas.
Shosti