I am trying to add a hook which calls shr-render-buffer after loading a file in html-mode. This is what I came up with:

(add-hook 'html-mode-hook (lambda () (call-interactively #'shr-render-buffer)))

This works, except not quite as expected.

Calling M-x shr-render-buffer in an html-mode buffer splits the current frame vertically, and puts the rendered output in the bottom window with the html still visible in the top window. On the other hand, if I do find-file and open an html file, my hook code splits the frame vertically, but buries the html buffer, so that I have the rendered buffer on the bottom, and whichever buffer I was visiting when I did find-file at the top.

What is happening here? Seemingly, something with the timing is getting messed up from the way I am calling shr-render-buffer, but I don't really know what/how.

  • 1
    It is not clear what the question is. You say "This works, except not quite as expected." Do you mind specifying what exactly is expected, i.e. the behavior you are trying to achieve? Thanks! Jan 26, 2016 at 5:16
  • 1
    In your hook function, you can try call (call-interactively #'she-render-buffer) with a slight delay by using run-at-time, e.g., (run-at-time 0.1 nil (lambda () (call-interactively #'shr-render-buffer))), to simulate a manual invocation.
    – xuchunyang
    Jan 26, 2016 at 5:35
  • 2
    This is not related to hooks at all, rather using functions which are meant to be used interactively in a programmatic way. If you check the sources of shr-render-buffer, you'll see that it uses pop-to-buffer to show you a *html* buffer. How exactly that happens depends on a number of unknowns as specified by the many display-buffer-related variables.
    – wasamasa
    Jan 26, 2016 at 7:37
  • @Constantine: see the paragraph right after that statement. I was hoping that it would work the way shr-render-buffer does when called interactively. However, I gather from @wasamasa 's comment that this is less straightforward than I thought. I will try @xuchunyang's solution to see if that helps.
    – elethan
    Jan 26, 2016 at 12:58
  • 1
    How about(?): Create a new function that just returns the *html* buffer my-shr-noselect-buf -- without displaying it. Then, have your four (4) favorite functions on stand-by for just such an occasion. Decide which direction you want to display your buffer (left/right/up/down), and then create another function with just one line -- e.g., my-html-display-buffer-left which calls (my-display-buffer-left (my-shr-noselect-buf)) and and that to the hook.
    – lawlist
    Jan 26, 2016 at 17:53

1 Answer 1


(1) shr-render-current-buffer-noselect creates an HTML rendering of the current-buffer, but does not select or display said rendering in any particular window. The value returned is the HTML buffer.

(2) my-display-buffer is a little complex in terms of its capabilities that are dependent upon the built-in library window.el, which is beyond the scope of this example. For purposes of this example, let us just focus on the third argument DIRECTION -- either 'left, 'right, 'above, or 'below. Essentially, there are three built-in options -- (a) if the window already displaying the target buffer exists, then that will become the target window; (b) if there is already a window in the desired direction, then it will be used as the target window; (c) the catch-all is to create a new window in the desired direction if (a) and (b) are not applicable.

(3) my-shr-display-buffer uses a direction of 'right in this example (see above).

(4) The hook.

(require 'shr)

(defun shr-render-current-buffer-noselect ()
"Generate (but do not select) an HTML rendering of the current buffer."
  (or (fboundp 'libxml-parse-html-region)
      (error "This function requires Emacs to be compiled with libxml2"))
  (let* (
      (htmlbuf (get-buffer-create "*html*"))
      (dom (libxml-parse-html-region (point-min) (point-max))) )
    (with-current-buffer htmlbuf
      (shr-insert-document dom)
      (goto-char (point-min)))

(defun my-display-buffer (buffer alist direction &optional size pixelwise)
"BUFFER:  The buffer that will be displayed.
ALIST:  See the doc-string of `display-buffer' for more information.
DIRECTION:  Must use one of these symbols:  'left 'right 'below 'above
SIZE:  See the doc-string for `split-window'.
PIXELWISE:  See the doc-string for `split-window'"
  (let (
          ((get-buffer-window buffer (selected-frame)))
          ((window-in-direction direction))
            (split-window (selected-window) size direction pixelwise)))))
    (window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)

(defun my-shr-display-buffer ()
  (let ((buffer (shr-render-current-buffer-noselect)))
    (my-display-buffer buffer nil 'right nil nil)))

(add-hook 'html-mode-hook 'my-shr-display-buffer)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.