Como entrar no modo somente visualização ao navegar pelo código-fonte do Emacs a partir da ajuda?

10

Quando eu navego na ajuda do Emacs em busca de funções C-h f, geralmente quero espiar a implementação do Elisp / C. Quero entrar view-modeautomaticamente quando acessar o código-fonte dessa maneira para evitar modificações desnecessárias. Existe um gancho ou função que eu possa aconselhar para fazer isso?

rationalrevolt
fonte
2
Aqui está o que eu uso para impedir a modificação acidental de qualquer um dos meus arquivos que se abrem emacs-lisp-modee o faço C-x C-qse quiser editar o código-fonte. (defun set-buffer-read-only () (setq buffer-read-only t)) (add-hook 'emacs-lisp-mode-hook 'set-buffer-read-only)
lawlist

Respostas:

2

Atualização (após uma noite de sono): Esta resposta tem uma falha importante: permite view-modenavegar em qualquer função, não apenas nas fontes do Emacs. Isso pode ser corrigido, mas é melhor usar a resposta de @phils .

Ao fazer C-h f describe-function RETe depois de ler o código fonte do describe-functionque eu descobri que ele cria um "botão" de um tipo especial de links para definições de funções: help-function-def.

A execução zrgrepcom esta string (" help-function-def") me indicou help-mode.el.gz.

Depois de toda essa escavação, podemos substituir esse tipo de botão pelo nosso (observe o comentário no código):

