буфер-локальная функция в elisp

#function #emacs #elisp #buffer #local

#функция #emacs #elisp #буфер #Местные новости

Вопрос:

Я хотел бы переопределить существующую функцию foo , но только для определенного буфера.

 (defun foo ()
  (message "Not done:("))
  

Я думал, что это подойдет:

 (make-local-variable 'foo)
(fset 'foo #'(lambda () (message "Done!")))
  

Но это не так.
Есть идеи?

[РЕДАКТИРОВАТЬ: Альтернативно, поскольку функция ограничена ключом, было бы достаточно изменить привязку только для текущего буфера. Но я не вижу, как это сделать. Локальная карта ключей является общей для всех буферов в основном режиме. Ее изменение изменяет привязки во всех буферах с этим основным режимом.

Единственное уродливое решение, о котором я могу подумать, — это установить текстовое свойство ключевой карты для всего буфера. Это единственный способ продолжить?]

Комментарии:

1. Какую проблему вы на самом деле пытаетесь решить? Возможно, есть другой подход, который работал бы лучше. Как вы узнаете, когда вы хотите «Готово!» и «Не сделано:(«?

2. @Trey Справочный режим пакета имеет функцию навигации (переход к разделам). Она короткая и элегантная. Но для одного очень специфического типа файла справки мне нужно другое поведение. Вместо того, чтобы встраивать функцию в функцию, я надеялся переопределить ее локально. Ваш ответ на самом деле делает то, что я хочу.

Ответ №1:

Вы можете создать функцию, которая выполняет переопределение для вас, что-то вроде этого:

 (defun override-the-keymap ()
  (let ((my-overriding-keymap (make-sparse-keymap)))
(set-keymap-parent my-overriding-keymap (current-local-map))
(use-local-map my-overriding-keymap)
(define-key my-overriding-keymap (kbd "C-M-x") 
      '(lambda () (interactive) (message "Done!")))))
  

Очевидно, что необходимо соответствующим образом настроить привязку ключа. Это действует только в текущем буфере.

Комментарии:

1. Это делает свое дело, и я пойду на это, но в ответе @desudesudesu рассматривается первоначальный вопрос о создании функций buffer-local, который имеет немного теоретическую сторону. Таким образом, я принимаю его ответ.

Ответ №2:

Свойства значения и функции данного символа являются отдельными, и поэтому, по-видимому, make-local-variable будут влиять только на значение, тогда как fset работает со свойством функции.

Вероятно, вам лучше более подробно описать, что именно вы хотите сделать, но одним из общих решений было бы использовать «обходной совет», чтобы обернуть исходную функцию вашим собственным кодом.

 (defadvice foo (around my-foo-wrapper)
  (if (not (and (boundp 'use-my-foo) 'use-my-foo))
      ad-do-it
    (message "Not done:(")))
(ad-activate 'foo)

;; in special buffer
(set (make-local-variable 'use-my-foo) t)
  

РЕДАКТИРОВАТЬ: (относительно дополнительных комментариев к карте ключей)

Возможно, тогда вы захотите определить второстепенный режим для использования в вашем специальном буфере. Карты ключей второстепенного режима имеют приоритет над картами ключей основного режима, поэтому вам просто нужно будет определить ту же привязку в карте второстепенного режима. Смотрите define-minor-mode .

Комментарии:

1. Это для моего пакета, поэтому я могу изменять функцию напрямую, а не советовать ее. Проблема в том, что для одного очень специфического буфера мне нужна другая функциональность, и я не хочу отключать функцию только для этого конкретного случая. Это интерактивная функция, поэтому локальное изменение карты было бы достаточно. Но я тоже не знаю, как это сделать. Отредактировал Q.

Ответ №3:

Как насчет записи ваших символьных функций в символьные значения, а затем оценки их с помощью какой-либо другой функции?

 (defvar my-buffer-local-function
  (lambda ()
    (interactive)
    (message "Default message"))
  "This variable contains buffer local function")

(make-variable-buffer-local 'my-buffer-local-function)

(defun run-my-buffer-local-function (amp;rest args)
  "This function run buffer-local function"
  (interactive)
  (if (called-interactively-p 'any)   ;To call interactively AND to
                                      ;be able to have elisp-calls
    (call-interactively my-buffer-local-function)
    (apply my-buffer-local-function args)))

(setq my-buffer-local-function
  (lambda (amp;optional arg)
    (interactive "sinsert message: ")
    (message (concat "Not so default message: " arg))))
  

Видимая плохая сторона заключается в том, что она хорошо работает, если my-buffer-local-function она интерактивна. Если нет, run-my-buffer-local-function все равно будет интерактивным и видимым в M-x списке. Я думаю, вы не можете создавать иногда интерактивные функции, потому что interactive должен быть вызов верхнего уровня.

Кстати, вы можете назвать функцию и значение одним и тем же именем.

Комментарии:

1. Классный трюк. На данный момент это лучший ответ на мой вопрос. Хотя для моей цели это немного длинновато. Я буду помнить об этом на будущее.

Ответ №4:

Если определение функции должно быть специфичным для буфера, это обычно признак того, что оно должно быть специфичным для основного режима. Если это действительно так, то правильный способ справиться с этим — использовать другую функцию, адаптированную к этому конкретному буферу / режиму. Если проблема заключается в привязке ключа, то просто привяжите ключ к специализированной функции в ключевой карте основного режима.