Как войти в режим только просмотра при просмотре исходного кода Emacs из справки?

10

Когда я просматриваю справку Emacs для функций через C-h f, я часто хочу заглянуть в реализацию Elisp / C. Я хочу ввести view-modeавтоматически при доступе к исходному коду таким образом, чтобы избежать ненужных изменений. Есть ли ловушка или функция, которую я могу посоветовать для достижения этой цели?

rationalrevolt
источник
2
Вот то, что я использую, чтобы предотвратить случайное изменение любых моих файлов, которые открываются в, emacs-lisp-modeи я просто делаю, C-x C-qесли я хочу редактировать исходный код. (defun set-buffer-read-only () (setq buffer-read-only t)) (add-hook 'emacs-lisp-mode-hook 'set-buffer-read-only)
юрист

Ответы:

2

Обновление (после ночного сна): у этого ответа есть большой недостаток: он включается view-modeпри переходе к любой функции, а не только к источникам Emacs. Это можно исправить, но лучше использовать ответ @phils .

Поступая , C-h f describe-function RETа затем читает исходный код describe-functionя обнаружил , что он создает «кнопку» специального типа для ссылок на определения функций: help-function-def.

Запуск zrgrepс этой строкой (" help-function-def") указал мне на help-mode.el.gz.

После всего этого мы можем заменить этот тип кнопки своим собственным (обратите внимание на комментарий в коде):

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

Насколько я могу судить, нет функции для добавления совета: Emacs использует lambdaздесь. С другой стороны (как указано @rationalrevolt ) можно заменить help-functionсвойство типа help-function-defкнопки:

(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))))
Константин
источник
1
Я думаю, что я могу попытаться использовать button-type-getи button-type-putзаменить лямбду своей собственной, которая пересылает к существующей лямбде.
рациональное
@rationalrevolt: Хорошая идея! (Кажется, немного хрупким, но я полагаю, что это тоже хрупко.)
Константин
@rationalrevolt: Пожалуйста, смотрите обновленный ответ. (Кажется, в комментариях не может быть новых строк ...)
Константин
Спасибо! Я пробовал что-то похожее, но будучи новичком в elisp, я был укушен динамическим связыванием и не смог заставить свое закрытие работать :)
рациональное
16

Вы можете использовать локальные переменные каталога, чтобы сделать исходные файлы Emacs доступными по умолчанию только для чтения. (См. Также C-hig (emacs) Directory Variables RET).

Создайте файл с именем .dir-locals.elв корне дерева каталогов, которое вы хотите защитить, со следующим содержимым:

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

Редактировать: Михал Политовски отмечает в комментариях, что включение view-modeтаким образом проблематично, потому что когда вы увольняете буфер с qним, он также отключает режим, что означает, что при следующем посещении этого буфера view-modeне будет включен.

Редактировать 2: Константин предоставил решение этой проблемы в комментариях ниже:

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

Это полезно добавляет тест, чтобы убедиться, что буфер уже посещает файл, но ключевым изменением является использование view-mode-enterвместо view-mode, так как первый принимает EXIT-ACTIONаргумент, который определяет, что делать при qвводе. В этом случае действие выхода - уничтожить буфер, гарантируя, что при следующем посещении файла он снова окажется в view-mode.

Редактировать 3: Следуя этому пути, мы также можем видеть, что указанное EXIT-ACTIONв конечном итоге передается view-mode-exitфункции, и ее строка документации дает нам альтернативное решение:

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.

Следовательно, мы можем использовать следующее:

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

Я использую альтернативный подход, который вы можете полностью указать в вашем файле инициализации (в отличие от создания .dir-locals.elфайла), и я просто делаю файлы только для чтения, а не использую их view-mode. Мой конфиг выглядит так:

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

Очевидно, вы можете сделать то же самое для вашего каталога elpa и любого другого каталога, который содержит сторонний исходный код.

Phils
источник
Большой! Это явно лучше, чем я придумал. (О чем я думал? Я знаю о себе и использую .dir-locals.elсебя ...)
Константин
Я был примерно таким же образом, основываясь на a find-file-hookи read-only-dirsсписке, но мне нравится этот подход.
glucas
Это выглядит как очень чистый подход. У меня есть одна небольшая проблема, хотя. С того, ((nil . ((eval . (view-mode 1)))))что это самый простой способ , чтобы View-quitубить буферы доступны через помощь? В противном случае после выхода из представления источника нажатием кнопки qбуфер остается, и при последующем доступе к источникам из того же файла из справки снова режим просмотра не запускается.
Михал Политовски,
Михал Политовски: Вы правы. Я обновил ответ, чтобы учесть этот факт, но у меня нет обходного пути. Ответ Константина (отредактированный) может быть лучшим решением для использования view-mode.
phils
1
Сегодня я понял, что мне нужен обходной путь для проблемы, указанной @ MichałPolitowski --- и я нашел один: использовать ((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))(обратите внимание, (view-mode-enter ...)вместо (view-mode 1)). Таким образом , нажатие qубивает буфер и view-mode будет включен в следующий раз , когда я посещаю один и тот же файл.
Константин
0

Я думаю, что все, что вам нужно, это добавить крючок :

(add-hook 'find-function-after-hook 'view-mode)
ДСН
источник
Это лучше, чем моя чудовищность, но все же не совсем соответствует вопросу: он включается view-modeпри переходе к любой функции, использующей C-h fне только источники Emacs.
Константин
Экспериментально ссылки справки на самом деле не запускают этот хук. Похоже, что только интерактивные find-THINGкоманды используют этот хук, а кнопки помощи обходят его.
phils
0

Это относится не к вашему конкретному случаю, а к более общему случаю переключения на view-modeвсякий раз, когда вы посещаете исходный файл из буфера справки. Я предлагаю его в качестве альтернативы ответу @ Constantine, поскольку он не был читаемым в качестве комментария.

Похоже, я изначально получил это из 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
источник
0

Вот решение, которое работает со встроенной документацией, и пример, показывающий, как распространить его на ELPA. Он работает путем сопоставления пути к текущему файлу с некоторыми регулярными выражениями и применения, read-only-modeесли какой-либо из них совпадает.

Обратите внимание, что буфер доступен только для чтения, если вы просматриваете его diredтакже, а не только через справку.

Я добавил хук, который запускается после ввода, emacs-lisp-modeкоторый проверяет, совпадает ли путь к файлу /\.el\.gz$/, и применяет режим только для чтения, если это так.

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

Вот пример, который также проверяет ELPA, используя эвристику, согласно которой любой путь, содержащийся .emacs.d/elpaна самом деле, является кодом 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)
Грегори Нисбет
источник