Como lidar normalmente com erros no arquivo init

20

Eu gostaria de uma maneira de capturar erros ao executar meu arquivo init e, em seguida, manipulá-los normalmente. Muitas das minhas personalizações e combinações de teclas mais importantes aparecem no final do meu arquivo init para garantir que outras configurações não sejam aplicadas por cima delas. O problema é que, quando a inicialização é interrompida mais cedo, sinto-me totalmente incapacitado ao tentar depurar o problema sem que minhas ligações e configurações familiares sejam aplicadas.

Existe alguma maneira de concluir normalmente o processo de inicialização quando ocorrer um erro?

nispio
fonte

Respostas:

9

Duas opções, nenhuma das quais são perfeitas, vêm à mente. Primeiro, você pode agrupar a maior parte do seu código de inicialização inicial (ou seja, antes que ele atinja suas personalizações) (ignore-errors ...). Se não são erros, no entanto, não haverá um monte de feedback - ignore-errorssimplesmente retornará nil.

Uma opção mais complexa seria agrupar o código potencialmente incorreto em uma combinação de unwind-protecte with-demoted-errors(com debug-on-errordefinido como nulo). O último erro ocorre normalmente no primeiro erro encontrado e relata a mensagem de erro ao *Messages*buffer para sua inspeção. Enquanto isso, o restante do unwind-protectcorpo (presumivelmente suas personalizações) será avaliado. Então, por exemplo:

(unwind-protect
    (let ((debug-on-error nil))
      (with-demoted-errors
        (message "The world is about to end")
        (sleep-for 2)
        (/ 10 0)                        ; divide by zero signals an error
        (message "This will never evaluate")
        (sleep-for 2)
        (setq some-var 5)))
  (message "Here are the unwind forms that will always evaluate")
  (sleep-for 2)
  (setq some-var 10)
  (setq another-var "puppies")
  (message "All done!"))
Dan
fonte
1
Legal, eu não fiz with-demoted-errors. Você pode adicionar um argumento de string a ele "LOOK OVER HERE!!! %s", para ter menos chances de perder o erro no buffer de mensagens.
Malabarba
@Malabarba Este formulário with-demoted-errorsestá disponível apenas em 24.4
lunaryorn 4/04
@ Lunaryorn Obrigado, não sabia disso.
Malabarba
Na verdade, a versão em que estou é 24.3.1.
Dan
8

@Dan descreveu bem como você pode transformar erros em mensagens. Você também pode fazer o que quiser com erros usando condition-case. Ainda outra opção é usar unwind-protect.

Vou ficar condition-caseaqui, sem motivo algum.

Captura do erro

Isso sempre deve garantir que suas principais definições sejam avaliadas, independentemente do que aconteceu lá dentro condition-case. Qualquer erro é armazenado dentro init-error.

(defvar init-error nil 
  "The error which happened.")

(condition-case the-error
    (progn
      ;; Do the dangerous stuff here.
      (require 'what-I-want))
  (error
   ;; This is only evaluated if there's an error.
   (setq init-error the-error)))

;;; Do the safe stuff here.
(define-key uncrippling-map "\C-h" 'help!)

Jogando de volta

Depois, basta lançar o erro novamente. Existem várias maneiras de fazer isso, aqui está uma.

;;; Throw the error again here.
(when init-error
  (funcall #'signal (car init-error) (cdr init-error)))
Malabarba
fonte
unwind-protectfaz com que o erro seja imediatamente gerado novamente, após a execução de qualquer código que você inserir na cláusula de resgate. É como finallyem uma linguagem como Java, em vez de catch.
Sanityinc
2

As outras respostas cobriram muito bem os recursos de tratamento de erros de baixo nível que serão úteis em um caso como esse. Outra abordagem que pode ajudar é a modularidade. Por exemplo, divido meu arquivo de inicialização em vários arquivos diferentes (usando provideconforme apropriado) e carrego-os usando esta função em vez de require:

(defun my/require-softly (feature &optional filename)
  "As `require', but instead of an error just print a message.

If there is an error, its message will be included in the message
printed.

Like `require', the return value will be FEATURE if the load was
successful (or unnecessary) and nil if not."
  (condition-case err
      (require feature filename) 
    (error (message "Error loading %s: \"%s\""
                    (if filename (format "%s (%s)" feature filename) feature)
                    (error-message-string err))
           nil)))

Um erro ao carregar um arquivo dessa maneira ainda imprimirá uma mensagem, mas não impedirá a execução de nada fora do arquivo em que o erro realmente ocorreu.

Obviamente, essa função não é tão diferente de encerrar uma requirechamada with-demoted-errors(eu a escrevi antes de conhecer with-demoted-errors), mas o ponto importante é que você pode implementar algo como a combinação de Dan with-demoted-errorse unwind-protectsem quebra de Dan (potencialmente muito longa) blocos de código.

Aaron Harris
fonte
Essa função era exatamente o que eu procurava. Meu emacs inicializa agora apesar de relatar um erro. Depois disso, eu apenas carrego meu arquivo init e eval-buffer. Obrigado por publicá-lo.
Kevin