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

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

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

Я прыгал, это будет делать:

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

Но это не так. Любые идеи?

[EDIT: В качестве альтернативы, поскольку функция ограничена ключом, было бы достаточно изменить привязку только для текущего буфера. Но я не понимаю, как это сделать. Локальная раскладка разделяется всеми буферами в основном режиме. Изменение этого параметра изменяет привязки во всех буферах с помощью этого основного режима.

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

Ответ 1

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

(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 (&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 (&optional arg)
    (interactive "sinsert message: ")
    (message (concat "Not so default message: " arg))))

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

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

Ответ 2

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

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

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

Ответ 3

Значение и функциональные свойства данного символа являются отдельными, и поэтому предположительно 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)

EDIT: (относительно дополнительных комментариев к карте)

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

Ответ 4

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