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?

  • How is the non-yaml fontification implemented? Do you apply text properties manually, or do you use font-lock-mode? – Malabarba Dec 15 '14 at 14:00
  • The links in this post sum-up at least a good chunk of the options out there: http://www.wisdomandwonder.com/link/8610/lightweight-multiple-modes-for-semi-literate-programming – grettke Dec 16 '14 at 04:24

3 Answers3

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")))
Iqbal Ansari
  • 7,468
  • 1
  • 28
  • 31
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.

Drew
  • 75,699
  • 9
  • 109
  • 225
freakhill
  • 171
  • 4
  • 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? –  Dec 15 '14 at 22:56
  • And anyway, a wiki package is not an option. –  Dec 15 '14 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. –  Dec 15 '14 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.

Joakim Hårsman
  • 719
  • 5
  • 11
  • 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 '14 at 16:14