1

I wrote a function to show me a list of all defuns in the current buffer. The output is syntactically incorrect Lisp code (if you can even call it Lisp code, being syntactically a mess). Here's what the output looks like in the help window:

List of results:

(defvar var-one nil "Var one doc")
(defvar var-two  nil "Var two doc")
(defun foobar (one two)
(defmacro mymacro (body)

If possible, I would like to fontify the display a bit, to color the defun words and the function names in some way.

I tried to do what org-mode does (suggested on another thread on SO). That is, copy my results to a temp buffer, enable Lisp mode, run font-lock-fontify-buffer in Lisp mode on the temp buffer, and copy the fontified results back. But that didn't work, for two reasons. First, Lisp mode tried to indent my results like a mess of cascaded, nested defuns (ugh).

The main problem seems to be that with-help-window loses or blocks all font locking. Maybe it's because of the princ statements used to generate output into the help window?

Here's my display code (which does not work for keeping font info):

(with-help-window (help-buffer)
  (princ (concat title "\n\n"))
  (princ alldefuns)
  (lisp-mode)  ; doesn't help, in or out
  (font-lock-fontify-buffer))

My questions are:

Is it possible to display fontified text using with-help-window?

Or is there an alternate route that I should try? (I suppose I could go study the occur source, which displays the same kind of results in a normal buffer. But I'd like to stick with with-help-window approach if I could...)

Update for @lawlist comments below:

Here is the code that generates the list of defuns from the current buffer. The code looks like it should correctly collect and preserve font-lock info from the current buffer because it uses buffer-substring to extract the lines.

(defun ct-u-buffer-defuns-list ()
  "Return a stringlist of defuns in current buffer."
  (let (result buf-contents tmp regex)
    (setq buf-contents (buffer-string))
    (with-temp-buffer
      (insert buf-contents)
      (goto-char (point-min))
      (setq regex "^(defun")
      (while (re-search-forward regex nil t)
        (let (bol eol)
          (setq bol (line-beginning-position))
          (setq eol (line-end-position))
          (setq tmp (buffer-substring bol eol))
          (setq result (concat result tmp "\n")))))
    result))

But on a 20 line display displayed in a normal buffer using the code below executed in the scratch buffer, only the first 10 lines retain their font-lock info. Or sometimes it's another 5 or 10 lines in a 30 line display. I don't know why.

  (let (alldefuns)
    (switch-to-buffer "ct-utils.el")
    (setq alldefuns (ct-u-buffer-defuns-list))
    (switch-to-buffer (get-buffer-create "foobar"))
    (insert "List of results:\n")
    (insert alldefuns))

So it seems that at least part of the font-lock properties are collected and preserved by the extraction (search) code, and at least part of the font-lock properties are preserved by inserting the collected string into a temp buffer (Fundamental mode is active in the temporary buffer).

Further, when I try the following bits of code in the *scratch* buffer as suggested in the comments (alldefuns is a variable that contains the lines extracted from the code buffer), I get the following results:

(defun foo ()
  "Insert a string of defun lines in the current buffer."
  (insert alldefuns))

;;works perfectly in Lisp mode scratch buffer
(foo) 

;; only displays 10 of 30 lines with font lock info
(let ((temp-buffer-window-setup-hook 'foo))
  (help-window-full-frame (help-buffer)
      (princ "This title appears last in output.\n")))

It's a puzzle why displaying in (1) a temporary help buffer, or (2) in a temporary Fundamental mode buffer, only shows 1 chunk of 10 lines or so of font-lock info.

Kevin
  • 1,308
  • 8
  • 20
  • Are you extracting the variables/functions/macros from Lisp files that are already in `emacs-lisp-mode` such that they might already contain the correct highlighting? Perhaps it is just a matter of adjusting the extraction method to preserve the highlighting . . . Or, are you actually using `grep` to extract the list of results? Perhaps providing a little more details about the extraction method might generate other potential solutions . . . – lawlist Jul 09 '16 at 23:14
  • Hi lawlist, interesting viewpoint. No, I'm not using grep. I just use a `while (string-match "(defun")` loop to walk the current buffer, and `(setq string (concat string (buffer-line-contents) "\n"))` to build up the string that is displayed. The main problem is that the help window method strips the font lock info. I can get some font-lock colors by copying and fonting in a temp buffer, and bringing back the fontified string. But then it's lost in the help window process. My guess is that I'll end up using a normal buffer like `occur` does. – Kevin Jul 10 '16 at 02:04
  • `buffer-substring` preserves the text-properties, so if you use that function when gathering the results, then it won't be necessary to fontify anything afterwords and there would be no need to use a help-buffer. There is no such animal as `buffer-line-contents` as far as I am aware. You can just use a plain old buffer and add things like a `special-mode-map` to quit the buffer with `q` and so forth. – lawlist Jul 10 '16 at 03:21
  • And here is another option for you. Gather your results with `buffer-substring` and save it in a variable -- e.g., `my-variable`, and use a simple function like `(defun foo () (insert my-variable))` Then, use this: `(let ((temp-buffer-window-setup-hook 'foo)) (help-window-full-frame (help-buffer)))` in conjunction with the macro that we wrote together a couple of weeks ago: http://emacs.stackexchange.com/a/24037/2287 Of course, you can use a list to store the information and populate your help buffer accordingly. – lawlist Jul 10 '16 at 03:37
  • Thank you for your continued comments. As you requested, I updated the original question with more coding details, and with my test results. I think you're right that I'll end up using a normal buffer (vs a help buffer), and rebinding the q key. I think I'll go have a look at the `occur` source code now, to see if I can figure out how he does it. – Kevin Jul 10 '16 at 05:31
  • Font-lock doesn't fontify the whole buffer by default, it does it in hunks and with timers and it focuses on what's visible in the window first, and also what was recently changed by the user. I don't have a solution (off the top of my head) other than to force the entire fontification of the source buffer beforehand, but I'd need to look up how to do it. Anyway, 99.9999% that is the issue you are experiencing at this juncture. There is probably a fontify region function that can be forced with `point-min` and `point-max` as the bounds. Perhaps you can fontify while gathering the data. – lawlist Jul 10 '16 at 05:40
  • Thank you once again. Wow, hunks and timers! That explains it--I never would have guessed that. My first attempts used `font-lock-fontify-buffer`, but the `princ` wiped out that info, so I stopped using it. So for now, I have added a trailing ) to each extracted line to make it look like valid code, and then use `(lisp-mode)` on the display buffer. It displays enough color for me, so I don't have to mess with `font-lock-fontify-buffer` or the _many_ techniques in the `occur-1` and `occur-engine` sources. Best regards. – Kevin Jul 10 '16 at 05:53

1 Answers1

1

Here's the code that I am using for now. It displays the output lines in normal font-lock colors for Lisp-mode. Note that I had to add a trailing paren ) to each extracted (defun foo (..) line (see the extraction function above), to make it valid Lisp code for the (lisp-mode) call in the sample code below.

  (let (title alldefuns)
    ;; get a list of matches
    (setq alldefuns (ct-u-buffer-defuns-list))
    (switch-to-buffer (get-buffer-create "Defun-output"))
    (lisp-mode)
    (local-set-key "q" (lambda () (interactive) (kill-buffer)))
    (setq title "List of results:\n")
    (insert alldefuns))
Kevin
  • 1,308
  • 8
  • 20