(or emacs irrelevant)

The Elisp Synergy

After having written a few Emacs packages, it happens sometimes when I'm reading questions on Emacs Stack Exchange, I think to myself that either:

I've done this before in package X

or:

This feature would fit nicely in package Y

On very rare occasions it happens that a question matches two packages at once. Here is such a question:

Is there a yasnippet producing a prepopulated doxygen comment?

For the following C++ function:

bool importantStuff(double a, double b);

It should output the following snippet, perhaps without the tags:

/**
* <Insert description of importantStuff>
*
* @param a <Insert description of a>
* @param b <Insert description of b>
* @return <Insert description of the return value>
*/

Well, I've got a package called function-args for C++ that uses CEDET to:

  • display tool-tips with function arguments
  • jump to a semantic tag in current file
  • do a bit of completion
  • do a bit of generation (like inherited function signatures)

And the expansion part can be handled by auto-yasnippet, which I've covered in an earlier post.

The code

I think it might be useful to look at the code, since it's small and shows how to use CEDET's and auto-yasnippet's API:

(defun moo-doxygen ()
  "Generate a doxygen yasnippet and expand it with `aya-expand'.
The point should be on the top-level function name."
  (interactive)
  (move-beginning-of-line nil)
  (let ((tag (semantic-current-tag)))
    (unless (semantic-tag-of-class-p tag 'function)
      (error "Expected function, got %S" tag))
    (let* ((name (semantic-tag-name tag))
           (attrs (semantic-tag-attributes tag))
           (args (plist-get attrs :arguments))
           (ord 1))
      (setq aya-current
            (format
             "/**
* $1
*
%s
* @return $%d
*/
"
             (mapconcat
              (lambda (x)
                (format "* @param %s $%d"
                        (car x) (incf ord)))
              args
              "\n")
             (incf ord)))
      (aya-expand))))

The bonus

If you're interested in Doxygen, here's a bit of code that I found laying around. It will prettify e.g. <tt>Numerical Recipies</tt> to Numerical Recipies in the comments. This code uses a similar approach to the one used in my posts about prettifying Elisp regex, and ElTeX.

(defface font-lock-doxygen-face
    '((nil (:foreground "SaddleBrown" :background "#f7f7f7") ))
    "Special face to highlight doxygen tags such as <tt>...</tt>
and <code>...</code>."
    :group 'font-lock-highlighting-faces)

(font-lock-add-keywords
 'c++-mode
 '(("\\(<\\(?:code\\|tt\\)>\"?\\)\\([^<]*?\\)\\(\"?</\\(?:code\\|tt\\)>\\)"
    (0 (prog1 ()
         (let* ((expr (match-string-no-properties 2))
                (expr-len (length expr)))
           (if (eq 1 expr-len)
               (compose-region (match-beginning 0)
                               (match-end 0)
                               (aref expr 0))
             (compose-region (match-beginning 1)
                             (1+ (match-end 1))
                             (aref expr 0))
             (compose-region (1- (match-beginning 3))
                             (match-end 3)
                             (aref expr (1- expr-len)))))))
    (0 'font-lock-doxygen-face t))))

Outro

I hope that the code listed here will be useful to someone other than me. It's a nice highlight of how a language can be made much cooler by just having a package manager that allows to quickly glue various things together. I mean, what would happen to JavaScript without npm (besides stopping people requiring Angular just to use arrays)?

C++, on the other hand, is too cool to care about such things as package managers and modules and stuff. But we're stuck with it for performance reasons, so might as well try to make the experience more bearable by adding some niceties to c++-mode.