reddit / emacs
Basic hook help
feltentragus · 2022-02-16 13:38
I'm having trouble customizing emacs.
I have learned how to use hooks to switch things on and off, e.g.:
```
(add-hook 'text-mode-hook 'visual-line-mode)
```
So that text files will open with word wrap on.
What I've not been able to do is to pass a value in on starting a mode, e.g.:
```
**doesn't work**
(add-hook 'text-mode-hook 'text-scale-adjust 3))
or
(add-hook 'text-mode-hook 'text-scale-adjust &eval ('text-scale-adjust 3))
**doesn't work**
```
I'm sure you can see what I'm trying to do -- when in text mode, automatically enlarge the text so that I can read it.
I have read the manual -- but unfortunately that part of the manual (23.1.1 Running Hooks) doesn't give any *examples.*
I think the bit that reads: "If a hook variable has a non-nil value, that value should be a list of functions. run-hooks calls all the functions, one by one, with no arguments." refers to what I'm trying to do, but how I write a list of functions that evaluates to "3" I've no idea.
Any help appreciated.
EcstaticBill2638 · 2022-02-16 13:49
Put it in a `lambda`
(add-hook 'text-mode-hook (lambda () (text-scale-adjust 3)))
feltentragus · 2022-02-16 14:11
Thanks, but I read on various tutorials, that using a lambda (that's an anonymous function, right?) is bad because it makes it difficult to maintain in the future if I want to stop doing whatever it was I once thought was a good idea.
from emacswiki:
Removing Hooks
To remove the hook, it’s like add-hook, mostly:
(remove-hook 'text-mode-hook 'turn-on-auto-fill)
If the hook function is anonymous (a lambda), then you need to take care that the exact same lambda is used for the removal. If the lambda has been byte-compiled then you need to make sure the same byte-code object is used for the removal. So clearly it generally makes sense to use named functions on hooks.
----
But thanks for replying!
(Yeesh - I thought using hooks to customize stuff was something that beginners like me were supposed to be able to do, but it's certainly non-trivial, at least for me. I wish I could use the easy customization interface, but it seemed I could only use that if either someone had already anticipated that need, or I already understood lisp -- at which point I decided the easiest interface was the text editor!)
EcstaticBill2638 · 2022-02-16 14:27
Sure, if you want to refer to it later, define it as a function, then add that to your hook:
(defun my-text-adjuster ()
(text-scale-adjust 3))
(add-hook 'text-mode-hook #'my-text-adjuster)
For trivial stuff like this that doesn't really need a name I just use anonymous functions. In the absolute worst case you just remove the hook from your config and reload emacs.
kaushalmodi · 2022-02-16 14:30
> but I read on various tutorials, that using a lambda (that's an anonymous function, right?) is bad
Yes.
At the expense of slightly more verbose config, I always prefer to define functions to be added to hooks.
Someone may say why?
1. You get a clean output when you do `C-h v` of that hook variable
2. You can keep on updating that hook function without having to update the hook variable
3. Easy to remove that function from the hook (compared to removing a lambda function)
4. You can edebug that named function added to the hook
5. .. there's probably lot more but here's what I could think quickly as I am typing :)
Here's a random example which I wrote recently:
(defun my/org-ref-process-buffer--html (backend)
"Preprocess `org-ref' citations to HTML format.
Do this only if the export backend is `html' or a derivative of
that."
;; `ox-hugo' is derived indirectly from `ox-html'.
;; ox-hugo <- ox-blackfriday <- ox-md <- ox-html
(when (org-export-derived-backend-p backend 'html)
(org-ref-process-buffer 'html)))
(add-to-list 'org-export-before-parsing-hook #'my/org-ref-process-buffer--html)
feltentragus · 2022-02-16 14:49
Thanks, that's really clear!
One of the problems I had reading tutorials etc. was that the writers (gurus all) couldn't resist the impulse to write a complicated example, in the hope that it would be of use to people with a wider range of skill levels. But it just left me with a "huh?" expression on my face.
Edit: just tested it out and it works! I've saved it into a separate text file, and I'll manually type it into the scratchpad buffer over the next few sessions before I add it to my initialization file. That way I'll be sure to tromp the technique into my brain so I'll remember it when I need it again.
Thanks again for your help.
EcstaticBill2638 · 2022-02-16 14:52
I get that, sometimes you just need a straightforward answer.
feltentragus · 2022-02-16 15:33
Thanks! I was pretty sure that the emacs manual said that using anonymous functions in hooks was a Bad Idea(TM), but having read so many different bits of the manual today trying to make sense of how hooks worked I couldn't remember where that advice was. I notice you've added comments to your code too -- noted for the future!
so_itgoes · 2022-02-16 21:53
u/EscstaticBill2638's thread below describes the correct solutions, but I wanted to add some color as to _why_ your solutions above aren't working, in case it helps your understanding.
If you run `describe-function` for `add-hook`, you'll see that it has the following function signature:
```
(add-hook HOOK FUNCTION &optional DEPTH LOCAL)
```
Ignoring the optional arguments for the purpose of my post, you can see that the two required arguments are the hook you're adding to (`HOOK`), and the function you'd like to add (`FUNCTION`).
The key part to understand is the second `FUNCTION` argument. You are passing a piece of logic that will be evaluated later, when the hook is run. This is distinct from evaluating an expression:
When you do:
```
(add-hook 'text-mode-hook 'text-scale-adjust 3)
```
you're using bad syntax because `add-hook` doesn't take 4 arguments (it actually does with &optionals, but they are out of scope of this situation). What I'm guessing you meant to write is:
```
(add-hook 'text-mode-hook (text-scale-adjust 3))
```
Which might seem intuitive at first, because `(text-scale-adjust 3)` is an expression which takes zero arguments, so it looks like a function. But it's not a function, it's an _expression_. You're essentially telling emacs to evaluate `text-scale-adjust` when you call `add-hook`. Ignoring that this is improper syntax for `add-hook`, it's also intuitively incorrect: you don't want to adjust text size when you call `add-hook`, you want to adjust text size when `text-mode-hook` is run. Hence, you want to create a piece of encapsulated logic which runs at a later time with `text-mode-hook`. When you instead do the following (using lambdas because I'm lazy, but named functions are a best practice as mentioned below):
```
(add-hook 'text-mode-hook (lambda () (text-scale-adjust 3)))
```
Then `text-scale-adjust` isn't actually evaluated on `add-hook`. Instead, you're simply telling emacs to evaluate `text-scale-adjust` _later_ with the hook.
In short, this is an important distinction between an expression being evaluated (e.g. `(text-scale-adjust 3)`), and a function being defined, but _not_ evaluated (e.g. `(lambda () (text-scale-adjust 3))`)
7890yuiop · 2022-02-17 00:31
> So clearly it generally makes sense to use named functions on hooks.
Yes, you definitely sidestep several potential problems if you stick with named functions when it comes to hooks (and keybindings too).
You're doing it right :)
feltentragus · 2022-03-02 16:36
Oh, thanks! I've been trying to make sense of the emacs manual but it often seems to use a language from a parallel dimension.
I really appreciate everyone helping me. Emacs is an amazing tool, but it does take a while to learn.