Wednesday, August 21, 2013

A Handful of Emacs Utilities

Every now and then, I like to take stock of what's in my .emacs file.  It reminds me of a bush in my yard.  I see it so frequently that I don't notice it growing steadily over time.  One day it just hits you that this thing is big and scraggly and needs to be trimmed way back.

I've learned the hard way that if wait too long before trimming back a bush, there's no going back to the way it used to be.  After trimming off several years worth of growth, you realize that it's not that nice little bush anymore.  It's a big bush that's been hacked into an ugly nub of its former self.

So, in an effort to avoid this situation (sometimes referred to as emacs bankruptsy), I periodically go through my .emacs file, pruning unused functions and reshaping the ones that need a little TLC.

I thought it might be interesting to share some of the utilities I've accumulated.  They have remained in my .emacs file over the years, winning the evolutionary battle to avoid being pruned.  Some are very small and self-contained, a few are slightly more ambitious.

Note: Some of these have been in my .emacs file for a long time.  There may be better alternatives that exist now.  Please let me know in the comments.

inc-num-region

This utility operates on a region of text, incrementing the number it finds on each line.

(defun inc-num-region (p m)
  "Increments the numbers in a given region"
  (interactive "r")
  (save-restriction
    (save-excursion
      (narrow-to-region p m)
      (goto-char (point-min))
      (forward-line)
      (let ((counter 1))
        (while (not (eq (point)
                        (point-max)))
          (goto-char (point-at-eol))
          (search-backward-regexp "[0-9]+" (point-at-bol) t)
          (let* ((this-num (string-to-number (match-string 0)))
                 (new-num-str (number-to-string (+ this-num
                                                   counter))))
            (replace-match new-num-str)
            (incf counter)
            (forward-line)))))))
For example, if you ran this command on a region consisting of:
foo0
foo0
foo0
It would change to:
foo0
foo1
foo2

Note: Reddit user rcfox has informed me that you can do something similar with a standard emacs regex replace with the special "\#" token

 add-code-review-note

Before we started using Review Board at my company, we would do over-the-shoulder reviews.  The add-code-review-note was a utility that I wrote to make it easier to take notes during these reviews.
(defun add-code-review-note ()
  "Add note for current file and line number"
  (interactive)
  (let ((file-name (buffer-file-name))
        (file-line (line-number-at-pos)))
    (switch-to-buffer-other-window (get-buffer-create "NOTES"))
    (goto-char (point-min))
    (when (not (search-forward "-*- mode:compilation-shell-minor"
                               nil t))
      (compilation-shell-minor-mode 1)
      (insert "-*- mode:compilation-shell-minor -*-\n\n"))
    (goto-char (point-max))
    (if (/= (current-column) 0)
        (newline))
    (insert file-name ":" (number-to-string file-line) ": ")))
This function adds notes to a buffer called "NOTES" (which it creates if it needs to).  The nice thing about this utility is that it puts the buffer in compilation-shell-minor-mode and prints out file and line number information in a way that this mode can interpret.  With this minor-mode enabled, file and line information is linked/highlighted and it's easy to jump right to the location in question.

Here is an example of what the NOTES buffer might look like:
-*- mode:compilation-shell-minor -*-
/home/jdavis/test.el:41: bad function name
/home/jdavis/test.el:55: unnecessary code
/home/jdavis/test.el:65: there must be a better way to do this
/home/jdavis/foo.cpp:11: maybe factor out into a separate function?
I have this function bound to "ctrl-c r".

automatic include guards

In C and C++, include guards are a necessary annoyance.  However, with this utility, I don't really have to think about them anymore.
(defun get-include-guard ()
  "Return a string suitable for use in a C/C++ include guard"
  (let* ((fname (buffer-file-name (current-buffer)))
         (fbasename (replace-regexp-in-string ".*/" "" fname))
         (inc-guard-base (replace-regexp-in-string "[.-]"
                                                   "_"
                                                   fbasename)))
    (concat (upcase inc-guard-base) "_")))

(add-hook 'find-file-not-found-hooks
          '(lambda ()
             (let ((file-name (buffer-file-name (current-buffer))))
               (when (string= ".h" (substring file-name -2))
                 (let ((include-guard (get-include-guard)))
                   (insert "#ifndef " include-guard)
                   (newline)
                   (insert "#define " include-guard)
                   (newline 4)
                   (insert "#endif")
                   (newline)
                   (previous-line 3)
                   (set-buffer-modified-p nil))))))
This hook automatically adds an include guard to new header files based on the file name.  So, if you create a new file called foo-bar.h, emacs will automatically insert this:
#ifndef FOO_BAR_H_
#define FOO_BAR_H_

// puts cursor here

#endif

next-file-with-basename

It's a fairly common idiom in C and C++ to have header files and implementation files with the same basename, but different extensions.  Like foo.h and foo.c, or bar.h and bar.cpp.

