从Emacs中的挂钩中删除特定lambda的函数
我找到此宏,用于运行特定项目路径的代码:从Emacs中的挂钩中删除特定lambda的函数,emacs,lambda,elisp,hook,Emacs,Lambda,Elisp,Hook,我找到此宏,用于运行特定项目路径的代码: (defmacro project-specifics (name &rest body) `(progn (add-hook 'find-file-hook (lambda () (when (string-match-p ,name (buffer-file-name)) ,@body))) (add-hook 'dired-af
(defmacro project-specifics (name &rest body)
`(progn
(add-hook 'find-file-hook
(lambda ()
(when (string-match-p ,name (buffer-file-name))
,@body)))
(add-hook 'dired-after-readin-hook
(lambda ()
(when (string-match-p ,name (dired-current-directory))
,@body)))))
我用它:
(project-specifics "projects/test"
(message "z"))
我正在进行修改,将prevoius lambda从钩子中移除,到目前为止,我已经有了帮助函数
(defun remove-lambda-helper (list matcher)
(dolist (item list)
(if (and (listp item) (eq (car item) 'lambda))
(when (funcall matcher item)
(message "found")
(setq list (delete item list))))))
(defun remove-hook-name-lambda (name hook)
(remove-lambda-helper hook
(lambda (body)
(equal (cadr (cadr (caddr body))) name))))
但当我打电话时:
(remove-hook-name-lambda "projects/test" find-file-hook)
found显示在*Messages*
缓冲区中,但lambda未被删除。这里怎么了?问题出在哪里
原因可能是找到的对象是列表中的第一个对象,在这种情况下(删除项列表)
返回(cdr列表)
,而不是修改其结构以保留标识
这里重要的一点是,delete
不能保证
(eq x (delete item x))
==> t
e、 g.当项
是x
的唯一元素时,delete
将nil
返回到原始cons
解决方案
解决方案是通过替换来从删除lambda helper
返回列表的新值
(dolist (item list) ...)
与
并在删除钩子名称lambda
中使用它,如:
最后一句话
将lambda添加到挂钩中不是一个很好的主意,尤其是如果您想稍后删除它们。请注意,如果您碰巧编译了代码,lambda删除测试将失败
如果修改lambda,例如,如果
(add-hook 'my-hook (lambda () ...))
然后修改lambda
并再次计算addhook
表单,最后将在钩子中得到twolambda
更好的解决方案是使用defun
定义函数:
(defmacro add-project-specifics (name &rest body)
(let ((ffh (intern (concat name "-find-file-hook")))
(darh (intern (concat name "-dired-after-readin-hook"))))
`(progn
(defun ,ffh ()
(when (string-match-p ,name (buffer-file-name))
,@body))
(add-hook 'find-file-hook ',ffh)
(defun ,darh ()
(when (string-match-p ,name (dired-current-directory))
,@body))
(add-hook 'dired-after-readin-hook ',darh))))
(defmacro remove-project-specifics (name)
(let ((ffh (intern (concat name "-find-file-hook")))
(darh (intern (concat name "-dired-after-readin-hook"))))
`(progn
(remove-hook 'find-file-hook ',ffh)
(unintern ',ffh nil)
(remove-hook 'dired-after-readin-hook ',darh)
(unintern ',darh nil))))
附言
回应你在评论中表达的担忧,符号中的特殊字符是可以的,只要你在与读者打交道时引用它们;由于您不打算这样做-您将只使用添加项目细节
和删除项目细节
-您应该可以对它们进行实习
解决实际问题的最佳方案
使用.dir locals.el
将lambda添加到钩子中,并添加到您自己的按名称键入的哈希表中。然后,当需要删除它时,在哈希表中查找lambda并从钩子中删除它
关于Elisp哈希表:
顺便说一句,词法绑定t时(eq(car item)'lambda)将失败。我想提出一种重构,允许您定义一次挂钩,然后让它仅在满足某些条件时运行实际操作
(defvar project-specific-name-regex "projects/test"
"*Regex to match on buffer name in order to trigger `project-specific-fun'.")
(defvar project-specific-fun (lambda () (message "z"))
"*Lambda form to run when `project-specific-name-regex' triggers.")
(defsubst project-specific-trigger-maybe (name)
(and project-specific-name-regex
(stringp project-specific-name-regex)
(string-match-p project-specific-name-regex name)
project-specific-fun
(funcall project-specific-fun) ) )
(defun project-specific-find-file ()
"Hook run from `find-file-hook' to run `project-specific-fun' if it's set
and the buffer name matches `project-specific-name-regex'."
(project-specific-trigger-maybe (buffer-file-name)) )
(defun project-specific-dired-after-readin ()
"Hook run from `dired-after-readin-hook' to run `project-specific-fun'
if it's set and the directory name matches `project-specific-name-regex'."
(project-specific-trigger-maybe (dired-current-directory)) )
(add-hook 'find-file-hook #'project-specific-find-file)
(add-hook 'dired-after-readin-hook #'project-specific-dired-after-readin)
因此,只需取消设置特定于项目的名称
和/或特定于项目的乐趣
,即可禁用钩子中的操作。如果是lambda
,则明智的做法是使用defun
。然后删除它是很简单的(特别是如果你可能想在两次运行之间编辑正文。然后如果你使用lambda
,你如何找到旧定义或新定义的实例?等等)。@triple的答案是最好的-s?他应该将其作为答案发布,你应该接受它(IMHO)。您不应该随意添加额外的函数来完成您要做的事情(删除特定的lambda表单)。帮你自己一个忙,给那些匿名函数取个名字。这是为什么要命名函数的一个主要例子。当然,如果您无法控制将此类匿名函数添加到钩子中,那么您就不走运了。但通常可以避免这样做。@Drew我需要想出一种方法,基于unix路径定义2个函数名,这可能会中断(比如路径有空格或“
),我不认为在宏中创建具有名称的自定义函数,然后将该名称分配给挂钩是个好主意,你有兰博达斯,我明白了。你没有提到这些。不知道为什么您需要基于路径或其他什么的名称,但是如果您这样做了,您就这样做了。@Drew查看项目细节
宏,它会基于路径添加钩子。如果您想添加函数而不是lambda,您需要以某种方式命名该函数,识别该函数的唯一方法是名称参数(即路径),因此您需要将该路径转换为函数名。但是如果我想有几个项目,该怎么办。我认为代码需要将它们保存在列表或散列中,这样才能工作;你会如何用你所拥有的来实现这一点?是的,用一个alist扩展它会使它更加通用,而不会使它复杂化。如果您对一个操作感到满意,但需要许多项目,string-match-p
将正则表达式作为其第一个参数。我的文档串有点乱。因此,您可以将“projects/test\\\\ foo/bar”
放在两个项目中同时触发。现在修复了docstring,并进行了一些重构以减少重复代码。大+1用于提及.dir locals.el
。令人惊讶的是,现在这是多么少,有多少人痛苦地实现他们自己的黑客项目特定的变量。
(defmacro add-project-specifics (name &rest body)
(let ((ffh (intern (concat name "-find-file-hook")))
(darh (intern (concat name "-dired-after-readin-hook"))))
`(progn
(defun ,ffh ()
(when (string-match-p ,name (buffer-file-name))
,@body))
(add-hook 'find-file-hook ',ffh)
(defun ,darh ()
(when (string-match-p ,name (dired-current-directory))
,@body))
(add-hook 'dired-after-readin-hook ',darh))))
(defmacro remove-project-specifics (name)
(let ((ffh (intern (concat name "-find-file-hook")))
(darh (intern (concat name "-dired-after-readin-hook"))))
`(progn
(remove-hook 'find-file-hook ',ffh)
(unintern ',ffh nil)
(remove-hook 'dired-after-readin-hook ',darh)
(unintern ',darh nil))))
(defvar project-specific-name-regex "projects/test"
"*Regex to match on buffer name in order to trigger `project-specific-fun'.")
(defvar project-specific-fun (lambda () (message "z"))
"*Lambda form to run when `project-specific-name-regex' triggers.")
(defsubst project-specific-trigger-maybe (name)
(and project-specific-name-regex
(stringp project-specific-name-regex)
(string-match-p project-specific-name-regex name)
project-specific-fun
(funcall project-specific-fun) ) )
(defun project-specific-find-file ()
"Hook run from `find-file-hook' to run `project-specific-fun' if it's set
and the buffer name matches `project-specific-name-regex'."
(project-specific-trigger-maybe (buffer-file-name)) )
(defun project-specific-dired-after-readin ()
"Hook run from `dired-after-readin-hook' to run `project-specific-fun'
if it's set and the directory name matches `project-specific-name-regex'."
(project-specific-trigger-maybe (dired-current-directory)) )
(add-hook 'find-file-hook #'project-specific-find-file)
(add-hook 'dired-after-readin-hook #'project-specific-dired-after-readin)