Jak przejść do trybu tylko do przeglądania podczas przeglądania kodu źródłowego Emacsa z pomocy?

10

Kiedy przeglądam pomoc Emacsa dla funkcji C-h f, często chcę zajrzeć do implementacji Elisp / C. Chcę wprowadzić view-modeautomatycznie, gdy uzyskuję dostęp do kodu źródłowego w ten sposób, aby uniknąć niepotrzebnych modyfikacji. Czy jest jakiś haczyk lub funkcja, którą mogę doradzić, aby to osiągnąć?

rationalrevolt
źródło
2
Oto, czego używam, aby zapobiec przypadkowej modyfikacji dowolnego z moich plików, które się otwierają, emacs-lisp-modei robię to, C-x C-qjeśli chcę edytować kod źródłowy. (defun set-buffer-read-only () (setq buffer-read-only t)) (add-hook 'emacs-lisp-mode-hook 'set-buffer-read-only)
prawnik

Odpowiedzi:

2

Aktualizacja (po nocy snu): Ta odpowiedź ma poważną wadę: umożliwia przejście view-modedo dowolnej funkcji, nie tylko źródeł Emacsa. Można to naprawić, ale lepiej użyć odpowiedzi @phils .

Dzięki temu C-h f describe-function RET, a następnie odczytanie kodu źródłowego describe-functionodkryłem, że tworzy „przycisk” specjalnego typu dla linki do definicji funkcji: help-function-def.

Wskazanie na zrgrepten ciąg („ help-function-def”) wskazało mi help-mode.el.gz.

Po całym tym przekopaniu możemy zastąpić ten typ przycisku naszym własnym (zwróć uwagę na komentarz w kodzie):

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

O ile mogę stwierdzić, nie ma funkcji, do której można by dodać porady: Emacs używa lambdatutaj. Z drugiej strony (jak wskazał @rationalrevolt ) można zastąpić help-functionwłaściwość help-function-deftypu przycisku:

(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))))
Konstantyn
źródło
1
Myślę, że mogę spróbować użyć button-type-geti button-type-putzastąpić lambda własną, która przejdzie do istniejącej lambda.
rationalrevolt
@rationalrevolt: Dobry pomysł! (Wydaje się to trochę kruche, ale przypuszczam, że to też jest kruche.)
Constantine,
@rationalrevolt: Zobacz zaktualizowaną odpowiedź. (Wygląda na to, że w komentarzach nie ma nowych linii ...)
Constantine,
Dzięki! Próbowałem czegoś podobnego, ale będąc nowicjuszem, który wyszedł, zostałem ugryziony przez dynamiczne wiązanie i nie mogłem dostać zamknięcia do pracy :)
rationalrevolt
16

Możesz użyć zmiennych lokalnych katalogu, aby pliki źródłowe Emacsa były domyślnie tylko do odczytu. (Zobacz także C-hig (emacs) Directory Variables RET).

Utwórz plik o nazwie .dir-locals.elw katalogu głównym drzewa katalogów, który chcesz chronić, z następującą zawartością:

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

Edycja: Michał Politowski wskazuje w komentarzach, że włączenie view-modew ten sposób jest problematyczne, ponieważ po zwolnieniu bufora z qnim wyłącza również tryb, co oznacza, że ​​przy następnej wizycie bufor view-modenie zostanie włączony.

Edycja 2: Constantine podał rozwiązanie tego problemu w komentarzach poniżej:

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

Przydatnie dodaje to test, aby upewnić się, że bufor już odwiedza plik, ale kluczową zmianą jest użycie view-mode-enterzamiast view-mode, ponieważ ten pierwszy przyjmuje EXIT-ACTIONargument, który określa, co zrobić, gdy qzostanie wpisany. W takim przypadku czynnością wyjścia jest zabicie bufora, zapewniając, że przy następnej wizycie plik ponownie się skończy view-mode.

