I found this macro, to run code for specific project path:
(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)))))
and I use it:
(project-specifics "projects/test"
(message "z"))
And I work on modification that will remove prevoius lambda from the hook, so far I have helper functions
(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))))
But when I call:
(remove-hook-name-lambda "projects/test" find-file-hook)
found is show up in *Messages* buffer but the lambda is not removed. What's wrong here?
The reason is probably that the found object is the first in list, in which case (delete item list) returns (cdr list) instead of modifying its structure to preserve identity.
The important point here is that delete cannot guarantee that
(eq x (delete item x))
==> t
e.g. when item is the only element of x, delete will return nil which cannot be eq to the original cons.
The solution is to return the new value of list from remove-lambda-helper by replacing
(dolist (item list) ...)
with
(dolist (item list list) ...)
and use it in remove-hook-name-lambda like in add-to-list:
(defun remove-hook-name-lambda (name hook-name)
(set hook-name
(remove-lambda-helper (symbol-value hook)
(lambda (body)
(equal (cadr (cadr (caddr body))) name)))))
Adding lambdas to hooks is not a very good idea, especially if you want to remove them later. Note that your lambda removal test will fail if you happen to compile your code.
Lambdas also accumulate in the hook if you modify them, e.g., if you have
(add-hook 'my-hook (lambda () ...))
and then you modify the lambda and evaluate the add-hook form again, you will end up with two lambdas in the hook.
The much better solution is to use defun to define the functions:
(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))))
Responding to the concern you expressed in a comment, special characters in symbols are okay as long as you quote them when dealing with the reader; since you are not going to do that - you will be only using add-project-specifics and remove-project-specifics - you should be fine with interning them.
Use Per-Directory Local Variables in .dir-locals.el.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With