Elisp中的缓冲区局部函数

14

我想重新定义一个已存在的函数 foo,但只针对特定的缓冲区。

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

我希望这样做可以达到我的目的:

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

但是它并没有起作用。有什么想法吗?

[编辑:或者,因为函数被绑定到一个键上,仅修改当前缓冲区的绑定就足够了。但我不知道怎么做。本地按键映射由主模式中所有缓冲区共享。修改它会修改具有此主模式的所有缓冲区中的绑定。

我唯一能想到的丑陋解决方案是为整个缓冲区设置一个键映射文本属性。这是唯一的解决方法吗?]


你实际要解决的问题是什么?也许有一种不同的方法可以更好地解决问题。你如何知道何时需要显示 "完成!" 和 "未完成:("? - Trey Jackson
@Trey 这个包的帮助模式有一个导航功能(跳转到各个部分)。它简短而优雅。但是对于一种非常特定类型的帮助文件,我需要不同的行为。我希望能够在本地重新定义它,而不是将该功能构建到函数中。事实上,你的答案正是我想要的。 - VitoshKa
4个回答

6
您可以创建一个函数,为您完成覆盖的操作,大致如下所示:
(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!")))))

显然需要适当地自定义按键绑定。这将仅在当前缓冲区中生效。

这可以解决问题,我会采用这种方法,但是@desudesudesu的答案涉及到了使函数缓冲区本地化的原始问题,其中有一些理论方面的内容。因此我接受他的答案。 - VitoshKa

5
给定符号的值和功能属性是分离的,因此很可能make-local-variable只会影响值,而fset则操作函数属性。
您最好详细描述您想要做什么,但一个通用解决方案是使用“around advice”来包装原始函数与您自己的代码。
(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


这是为我的程序包而做的,所以我可以直接修改函数而不是建议它。问题在于,对于一个非常特定的缓冲区,我需要不同的功能,而且不想为了这个非常特殊的情况而弄乱函数。这是一个交互式函数,因此在本地修改映射即可。但我也不知道该如何做。编辑了问题。 - VitoshKa

4
把你的符号函数写成符号值,然后用其他函数对它们进行评估,这样怎么样?
(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应该是顶级调用。

顺便说一下,您可以使用相同的名称命名函数和值。


酷炫的技巧。这是迄今为止最好的回答了我的问题,虽然对于我的目的来说有点长。我会将其记在心中,以备将来之需。 - VitoshKa

2
如果函数定义是特定于缓冲区的,通常这意味着它是特定于主模式的。如果确实是这种情况,那么处理它的正确方法是使用适用于该特定缓冲区/模式的不同函数。如果问题是键绑定,则只需在主模式的键映射中将键绑定到专门的函数即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接