(define-button-type 'help-function-def
  :supertype 'help-xref
  'help-function (lambda (fun file)
               (require 'find-func)
               (when (eq file 'C-source)
                 (setq file
                       (help-C-file-name (indirect-function fun) 'fun)))
               ;; Don't use find-function-noselect because it follows
               ;; aliases (which fails for built-in functions).
               (let ((location
                      (find-function-search-for-symbol fun nil file)))
                 (pop-to-buffer (car location))
                 (if (cdr location)
                     (goto-char (cdr location))
                   (message "Unable to find location in file")))
                   (view-mode t)) ; <= new line: enable view-mode
  'help-echo (purecopy "mouse-2, RET: find function's definition"))

Até onde eu sei, não há nenhuma função para adicionar conselhos: o Emacs usa um lambdaaqui. Por outro lado (como indicado por @rationalrevolt ), pode-se substituir a help-functionpropriedade do help-function-deftipo de botão:

(require 'help-mode)
(let ((help-func (button-type-get 'help-function-def 'help-function)))
  (button-type-put 'help-function-def 'help-function
                   `(lambda (func file)
                      (funcall ,help-func func file) (view-mode t))))
Constantine
fonte
11
Acho que posso tentar usar button-type-gete button-type-putsubstituir o lambda pelo meu que encaminha para o lambda existente.
rationalrevolt
@rationalrevolt: Boa ideia! (Parece um pouco frágil, mas suponho que isso também seja frágil.)
Constantine
@rationalrevolt: Por favor, veja a resposta atualizada. (Não pode ter novas linhas em comentários, parece ...)
Constantine
Obrigado! Eu estava tentando algo semelhante, mas sendo um novato para Elisp I foi mordido pela ligação dinâmica e não poderia obter o meu fechamento ao trabalho :)
rationalrevolt
16

Você pode usar variáveis ​​de diretório local para tornar os arquivos de origem do Emacs somente leitura por padrão. (Veja também C-hig (emacs) Directory Variables RET).

Crie um arquivo chamado .dir-locals.elna raiz da árvore de diretórios que você deseja proteger, com o seguinte conteúdo:

((nil . ((eval . (view-mode 1)))))

Edit: Michał Politowski aponta nos comentários que ativar view-modedessa maneira é problemático, porque quando você dispensa o buffer com qele também desativa o modo, o que significa que na próxima vez que você visitar esse buffer view-modenão será ativado.

Edit 2: Constantine forneceu uma solução para esse problema nos comentários abaixo:

((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))

Isso utilmente adiciona um teste para garantir que o buffer já esteja visitando um arquivo, mas a principal mudança é o uso de view-mode-entervez de view-mode, pois o primeiro aceita um EXIT-ACTIONargumento que determina o que fazer quando qé digitado. Nesse caso, a ação de saída é eliminar o buffer, garantindo que na próxima vez que o arquivo seja visitado, ele acabe novamente view-mode.

Edit 3: Seguindo esse caminho, também podemos ver que o especificado EXIT-ACTIONé passado para a view-mode-exitfunção e sua doutrina nos fornece uma solução alternativa:

view-no-disable-on-exit is a variable defined in `view.el'.
Its value is nil

Documentation:
If non-nil, View mode "exit" commands don't actually disable View mode.
Instead, these commands just switch buffers or windows.
This is set in certain buffers by specialized features such as help commands
that use View mode automatically.

Portanto, podemos usar o seguinte:

((nil . ((eval . (when buffer-file-name
                   (setq-local view-no-disable-on-exit t)
                   (view-mode-enter))))))

Eu uso a abordagem alternativa que você pode especificar inteiramente no seu arquivo init (em vez de criar um .dir-locals.elarquivo), e simplesmente os faço como somente leitura, em vez de usar view-mode. Minha configuração fica assim:

;; Emacs
(dir-locals-set-class-variables
 'emacs
 '((nil . ((buffer-read-only . t)
           (show-trailing-whitespace . nil)
           (tab-width . 8)
           (eval . (whitespace-mode -1))))))

(dir-locals-set-directory-class "/usr/local/src/emacs" 'emacs)
(dir-locals-set-directory-class "/usr/local/share/emacs" 'emacs)
(dir-locals-set-directory-class "/usr/share/emacs" 'emacs)

Obviamente, você pode fazer o mesmo pelo diretório elpa e por qualquer outro diretório que contenha código fonte de terceiros.

phils
fonte
Ótimo! Isso é claramente melhor do que eu inventei. (No que eu estava pensando? Eu conheço e .dir-locals.elme uso ...)
Constantine
Eu tenho algo parecido com as mesmas linhas, com base em uma find-file-hooke uma read-only-dirslista, mas eu gosto dessa abordagem.
glucas
Parece uma abordagem muito limpa. Eu tenho um pequeno problema embora. Com ((nil . ((eval . (view-mode 1)))))qual é a maneira mais simples de View-quitmatar os buffers acessados ​​via ajuda? Caso contrário, depois de sair da visualização de origem pressionando q, o buffer permanece para trás e, quando acessa fontes do mesmo arquivo posteriormente a partir da ajuda novamente, o modo de exibição não é iniciado.
Michał Politowski
Michał Politowski: Você está certo. Atualizei a resposta para incorporar esse fato, mas não tenho uma solução alternativa. A resposta de Constantine (editada) pode ser a melhor solução para o uso view-mode.
Phs #
11
Hoje eu descobri que precisava de uma solução alternativa para o problema apontado por @ MichałPolitowski --- e encontrei um: use ((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))(note em (view-mode-enter ...)vez de (view-mode 1)). Dessa forma, pressionar qmata o buffer e view-mode é ativado na próxima vez que visito o mesmo arquivo.
Constantine
0

Eu acho que tudo que você precisa é adicionar um gancho :

(add-hook 'find-function-after-hook 'view-mode)
sds
fonte
Isso é melhor do que minha monstruosidade, mas ainda não corresponde à pergunta: ela é ativada view-modeao navegar para qualquer função usando C-h f, não apenas as fontes do Emacs.
Constantine
Experimentalmente, os links de ajuda não funcionam de verdade. Parece que apenas os find-THINGcomandos interativos utilizam esse gancho e os botões de ajuda o ignoram.
phils
0

Isso não aborda seu caso específico, mas o caso mais geral de alternar para view-modesempre que você visita um arquivo de origem a partir de um buffer de ajuda. Estou oferecendo isso como uma alternativa à resposta de @ Constantine, já que não era legível como comentário.

Parece que recebi isso originalmente do EmacsWiki .

(defadvice find-function-search-for-symbol (after view-function-source last (symbol type library) activate)
  "When visiting function source via Help, switch to view-mode"
  (with-current-buffer (car ad-return-value)
    (view-mode 1)))

(defadvice find-variable-noselect (after view-var-source last (variable &optional file) activate)
  "When visiting variable source via Help, switch to view-mode"
  (with-current-buffer (car ad-return-value)
    (view-mode 1)))
glucas
fonte
0

Aqui está uma solução que funciona para documentação interna e um exemplo que mostra como estendê-la ao ELPA. Ele funciona combinando o caminho para o arquivo atual com algumas expressões regulares e aplicando read-only-modese alguma delas corresponder.

Observe que o buffer é somente leitura se você o visitar diredtambém, não apenas pela ajuda.

Adicionei um gancho que é executado depois de entrar para emacs-lisp-modeverificar se o caminho para o arquivo corresponde /\.el\.gz$/e se aplica o modo somente leitura.

(defun readonly-if-el-gz ()
  (cond
   ((string-match "\\.el\\.gz\\'" (or (buffer-file-name) ""))
    (read-only-mode +1))))

(add-hook 'emacs-lisp-mode-hook 'readonly-if-el-gz)

Aqui está um exemplo que verifica o ELPA também, usando a heurística de que qualquer caminho que contenha .emacs.d/elpaé realmente código ELPA.

(defun readonly-if-internal ()
  (let
      ((name (or (buffer-file-name) "")))
    (cond
     ((string-match "\\.el\\.gz\\'" name) (read-only-mode +1))
     ((string-match "\\.emacs\\.d/elpa" name) (read-only-mode +1)))))

(add-hook 'emacs-lisp-mode-hook 'readonly-if-internal)
Gregory Nisbet
fonte