Emacs: use add-hook inside function (defun) - emacs

If I do
(add-hook 'haskell-mode-hook
(lambda ()
(setq indent-tabs-mode t)
(setq tab-width 4)
(message "OK")))
in my ~/.emacs.d/init.el, then the (lambda ...) does get executed when I enter haskell-mode.
However, if I use a function like this:
(defun my-add-hook (hook tmode twidth)
(add-hook hook
(lambda ()
(setq indent-tabs-mode tmode)
(setq tab-width twidth)
(message "OK"))))
and then call it later in ~/.emacs.d/init.el like so:
(my-add-hook 'haskell-mode-hook t 4)
Then nothing happens (even the "OK" message isn't displayed). Is add-hook a
special function that cannot be used from within a defun? I have per-project
settings defined in a separate initialization file that detects the buffer name
and adds (lambda ()...) calls to the pertinent major mode (in the example
above, haskell-mode); I want to reduce the code verbosity by using a thin
wrapper like my-add-hook above, but I cannot tell why add-hook is being so
difficult.
EDIT1: Added code for clarification.
EDIT2: I do get a "File mode specification error: (void-variable tmode)" message when I try to use my-add-hook.

Here's a simple fix without needing to know about lexical binding:
(defun my-add-hook (hook tmode twidth)
(add-hook hook
`(lambda ()
(setq indent-tabs-mode ,tmode)
(setq tab-width ,twidth)
(message "OK"))))

You can use lexical-let to rebind the variables, then the lambda function will preserve their values:
(defun my-add-hook (hook tmode twidth)
(lexical-let ((tmode tmode)
(twidth twidth))
(add-hook hook
(lambda ()
(setq indent-tabs-mode tmode)
(setq tab-width twidth)
(message "OK")))))
I'm not sure whether this is the most idiomatic Emacs Lisp code, but it does follow the same pattern shown in the Emacs Wiki article DynamicBindingVsLexicalBinding, which defines compose as:
(defun compose (f g)
(lexical-let ((f f)
(g g))
(lambda (x)
(funcall f (funcall g x)))))

Related

add a hook in prog-mode except a particular mode

i have a function
(defun a--before-test-save-hook()
"Test of before save hook"
(message "foobar"))
and i want to run it in prog-mode except python-mode,
but i have no clue now
and i just add-hook in prog-mode including python-mode
(add-hook 'prog-mode (lambda ()
(add-hook 'before-save-hook 'a-test-before-save-hook t t)))
i have try
(defun a-test-before-save-hook()
"Test of before save hook"
(unless (eq major-mode 'python-mode)
(message "foobar")))
but i want a better try,any solution will be appreciated.
Not sure if it's better, but you could do:
(add-hook 'prog-mode-hook
(lambda ()
(unless (derived-mode-p 'python-mode)
(add-hook 'before-save-hook
#'a-test-before-save-hook t t))))
Of course, my own reflex is to ask "what makes Python special?". The answer might let you replace the (derived-mode-p 'python-mode) test with something that goes more directly at the heart of the issue (e.g. maybe the issue is related to indentation-significance and would hence also apply to Coffeescript and Haskell and maybe you could check electric-indent-inhibit instead).

use .dir-locals.el to pick major mode [duplicate]

I have defined a .dir-locals.el file with the following content:
((python-mode . ((cr/virtualenv-name . "saas"))))
In my .emacs I have the following function to retrieve this value and provide a virtualenv path:
(defun cr/virtualenv ()
(cond (cr/virtualenv-name (format "%s/%s" virtualenv-base cr/virtualenv-name))
((getenv "EMACS_VIRTUAL_ENV") (getenv "EMACS_VIRTUAL_ENV"))
(t "~/.emacs.d/python")))
Finally, in my python-mode-hook list, I have this hook function:
(add-hook 'python-mode-hook 'cr/python-mode-shell-setup)
(defun cr/python-mode-shell-setup ()
(message "virtualenv-name is %s" cr/virtualenv-name)
(let ((python-base (cr/virtualenv)))
(cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython")))
(setq python-python-command (concat python-base "/bin/ipython"))
(setq py-python-command (concat python-base "/bin/ipython"))
(setq py-python-command-args '( "-colors" "NoColor")))
(t
(setq python-python-command (concat python-base "/bin/python"))
(setq py-python-command (concat python-base "/bin/python"))
(setq py-python-command-args nil)))))
When I open a new python file, the message logged by cr/python-mode-shell-setup indicates that cr/virtualenv-name is nil. However, when I C-h v the name, I get "saas" instead.
Obviously there's a load order issue here; is there a way to have my mode hook statements respond to directory-local variables?
This happens because normal-mode calls (set-auto-mode) and (hack-local-variables) in that order.
However hack-local-variables-hook is run after the local variables have been processed, which enables some solutions:
The first is to make Emacs run a new "local variables hook" for each major mode:
(add-hook 'hack-local-variables-hook 'run-local-vars-mode-hook)
(defun run-local-vars-mode-hook ()
"Run a hook for the major-mode after the local variables have been processed."
(run-hooks (intern (concat (symbol-name major-mode) "-local-vars-hook"))))
(add-hook 'python-mode-local-vars-hook 'cr/python-mode-shell-setup)
(Your original function can be used unmodified, with that approach.)
A second option is to utilise the optional LOCAL argument to add-hook that makes the specified function buffer-local. With this approach you could write your hook as follows:
(add-hook 'python-mode-hook 'cr/python-mode-shell-setup)
(defun cr/python-mode-shell-setup ()
(add-hook 'hack-local-variables-hook
(lambda () (message "virtualenv-name is %s" cr/virtualenv-name)
(let ((python-base (cr/virtualenv)))
(cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython")))
(setq python-python-command (concat python-base "/bin/ipython"))
(setq py-python-command (concat python-base "/bin/ipython"))
(setq py-python-command-args '( "-colors" "NoColor")))
(t
(setq python-python-command (concat python-base "/bin/python"))
(setq py-python-command (concat python-base "/bin/python"))
(setq py-python-command-args nil)))))
nil t)) ; buffer-local hack-local-variables-hook
i.e. python-mode-hook runs first and registers the anonymous function with hack-local-variables-hook for the current buffer only; and that function is then called after the local variables have been processed.
Lindydancer's comment prompts a third approach. It's not nearly as clean as the other two, but proved interesting regardless. I didn't like the idea of causing (hack-local-variables) to be called twice, but I see that if you set the local-enable-local-variables buffer-locally, it prevents (hack-local-variables) from doing anything, so you could do this:
(defun cr/python-mode-shell-setup ()
(report-errors "File local-variables error: %s"
(hack-local-variables)))
(set (make-local-variable 'local-enable-local-variables) nil)
(let ((python-base (cr/virtualenv)))
...))
Obviously that modifies the normal sequence of execution a little, so side effects may be possible. I was worried that if the same major mode is set by a local variable comment in the file, this might cause infinite recursion, but that doesn't actually appear to be a problem.
Local variable header comments (e.g. -*- mode: foo -*-) are handled by (set-auto-mode), so those are fine; but a mode: foo Local Variables: comment seems like it would be an issue as it is handled by (hack-local-variables), and so if the mode is set that way I thought it would cause recursion.
In practice I was able to trigger the problem by using a simple function as a 'mode' which did nothing more than try to run its hooks; however testing with a 'proper' mode did not exhibit the problem, so it's probably safe in reality. I didn't look into this further (as the other two solutions are much cleaner than this), but I would guess the delayed mode hooks mechanism probably explains it?

local keymap for emacs outline-minor-mode

I want to set the outline-minor-mode for init.el file and when TAB key is pressed on the lines starting with ; the function outline-toggle-children should be called in order to fold and expand the sub headings.
Below is the code for hook. But it does not work for the "TAB" key binding as expected.
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(if (equal (buffer-name) "init.el")
(progn
(outline-regexp "^;+")
(outline-minor-mode 1)
(local-set-key (kbd "TAB") ; this does not work
(lambda ()
(if (string-match outline-regexp (thing-at-point 'line))
(outline-toggle-children))))))))
I presume that the error you get is wrong-type-argument commandp. This happens because functions bound to keys must be "interactive" functions. You need to add an (interactive) declaration to the function, so that Emacs knows how to invoke the function in response to an event:
(lambda ()
(interactive)
(if (string-match outline-regexp (thing-at-point 'line))
(outline-toggle-children)))

Logging Emacs file open/save commands

I'm looking to keep a history of file operations in Emacs, in particular file open and save commands, with associated timestamps and pid, has anyone done something like this?
You can easily implement it by yourself using emacs standard hooks.
Something like this:
(setq *find-file-list* '())
(setq *save-file-list* '())
(add-hook 'find-file-hook (lambda ()
(push (list (buffer-file-name) (time-stamp-string)) *find-file-list*)))
(add-hook 'after-save-hook (lambda ()
(push (list (buffer-file-name) (time-stamp-string)) *save-file-list*)))

Enabling certain emacs modes or features *almost* always

There's a couple of emacs features, such as flyspell-mode,
highlight-beyond-fill-column, or auto-fill-mode, I find so useful that I
want them enabled almost all the time. However, there's always certain
conditions in which they don't make much sense.
highlight-beyond-fill-column, for example, I tend to want for pretty much
everything I edit myself, but for reading things others wrote, like in Gnus or
when reading the built-in documentation, it's actually quite annoying.
Similarly auto-fill-mode is incredibly handy when writing just Text. However,
it's entirely unhelpful when programming.
For those reasons I can't just enable features like that globally. Always
enabling them manually isn't very practical as well, but so is having to write
hooks for each and every mode or application I'm using within emacs, obviously
not being able to cover all of them, and still ending up enabling those features
manually.
What I believe I'm looking for is a way to globally enable some features, but
selectively turn them off again, based on various conditions such as which major
or minor modes are being used, if the buffer is read-only or writable, or
depending on the buffer containing text or source code. I do realize that at
least the last thing might not be easy for emacs to answer, but at least for
that I believe I'd be fine with a hard-coded list of "programming-modes" i use
regularly.
So you want total control over what's executed when a particular mode is opened or a particular type of file... OK here is what you need :
;; The function where you could put all your customization
(defun my-func ()
(turn-on-auto-fill))
;; This is an example, customize it like you need it.
(defvar functions-to-call
`(((c-mode c++-mode) ".h$" (my-func))
((cperl-mode perl-mode) nil (my-func)))
"A list of triples, used for storing functions.
A triplet is composed of a symbol for the major mode (or a list of symbols),
a regular expression to match against the buffer's file name,
and the functions to call when both the major mode and regular expr match.")
(defun call-mode-functions ()
"call functions, based on major mode and buffer name regexp matching"
(interactive)
(let ((l functions-to-call))
(while l
(let* ((elt (car l))
(modes (if (listp (car elt)) (car elt) (list (car elt))))
(re (cadr elt))
(fcts (caddr elt)))
(when (and (member major-mode modes)
(or (null re)
(string-match re (buffer-file-name))))
(while fcts
(funcall (car fcts))
(setq fcts (cdr fcts)))
(setq l nil)))
(setq l (cdr l)))))
(add-hook 'after-change-major-mode-hook 'call-mode-functions)
With this code, you can can do the fine-grained customization you require. This is just an example, you can adapt it to your needs.
Interesting idea. I recommend using the
espect extension from your github.
It sounds like you basically want to turn specific minor-modes on or off for "specific buffers". Usually, the "specific buffers" can be distinguished by their major mode, which is how I usually look at this type of problem. How to turn minor modes on or off depends on the implementation of both the minor mode you're trying to turn on/off and the major mode you're trying to turn it on/off in.
The usual way to enable/disable things based on major-mode is via the major-mode-hook variable. This is where you stick things to customize the mode:
(add-hook 'text-mode-hook 'auto-fill-mode)
I usually write my own function, even if it's a simple one-liner, because I almost always will add stuff later on:
(defun my-text-mode-hook ()
"Stuff to do when `text-mode' is invoked."
(auto-fill-mode 1))
(add-hook 'text-mode-hook 'my-text-mode-hook)
You can also make things within the hook conditional:
(defun my-text-mode-hook ()
"Stuff to do when `text-mode' is invoked."
;; skip modes based on text-mode
(when (eq major-mode 'text-mode)
(auto-fill-mode 1))
)
(add-hook 'text-mode-hook 'my-text-mode-hook)
I usually do all of this in a major-mode-load-hook, so that it only happens when the major-mode's code is loaded:
(defun my-tnt-load-hook ()
(defun my-tnt-im-mode-hook ()
"Hook for TNT's im-mode hook."
(flyspell-mode 1)
(setq fill-column (- (frame-width) 5)))
(add-hook 'tnt-im-mode-hook 'my-tnt-im-mode-hook)
(add-hook 'tnt-chat-mode-hook 'my-tnt-im-mode-hook))
(add-hook 'tnt-load-hook 'my-tnt-load-hook)
A well-written major-mode will have a load-hook variable defined (I usually look at the mode's source code to find out). If it doesn't have a load-hook, you can simulate one with the eval-after-load function:
(defun my-view-mode-after-load-hook ()
"Stuff to do after view mode loads."
(defun my-view-mode-hook ()
"Stuff to run in `view-mode'."
(flyspell-mode 0))
(add-hook 'view-mode-hook 'my-view-mode-hook)
(define-key view-mode-map "b" 'View-scroll-page-backward)
(define-key view-mode-map [(delete)] 'View-scroll-page-backward)
(define-key view-mode-map "q" 'View-kill-and-leave)
(define-key view-mode-map "Q" 'View-quit))
(eval-after-load 'view '(my-view-mode-after-load-hook))
If you don't do this in a load-hook then you have to make sure the mode-hook is customizable, and then add in your my-mode-hook via customize; I'd rather have all of the stuff in one place in my .emacs, so I don't usually customize my hooks this way.
If you ever find a major-mode that does not have a major-mode-hook you can create your own major-mode based off of it using define-derived-mode. You'll then have to get the newly defined mode invoked whenever the old mode was.
(defun replace-alist-mode (alist oldmode newmode)
(dolist (aitem alist)
(if (eq (cdr aitem) oldmode)
(setcdr aitem newmode))))
(define-derived-mode hooked-foobar-mode foobar-mode "Foobar")
(replace-alist-mode auto-mode-alist 'foobar-mode 'hooked-foobar-mode)
(defun my-hooked-foobar-mode-hook ()
"Hook to run when `hooked-foobar-mode' is called."
(flyspell-mode 0))
(add-hook 'hooked-foobar-mode-hook 'my-hooked-foobar-mode-hook)
Some minor modes can be enabled globally. If you want them on most of the time and it supports it, you can turn it on globally and then turn it off for specific major modes.
(global-font-lock-mode 1)
;; example of how to do it without a defun
(add-hook 'text-mode-hook (function
(lambda () ""
(interactive)
(font-lock-mode 0))))
If the minor mode can't be enabled globally, or you don't want it enabled globally, just turn it on for specific modes, as shown above.
So here's what I came up with after reading [Jérôme Radix][1]'s excellent
reply. Especially the pointer to after-change-major-mode-hook has helped a
lot.
I now define my buffer-specific settings in a list like this:
;; no `highlight-beyond-fill-column' for w3m and gnus
'((((:not ((:mode "^gnus") (:mode w3m-mode))))
(lambda () (highlight-beyond-fill-column)))
;; `flyspell-mode` and `auto-fill-mode` for text-ish buffers
(((:mode message-mode)
(:mode org-mode)
(:mode pod-mode)
(:mode markdown-mode)
(:name "\\.\\(txt\\|mkn\\)$"))
(lambda ()
(flyspell-mode)
(auto-fill-mode)))
;; indenting with tabs for certain projects
(((:name t :fun (lambda () (and (not eproject-root)
(eproject-maybe-turn-on)))))
(lambda () (setq indent-tabs-mode t)))
When the major mode changes, I then iterate over all those settings, evaluate
the defined conditions in the buffer, and call the appropriate lambda if a
condition matches:
(add-hook 'after-change-major-mode-hook
(lambda () (rafl:apply-buffer-settings rafl:buffer-settings)))
(defun rafl:apply-buffer-settings (settings)
(dolist (setting rafl:buffer-settings)
(let ((condition (car setting))
(action (cadr setting)))
(when (rafl:evaluate-buffer-condition condition)
(funcall action)))))
Evaluating those conditions is a little messy, but works rather well for me.
(defun rafl:evaluate-buffer-condition (con)
(cond
((functionp con)
(funcall con))
((listp con)
(cond
((listp (car con))
(reduce
(lambda (a b) (or a b))
(cons nil (mapcar #'rafl:evaluate-buffer-condition con))))
(t
(reduce
(lambda (a b) (and a b))
(cons
t
(let (ret)
(while con
(let ((k (pop con))
(v (pop con)))
(push (cond
((eq k :fun)
(funcall v))
((eq k :not)
(when (not (listp v))
(error ":not requires a list"))
(not (rafl:evaluate-buffer-condition v)))
((eq k :mode)
(if (stringp v)
(string-match-p v (symbol-name major-mode))
(eq v major-mode)))
((eq k :name)
(cond
((and (buffer-file-name) (stringp v))
(string-match-p v (buffer-file-name)))
((buffer-file-name)
v)
(t
(not v))))
(t
(error "unknown cond")))
ret)))
ret))))))
(t
(error "invalid condition"))))
It also turns out that I could do all my per-project setting, which I did quite
differently before, using this mechanism. I'm very happy about that.
1: Enabling certain emacs modes or features *almost* always
I do this
(require 'linum)
;(global-linum-mode t)
(add-hook 'find-file-hook (lambda ()
(if (not(equal major-mode 'term-mode))
(linum-mode nil))))