It's very useful to be able to jump between these files quickly, so I made a utility to make it easy to jump between files in the same directory with the same basename.
(defun next-file-with-basename ()
  "Cycles between files with the same basename as the given file.
   Usefull for cycling between header .h/.cpp/.hpp files etc."
  (interactive)
  (let* ((buf-file-name (replace-regexp-in-string
                         "^.*/" ""
                         (buffer-file-name)))
         (current-dir (replace-regexp-in-string
                       "[a-zA-Z0-9._-]+$" ""
                       (buffer-file-name)))
         (no-basename (equal ?. (aref buf-file-name 0)))
         (has-extension (find ?. buf-file-name)))
    ;; If the file is a .dot-file or it doesn't have an
    ;; extension, then there's nothing to do here.
    (unless (or no-basename (not has-extension))
      (let* ((basename (replace-regexp-in-string
                        "\\..*" ""
                        buf-file-name))
             (files-with-basename (directory-files
                                   current-dir f
                                   (concat "^" basename "\\."))))
        ;; If there's only 1 file with this basename, nothing to
        ;; do
        (unless (= (length files-with-basename) 1)
          ;; By making the list circular, we're guaranteed that
          ;; there will always be a next list element (ie. no
          ;; need for special case when file is at the end of
          ;; the list).
          (setf (cdr (last files-with-basename))
                files-with-basename)
          (find-file (cadr (member (buffer-file-name)
                                   files-with-basename))))))))
I have this function bound to "ctrl-c n"

Note: Reddit users davexunit and slavy pointed out a builtin function ff-find-other-file does something very similar to this.

in-line macro expansion

The last utility that I'd like to share is one that I wrote to escape the tedium of writing small snippets of boilerplate code.

Boilerplate code never bothered me much until I learned lisp.  Once you have something like lisp macros available, it's harder to stomach having to type very similar code snippets over and over again in other languages.

Since most languages (like C and C++) aren't homoiconic, there's no real chance of something like lisp macros helping us out here.

Something that would save me some time though would be if I could write shorthand s-expressions in my C and C++ code and have them automatically expand into the boilerplate code as I type.
(defun j-newline-and-indent ()
  "Same as \"newline-and-indent\" except it also expands 
   c-macros if it sees one."
  (interactive)
  (if (and (equal (char-before) ?\))
           (macro-function (car (preceding-sexp))))
      ;; This is a c-macro
      (expand-c-macro-in-place)
    (newline-and-indent)))

(defun macro-function (name)
  "Given a name, returns the c-macro-name symbol if it 
   exists as a function"
  (let ((macro-sym (intern (concat "c-macro-"
                                   (symbol-name name)))))
    (if (fboundp macro-sym)
        macro-sym
      nil)))

(defun expand-c-macro-in-place ()
  "Given that point is at the end of a c-macro, expands
   it in-place"
  (let* ((sexp (preceding-sexp))
         (macro-name (car sexp))
         (replacement-text (apply (macro-function macro-name)
                                  (cdr sexp)))
         (jump-to (string-match "!!!BODY!!!;" replacement-text)))
    ;; Delete macro invocation
    (backward-list)
    (let ((start-del (point)))
      (forward-list)
      (kill-region start-del (point))

      ;; Insert macro expansion and indent appropriately
      (insert replacement-text)
      (indent-region start-del (point))
      (when jump-to
        (search-backward "!!!BODY!!!;")
        (kill-line))))
  (c-indent-command))
This code replaces the normal "newline-and-indent" keybinding of ctrl-j with "j-newline-and-indent".  The j-newline-and-indent function looks for what I call a c-macro before the cursor and expands it in place if found.  Otherwise, it falls back to the normal newline-and-indent behavior.

In order for this to be useful, we need to define some c-macros.  These are just normal elisp function that start with "c-macro-".
(defun c-macro-doit (container-type arg1 &optional arg2)
  "Emits code for iterating over an stl (or stl-like) structure"
  (let ((iterator-name  (if arg2 arg1 "it"))
        (container-name (if arg2 arg2 arg1)))
    (format (concat "for (%s::iterator %s = %s.begin();\n"
                    "     %s != %s.end();\n"
                    "     ++%s)\n"
                    "{\n"
                    "   !!!BODY!!!;\n"
                    "}\n")
            container-type
            iterator-name
            container-name
            iterator-name
            container-name
            iterator-name)))

(defun c-macro-api-fn ()
  "Emits code for wrapping an api function in a try/catch block"
  (concat "try\n"
          "{\n"
          "   !!!BODY!!!;\n"
          "}\n"
          "catch(const std::exception& e)\n"
          "{\n"
          "   TRACE(\"Unhandled exception in function %s: %s\\n\",\n"
          "         __func__, e.what());\n"
          "   return -1;\n"
          "}\n"))
These are just 2 examples of c-macros.  As you can see, it's pretty easy to add new ones.
(doit std::vector<int> myContainer)
// at end of the previous line, hit C-j and this expands to:
for (std::vector<int>::iterator it = myContainer.begin();
     it != myContainer.end();
     ++it)
{
   // cursor goes here  
}

// You can also specify the iterator name if you want
(doit std::vector<int> myIt myContainer)
// expands to
for (std::vector<int>::iterator myIt = myContainer.begin();
     myIt != myContainer.end();
     ++myIt)
{
   // cursor goes here
}

(api-fn)
// expands to
try
{
   // cursor goes here
}
catch(const std::exception& e)
{
   TRACE("Unhandled exception in function %s: %s\n", 
         __func__, e.what());
   return -1;
}

Note: Reddit users aaptel and stack_pivot informed me about yasnippet which looks like it does this and a whole lot more.

Conclusion

So, there you have it.  Those are some of the utilities that have stood the test of time for me.  They help define the shape of the pretty little bush that is my .emacs file.

What are some small utilities that you find indispensable?  Let me know in the comments.

2 comments:

  1. Hey there. Thanks for the utilities. I like the first one. I remeber doing something similar a few years back but usnig an emacs macros. Your solution is more elegant and generic.

    ReplyDelete
  2. Your in-line macro expansion is an excellent candidate for yasnippet.

    ReplyDelete