13

I have a buffer that shows documentation for Ansible modules. This buffer has its own major mode which provides all the standard stuff. Notably, it fontifies the buffer to highlight inline markup, options, sections, etc. All in all, it is pretty similar to M-x man.

At the bottom of the documentation there are usually some code examples in YAML. I'd like to fontify these as well. I could add my own font lock keywords for YAML, of course, but I'd rather prefer to re-use the fontification of YAML Mode.

How can I do that? Specifically, how can I use a different major mode to fontify a particular region of a buffer?

Note that YAML Mode doesn't need to be active in this region. The whole buffer is only a passive display buffer, and not intended to be edited. I really only need the fontification produced as if YAML Mode were active in that region.

I presume I could copy the region of the buffer to a temporary buffer, enable YAML Mode, fontify the buffer, and the copy all text properties over. Would that work? If so, how do I copy text properties, and what are the relevant properties to copy? Or is there a better approach?

2
  • How is the non-yaml fontification implemented? Do you apply text properties manually, or do you use font-lock-mode?
    – Malabarba
    Dec 15, 2014 at 14:00
  • The links in this post sum-up at least a good chunk of the options out there: wisdomandwonder.com/link/8610/…
    – grettke
    Dec 16, 2014 at 4:24

3 Answers 3

10

I recently came across similar problem, I basically wanted to fontify code snippets in the documentation which I got some other source. I followed the approach mentioned towards the end of your answer and it worked fine for me. The function I ended up with some thing like the following

(defun my-fontify-yaml (text)
  (with-temp-buffer
    (erase-buffer)
    (insert text)
    (delay-mode-hooks (yaml-mode))
    (font-lock-default-function 'yaml-mode)
    (font-lock-default-fontify-region (point-min)
                                      (point-max)
                                      nil)
    (buffer-string)))

As @Malabarba pointed out in comments the simple approach above does not work if destination buffer uses font-lock-mode. However we can trick font-lock-mode into believing that the string is already font locked by setting the text property font-lock-face to the face, (we get the face property set, when we use the function above) and setting text-property fontified to t. The following function takes a string returned by the function above and does the required processing so that the string is inserted fontified (this is taken from org-mode's org-src-font-lock-fontify-block function

(defun my-fontify-using-faces (text)
  (let ((pos 0))
    (while (setq next (next-single-property-change pos 'face text))
      (put-text-property pos next 'font-lock-face (get-text-property pos 'face text) text)
      (setq pos next))
    (add-text-properties 0  (length text) '(fontified t) text)
    text))

Now you can use it as follows

(insert (my-fontify-using-faces (my-fontify-yaml "application: test\nversion: 1")))
5
  • This is the same solution I found for sx.el, but it's important to note it will only work if the destination buffer doesn't use font-lock-mode.
    – Malabarba
    Dec 15, 2014 at 14:02
  • Right! I just realized this worked for me because I used in buffer where font-lock was not used (Help buffer) Dec 15, 2014 at 15:17
  • @Malabarba found a way to make this work with destination buffers with font-lock-mode enabled Dec 15, 2014 at 16:14
  • Many thanks, works perfectly!
    – user227
    Dec 16, 2014 at 8:54
  • Oh nice! Glad I could help :) Dec 16, 2014 at 12:29
3

Library Narrow Indirect (narrow-indirect) can help here -- have a look at the header commentary. It relies on:

  1. narrowing, which limit editing to a portion on a buffer, and
  2. indirect buffers, which copy a buffer in another one for which you can change the major mode.

Note that narrowing is disabled by default because it is considered beginner-unfriendly (the default binding is C-x n n). You can enable it by putting the following in your init file:

(put 'narrow-to-region 'disabled nil)

You can also look at for my handmade myfn-narrow-indirect in my conf.

3
  • Does narrowing actually affect font lock, i.e. keep it away from outside of the narrowed region? Does font lock really never remove buffer restrictions?
    – user227
    Dec 15, 2014 at 22:56
  • And anyway, a wiki package is not an option.
    – user227
    Dec 15, 2014 at 22:56
  • Besides, you don't need to remove the disabled property from a command to call it from Emacs Lisp. It only affects interactive use.
    – user227
    Dec 15, 2014 at 22:58
0

I would suggest looking at org-src-font-lock-fontify-block which provides this functionality for org-mode source blocks. It seems to just copy the relevant parts to another buffer, enable the major mode there, and then copy the fontified text back. Hackish, but it seems to work.

1
  • 2
    This answer looks more like a comment insofar as it provides a suggested direction to deal with the problem, but not a complete answer. Please consider changing it to a comment.
    – Dan
    Dec 15, 2014 at 16:14

Your Answer

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