Como faço para corrigir um pacote Emacs?

16

Desejo alterar um pacote, testá-lo e, esperamos, enviar uma solicitação de recebimento posteriormente. Como faço isso de maneira segura e eficiente? A pergunta pode parecer muito ampla. Aceitarei a resposta que cobre os seguintes problemas:

  1. Eu esperaria instalar um ramo separado de um pacote e poder alternar entre ele e o ramo estável por capricho, com a recompilação realizada automaticamente quando for necessário, mas package.elnão parece oferecer uma maneira direta de fazer isso. Esta resposta no emacs-SE nos informa que “Se várias cópias de um pacote estiverem instaladas, a primeira será carregada”, então acho que se pode mexer manualmente, load-pathmas isso não parece robusto. Qual é a maneira padrão de selecionar uma versão específica do pacote entre as instaladas?

  2. Mesmo que eu consiga expor várias ramificações ao Emacs, para ajustes significativos, preciso garantir que a ramificação sem patch seja “descarregada” e seus efeitos colaterais isolados. Lida unload-featurecom isso adequadamente ou talvez tenha idiossincrasias que todo testador de pacotes com várias versões deve conhecer?

  3. Como instalo e testo a versão local? A resposta parece estar dependente de o pacote ser simples (= um arquivo) ou com vários arquivos. O EmacsWiki diz sobre pacotes com vários arquivos : " MELPA cria pacotes para você ". Duvido que precise (ou deva) conversar com o MELPA toda vez que altero um defunformulário em um pacote com vários arquivos, mas a questão permanece. Pelo menos eu preciso informar ao gerenciador de pacotes sobre a versão local e, em caso afirmativo, como faço?

  4. Quais nomes devo atribuir às versões locais dos pacotes? Suponha que eu queira trabalhar em vários recursos ou bugs simultaneamente, o que significa ter várias ramificações. O Emacs não permitirá nomear versões de maneira descritiva (na linha de 20170117.666-somebugorfeature). Acho que poderia renomear o pacote em si, um sufixo por ramificação, mas, novamente, como mexer manualmente load-pathno primeiro trimestre, esse é um truque feio, então não vou tentar com algo que pretendo enviar upstream, a menos que seja uma prática amplamente aceita .

As perguntas provavelmente são ingênuas, pois nunca escrevi um patch nem o apliquei com git ou com vcs similares. No entanto, para muitos usuários do Emacs, a correção de um pacote do Emacs pode ser o primeiro (ou talvez o único) empreendimento de programação social, e é por isso que acredito que as respostas a essa pergunta ainda sejam valiosas.

akater
fonte

Respostas:

7

Para entrar em sintonia com um fluxo de trabalho ligeiramente diferente para carregar versões diferentes de pacotes, eis algumas variações do que eu faço, que usam o load-pathpara controlar qual versão estou usando (alterar o nome do pacote é uma má ideia se houver dependências). Eu tenho a versão atual do "nice-package" instalada no ~/.emacs.d/elpauso M-x package-install, e o repositório de pacotes clonado ~/src/nice-packagecom git clone ....

Com pacote de uso

No init.el, eu tenho

(use-package nice-package
  :load-path "~/src/nice-package"
  ...)

Com a :load-pathlinha descomentada, isso usará a versão git do pacote. Comentar esta linha e recarregar o emacs usa a versão elpa.

Semelhante sem pacote de uso

No init.el,

