0

I would like to open url in eww in readable-mode and find str. Below is my code that doesn't work:

(defun my-eww-searh-readable (str)
  "Once-off call to `my-eww-searh-readable' after EWW is done rendering."
  (unwind-protect
      (eww-readable)
    (search-forward-regexp str)
    (remove-hook 'eww-after-render-hook (lambda () (my-eww-searh-readable str)))))

(defun my-eww-url (url str)
  "Open URL in `eww' with `eww-readable' enabled and search str."
  (interactive)
  (add-hook 'eww-after-render-hook (lambda () (my-eww-searh-readable str)))
  (eww url))

(setq strtmp "eww")
(setq ulr "https://emacs.stackexchange.com/questions/50807/search-in-readable-eww-buffer")
(my-eww-url url strtmp)

I found the following mistake:

Symbol’s value as variable is void: str

What do I do wrong?

Drew
  • 75,699
  • 9
  • 109
  • 225
Pfedj
  • 308
  • 2
  • 11
  • That error does not seem to be reproducible or consistent with the code you posted. Could you check if you posted the correct code and error message reproducible from that code? I guess `(my-eww-url comment-web/text-string-url strtmp)` should be `(my-eww-url ulr strtmp)`. – martin_joerg Jun 01 '19 at 08:33
  • Thank you, I fixed it. – Pfedj Jun 01 '19 at 09:25

1 Answers1

3

You seem to be using dynamic binding which means that str would be evaluated once your lambda function is executed at which time the variable str is no longer in scope.

You should turn on lexical binding to have your hook function converted into a closure during the invocation of my-eww-url so that once your hook function will be invoked str will be bound to the same variable it was when my-eww-url was invoked. You can turn on lexical binding by putting ;;; -*- lexical-binding: t; -*- in the first line of your file.

Note that with lexical binding your approach for removing the hook again in my-eww-searh-readable will no longer work as the closure created in my-eww-searh-readable will differ from the one previously created in my-eww-url. In your situation using (remove-hook 'eww-after-render-hook (first eww-after-render-hook)) instead should suffice.

If for some reason you cannot or do not want to use lexical binding you can have the variable str evaluated when adding (and removing) the hook function by using backquotes with commas as follows:

(defun my-eww-searh-readable (str)
  "Once-off call to `my-eww-searh-readable' after EWW is done rendering."
  (unwind-protect
      (eww-readable)
    (search-forward-regexp str)
    (remove-hook 'eww-after-render-hook `(lambda () (my-eww-searh-readable ,str)))))

(defun my-eww-url (url str)
  "Open URL in `eww' with `eww-readable' enabled and search str."
  (interactive)
  (add-hook 'eww-after-render-hook `(lambda () (my-eww-searh-readable ,str)))
  (eww url))

For more background also refer to (elisp)Variable Scoping and to the answer to Scope in lambda.

martin_joerg
  • 322
  • 1
  • 4
  • 10
  • 1
    Why are you quoting `',str`? Isn't its value going to be a string literal? Also, I suggest you make a stronger case for `lexical-binding`, which has been built-in since Emacs 24. Quoting lambdas comes with many disadvantages compared to evaluated functions, so should be discouraged. – Basil Jun 01 '19 at 11:06
  • You are right, quoting is not needed here. I have edited and extended my answer accordingly. – martin_joerg Jun 01 '19 at 13:12
  • +1 for showing not only the `lexical-binding` solution but also the dynamic-binding solution (only the function-creation time *value* of variable `str` is actually needed; no *variable* is needed a such (as a variable) when the function is invoked). The dynamic approach has the disadvantage that the quoted lambda form is not known to Emacs to be a function until it is invoked - it is really just a list with car `lambda` etc., so it is typically a bit less performant. – Drew Jun 01 '19 at 14:59
  • It's generally a good habit to quote the value returned by a comma'd sexp inside a backquote, whether or not the value is self-evaluating (as is a string). On the other hand, if the value is expected to be self-evaluating (e.g. a string) and for some reason it is not, you might well want an error to be raised. In sum, it's good to be aware of why and when you might want to use `',sexp` or `,sexp`. – Drew Jun 01 '19 at 15:06
  • Your answer is good. But IMO the question is a duplicate and should be closed. If it is closed as such, and if you think your answer adds something to those given for the original question of which this is a duplicate, consider adding such info as an answer to that question. – Drew Jun 01 '19 at 15:10