Back to home

How to hide long C headers

Published:

The problem came out to me because some people or teams embed their changelog in the source file headers.


This gives something like this:

/**
 * @file MySource.cpp
 * @desc a Foo objects protubazor.
 * @date 29/03/2009
 *
 * MODIFICATIONS:
 * 2009-04-02: jsmith: Removed a useless variable.
 * 2009-04-03: jsmith: Simplified the protubazor algorithm.
 * 2009-06-10: jdoe: Fixed compilation warning.
 */

They will argue that this is the best way to check why a line has changed in the past for example. I disagree with this. First, there should be a file dedicated to the whole project history to trace important updates. In GNU project, this is the ChangeLog file. But above all, sources tracking is the very raison d’être of version control. Every commit should be atomic and provide a meaningful comment. If commits are correctly described, writing a changelog inside the source is, at best, duplication of information, and I hate that.

Besides, I think this is a very annoying habit because this information will grow with time and eventually become as large as the useful part of your source file. At last, working on these files will involve lots of scrolling just to find the beginning of the code.

Anyway, there are those situations where I have no choice but to obey, or to adapt myself. The solution I found is to hide these annoying lines until I get forced to edit them (and then quickly hide them again). To achieve this, I’ve written a function to customize the hideshow minor mode from emacs behaviour on file headers. It can be loaded this way for example:

(load-library "hideshow")
(add-hook 'c++-mode-hook 'hs-minor-mode)
(add-hook 'c-mode-hook 'hs-minor-mode)

hideshow allows you to hide blocks of code. The meaning of blocks depends on the language you are editing, I mean the current major-mode. When hidden, the block is replaced by an ellipsis by default, but the hs-set-up-overlay function can be used to customize what is drawn inside a hidden block.

In the following implementation, I tweak the hideshow overlay so as to indicate the number of missing lines in hidden blocks. If I detect that the given block is a kind of comment and is at the beginning of the file, I also search for the MODIFICATIONS: token, which is used to introduced those damn changelogs. If I found it, I don’t really hide the block, but display its content minus everything following the token (which I replace with the count of hidden lines).

(setq hs-set-up-overlay
      (defun ds-lib-hs-overlay (ov)
        (let ((content (format " [%d lines]..."
                               (count-lines (overlay-start ov)
                                            (overlay-end ov)))))
          (when (and (< (overlay-start ov) 80) (eq 'comment (overlay-get ov 'hs)))
            (goto-char (point-min))
            (goto-char (point-min))
            (when (search-forward " MODIFICATIONS:" (overlay-end ov) t)
              (setq content (format "%s [%d lines]...\n */ "
                                    (buffer-substring (overlay-start ov) (point))
                                    (- (count-lines (point) (overlay-end ov)) 2)))))
          (overlay-put ov 'display content))))

(add-hook 'c++-mode-hook 'hs-hide-block t)
(add-hook 'c-mode-hook 'hs-hide-block t)

Thus, the sample given in the beginning would be displayed this way:

/**
 * @file MySource.cpp
 * @desc a Foo objects protubazor.
 * @date 29/03/2009
 *
 * MODIFICATIONS: [3 lines]...
 */

Note that even if it seems to be displayed because I draw a large part of it, the block is actually in an hidden state. That means that it must be showed again before it can be edited at all.