Edycja 3: Podążając tą ścieżką, możemy również zobaczyć, że określony EXIT-ACTIONjest ostatecznie przekazywany do view-mode-exitfunkcji, a jego dokumentacja daje nam alternatywne rozwiązanie:

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.

Dlatego możemy użyć następujących elementów:

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

Korzystam z alternatywnego podejścia, które możesz określić w całości w pliku init (w przeciwieństwie do tworzenia .dir-locals.elpliku), i po prostu zmieniam pliki na tylko do odczytu, zamiast ich używać view-mode. Moja konfiguracja wygląda następująco:

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

Oczywiście możesz zrobić to samo dla swojego katalogu elpa i każdego innego katalogu zawierającego kod źródłowy innej firmy.

phils
źródło
Świetny! To wyraźnie lepsze niż to, co wymyśliłem. (O czym myślałem? Wiem o .dir-locals.elsobie i używam ...)
Constantine,
Byłem coś w tym samym stylu na podstawie a find-file-hooki read-only-dirslisty, ale podoba mi się to podejście.
glucas
To wygląda na bardzo czyste podejście. Mam jednak jeden drobny problem. Jak ((nil . ((eval . (view-mode 1)))))najprościej View-quitzabić bufory dostępne za pośrednictwem pomocy? W przeciwnym razie po wyjściu z widoku źródłowego przez naciśnięcie qbufor pozostaje w tyle, a późniejszy dostęp do źródeł z tego samego pliku z pomocy ponownie nie powoduje uruchomienia trybu przeglądania.
Michał Politowski,
Michał Politowski: Masz rację. Zaktualizowałem odpowiedź, aby uwzględnić ten fakt, ale nie mam obejścia tego problemu. Odpowiedź (zredagowana) Constantine'a może być najlepszym rozwiązaniem view-mode.
phils
1
Dzisiaj doszedłem do wniosku, że potrzebuję obejścia problemu wskazanego przez @ MichałPolitowski --- i znalazłem jeden: use ((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))(uwaga (view-mode-enter ...)zamiast (view-mode 1)). W ten sposób naciśnięcie qzabija bufor i view-mode jest włączane przy następnej wizycie tego samego pliku.
Constantine
0

Myślę, że wystarczy dodać hak :

(add-hook 'find-function-after-hook 'view-mode)
sds
źródło
Jest to lepsze niż moja potworność, ale tak naprawdę nie pasuje do pytania: włącza się view-modepodczas nawigacji do dowolnej funkcji przy użyciu C-h f, nie tylko źródeł Emacsa.
Constantine,
Eksperymentalnie linki pomocy nie uruchamiają tego haka. Wygląda na to, że tylko interaktywne find-THINGpolecenia wykorzystują ten zaczep, a przyciski pomocy omijają go.
phils
0

Nie dotyczy to konkretnego przypadku, ale bardziej ogólny przypadek przełączania się view-modeza każdym razem, gdy odwiedzasz plik źródłowy z bufora pomocy. Oferuję ją jako alternatywę dla odpowiedzi @ Constantine, ponieważ nie można jej było odczytać jako komentarza.

Wygląda na to, że pierwotnie dostałem to z 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
źródło
0

Oto rozwiązanie, które działa w przypadku wbudowanej dokumentacji i przykład pokazujący, jak rozszerzyć ją na ELPA. Działa poprzez dopasowanie ścieżki do bieżącego pliku do niektórych wyrażeń regularnych i zastosowanie, read-only-modejeśli którykolwiek z nich jest zgodny.

Pamiętaj, że bufor jest tylko do odczytu, jeśli go odwiedzasz, direda nie tylko poprzez pomoc.

Dodałem hak, który uruchamia się po wejściu, emacs-lisp-modektóry sprawdza, czy ścieżka do pliku pasuje /\.el\.gz$/, i stosuje tryb tylko do odczytu, jeśli tak jest.

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

Oto przykład, który sprawdza również ELPA, używając heurystyki, że każda ścieżka zawierająca .emacs.d/elpajest w rzeczywistości kodem 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
źródło