(add-to-list 'load-path "~/src/nice-package")
(require 'nice-package)
...

Agora faça o mesmo truque de comentário com a primeira linha.

Usando emacs -L

Essa é a mesma idéia, mas manipulando a load-pathpartir da linha de comando. Você pode carregar uma instância do emacs com a versão git do pacote com

emacs -L ~/src/nice-package

que apenas anexa esse caminho à frente do caminho de carregamento. Dessa forma, você pode iniciar a emacspartir de um terminal diferente e colocar as versões antiga e nova do pacote rodando lado a lado.

Comentários variados

  1. Use M-x eval-bufferapós editar um arquivo de pacote para carregar as novas definições que você criou.
  2. Verificar o que o compilador diz com também M-x emacs-lisp-byte-compileé útil
justbur
fonte
Estou usando a emacs -Labordagem para carregar uma versão local de um pacote que também instalei globalmente usando o Cask. Uma coisa que me impressionou foi que a execução <package>-versionsempre retorna a versão globalmente instalada, mesmo quando eu estava realmente executando a versão modificada local. Acontece que isso ocorreu porque o <package>-versionpara este pacote obtém a versão packages.el.
Ntc2 02/06/19
3

Boa pergunta! A resposta é que, até agora, não havia uma boa resposta, pois nenhum dos gerenciadores de pacotes existentes foi projetado para este caso de uso (exceto o Borg , mas o Borg não tenta lidar com outras operações comuns de gerenciamento de pacotes, como o tratamento de dependências) .

Mas agora existe straight.elum gerenciador de pacotes de última geração para o Emacs que resolve esse problema da maneira mais abrangente possível. Disclaimer: eu escrevi straight.el!

Após inserir o snippet de auto - inicialização , a instalação de um pacote é tão simples quanto

(straight-use-package 'magit)

Isso clonará o repositório Git para Magit, compilará o pacote simbolizando seus arquivos em um diretório separado, compilará bytes, gerará e avaliará carregamentos automáticos e configurará load-pathcorretamente. Obviamente, se o pacote já estiver clonado e construído, nada acontecerá e o seu tempo de inicialização não sofrerá.

Como você faz alterações no Magit? É trivial! Basta usar M-x find-functionou M-x find-librarypara pular para o código fonte e cortar! Você pode avaliar suas alterações para testá-las ao vivo, como é prática geral para o desenvolvimento do Emacs Lisp, e quando você reiniciar o Emacs, o pacote será reconstruído automaticamente, recompilado e assim por diante. É completamente automático e infalível.

Quando estiver satisfeito com suas alterações, basta confirmar, enviar e fazer uma solicitação de recebimento. Você tem controle total sobre seus pacotes locais. Mas sua configuração ainda pode ser 100% reproduzível, porque você pode pedir straight.elpara criar um arquivo de bloqueio que salve as revisões do Git de todos os seus pacotes, incluindo straight.elele próprio, MELPA e assim por diante.

straight.elpode instalar qualquer pacote do MELPA, GNU ELPA ou EmacsMirror. Mas também possui uma receita DSL altamente flexível que permite instalar de qualquer lugar e personalizar como o pacote é construído. Aqui está um exemplo que mostra algumas das opções:

(straight-use-package
 '(magit :type git 
         :files ("lisp/magit*.el" "lisp/git-rebase.el"
                 "Documentation/magit.texi" "Documentation/AUTHORS.md"
                 "COPYING" (:exclude "lisp/magit-popup.el"))
         :host github :repo "raxod502/magit"
         :upstream (:host github :repo "magit/magit")))

straight.elpossui documentação ridiculamente abrangente. Leia tudo sobre isso no GitHub .

Radon Rosborough
fonte
2

Essas são todas boas perguntas!

O Emacs trabalha em um modelo de imagem de memória, onde o carregamento de um novo código altera a imagem de memória da instância em execução. A definição de novas funções e variáveis ​​é facilmente desfeita, se você mantiver uma lista delas, mas existem muitos efeitos colaterais que um módulo pode ter que você deseja desfazer. Parece que unload-featurefaz uma boa parte disso.

Eu acho que o que você vai querer fazer é uma combinação de codificação ao vivo e, ocasionalmente, o relançamento do Emacs, carregando o módulo no qual você está trabalhando a partir da sua filial e não de onde está instalado. Se você acabar com muitas dessas ramificações, poderá querer um script de shell que inicie o emacs com a correta load-pathpara a que você está trabalhando no momento. De qualquer forma, eu não mudaria o nome do pacote; Eu acho que seria ainda mais confuso, já que o emacs poderia carregar os dois.

À medida que você desenvolve seus patches, você pode simplesmente redefinir as funções que estão mudando na sua sessão ao vivo do Emacs. Isso permite testar as novas definições imediatamente, sem sair do Emacs. Especificamente, ao editar um arquivo elisp, você pode usar C-M-x( eval-defun) para avaliar a função atual na sua sessão atual do Emacs. Você pode chamá-lo para garantir que funcione. Se você estiver alterando algo que acontece na inicialização do Emacs, provavelmente precisará iniciar e parar o Emacs para testá-lo; você pode fazer isso iniciando e interrompendo um processo separado do Emacs para que sua sessão de edição não seja interrompida.

db48x
fonte
2

Ainda não acho que haja uma boa resposta para isso (espero que você possa obter uma solução parcial com Cask, embora eu não esteja suficientemente familiarizado com ela para lhe dar uma boa resposta; espero que alguém o faça), mas aqui está o que faço (raramente uso um pacote Elisp sem fazer alterações locais, por isso é realmente o meu jeito "normal"):

  • cd ~/src; git clone ..../elpa.git
  • para cada pacote cd ~/src/elisp; git clone ....thepackage.git
  • cd ~/src/elpa/packages; ln -s ~/src/elisp/* .
  • cd ~/src/elpa; make
  • no seu ~/.emacsadd

    (eval-after-load 'package
     '(add-to-list 'package-directory-list
                   "~/src/elpa/packages"))
    

Dessa forma, todos os pacotes são instalados "diretamente do Git", um simples cd ~/src/elpa; makerecompilará os que precisam e C-h o thepackage-functionsaltará para um arquivo de origem que esteja no Git.

Para "alternar entre ele e o ramo estável por um capricho", você precisará git checkout <branch>; cd ~/src/elpa; make; e se você deseja que isso afete a execução das sessões do Emacs, será mais trabalhoso. Eu geralmente recomendo não usar, unload-featureexceto em situações excepcionais (é um bom recurso, mas atualmente não é confiável o suficiente).

Também não atende a muitos de seus requisitos. E tem algumas desvantagens extras, principalmente o fato de que o clone Git de muitos pacotes não corresponde exatamente ao layout e ao conteúdo esperado pelo makefile do elpa.git, portanto, você precisará começar ajustando esses pacotes (normalmente coisas que têm a ver com <pkg>-pkg.el, uma vez que o makefile do elpa.git espera compilar esse arquivo em <pkg>.elvez de fornecê- lo, mas o mais problemático é que a compilação é executada de maneira diferente; portanto, às vezes você precisa brincar com os arquivos require.

Ah, é claro, isso basicamente significa que você está instalando esses pacotes manualmente, então você deve prestar atenção às dependências. Essa configuração interage adequadamente com outros pacotes instalados por package-install, tho, por isso não é tão terrível.

Stefan
fonte
2

As outras respostas a esta pergunta, incluindo minha outra resposta , falam sobre o patch de um pacote Emacs, fazendo alterações em seu código. Mas as pessoas que encontram essa pergunta pelo Google podem pensar em outra coisa quando dizem "corrigir um pacote Emacs" - ou seja, substituindo seu comportamento sem precisar modificar o código-fonte.

Os mecanismos para fazer isso incluem, em ordem crescente de agressividade:

Apesar do poder das duas primeiras opções, eu me peguei fazendo a terceira rota com bastante frequência, já que às vezes não há outra maneira. Mas então a questão é: e se a definição da função original mudar? Você não teria como saber que precisava atualizar a versão dessa definição que copiou e colou no seu arquivo init!

Como sou obcecado em consertar as coisas, escrevi o pacote el-patch, que resolve esse problema da maneira mais abrangente possível. A idéia é que você defina diferenças baseadas em expressão s no seu arquivo init, que descreve a definição da função original e suas alterações. Isso torna seus patches muito mais legíveis e também permite el-patchvalidar posteriormente se a definição da função original foi atualizada desde que você fez o patch. (Se sim, ele mostrará as alterações via Ediff!) Citando a partir da documentação:

Considere a seguinte função definida no company-statisticspacote:

(defun company-statistics--load ()
  "Restore statistics."
  (load company-statistics-file 'noerror nil 'nosuffix))

Suponha que desejemos alterar o terceiro argumento de nilpara 'nomessage, para suprimir a mensagem registrada ao company-statisticscarregar seu arquivo de estatísticas. Podemos fazer isso colocando o seguinte código em nosso init.el:

(el-patch-defun company-statistics--load ()
  "Restore statistics."
  (load company-statistics-file 'noerror
        (el-patch-swap nil 'nomessage)
        'nosuffix))

Simplesmente chamar, em el-patch-defunvez de defundefinir um patch não operacional: isto é, não tem efeito (bem, não exatamente - veja mais adiante ). No entanto, ao incluir diretivas de correção , você pode tornar a versão modificada da função diferente da original.

Nesse caso, usamos a el-patch-swapdiretiva. O el-patch-swapformulário é substituído nilpela definição original (ou seja, a versão comparada com a definição "oficial" em company-statistics.el) e 'nomessagepela definição modificada (ou seja, a versão que é realmente avaliada em seu arquivo init).

Radon Rosborough
fonte
0

Quando você faz muitas alterações, acho que deveria usar straight.el, veja a resposta de Radon Rosborough .

Se você quiser apenas fazer uma alteração única, vamos assumir um projeto chamado fork-mode, execute as seguintes etapas:

  • Crie um diretório para armazenar o git mkdir ~/.emacs.d/lisp-gits
  • Faça uma bifurcação do projeto que você deseja alterar, digamos em https://github.com/user/fork-mode
  • Clone seu garfo cd ~/.emacs.d/lisp-gits && git clone [email protected]:user/fork-mode.git

Escreva o seguinte código no seu .emacs

(if (file-exists-p "~/.emacs.d/lisp-gits/fork-mode")
    (use-package fork-mode :load-path "~/.emacs.d/lisp-gits/fork-mode")
  (use-package fork-mode :ensure t))

(use-package fork-mode
  :config
  (setq fork-mode-setting t)
  :hook
  ((fork-mode . (lambda () (message "Inside hook"))))

Agora você pode usar o modo emacs, usando C-h fpara encontrar as funções que deseja alterar. Você notará que quando o pacote estiver instalado no lisp-gits, você irá para lá. Use magit ou outros comandos git para confirmar / enviar alterações e, em seguida, use o github para enviar suas solicitações de recebimento.

Depois que suas solicitações de recebimento forem aceitas, você poderá remover o projeto ~/.emacs.d/lisp-gitse deixar o gerente de pacotes fazer seu trabalho.

ppareit
fonte