Emacs comment-box Revisited

Last week I wrote about Comment Boxes In Emacs and gave some examples of how the comment-box command provides an easy way to draw a box around a comment. The only problem is that I want the boxes to extend almost to the right hand margin. Instead of

##########################################
# get-cred -- Get the user's credentials #
##########################################
(defun get-cred ()
...)

I want

##########################################################################
# get-cred -- Get the user's credentials                                 #
##########################################################################
(defun get-cred ()
...)

One could argue, I suppose, that this adds nothing except some useless blanks to the file but I’ve always done it that way and as Henry Higgins might say, “I’ve grown accustomed to its face.” So, how to do it?

My idea was simple: pad the first line with blanks out to the fill column, and then call comment-box to draw the actual box. There’s nothing special about using the fill column as the right hand edge but it makes the function behave well for any size window.

I thought I’d knock off a quick bit of Elisp and be done. It didn’t work out that way. I kept getting a mysterious error that appeared to come from save-restriction (see the code below). After that lot of mucking about I discovered that the real problem was the call to comment-box. The documentation says that there’s an optional third argument to add extra space and, indeed, the beginning of the function is

(defun comment-box (beg end &optional arg)
...)

so I didn’t bother specifying the third argument. It turns out, though, that in the comment-box function we have this line

(comment-region beg end (+ comment-add arg))

so that the optional argument is always used. It works OK in the interactive case because the call to interactive is

(interactive "*r\np")

which causes the prefix argument to be passed to arg. When no prefix argument is specified, this results in a 1 being passed to arg so arg is never nil the way it is if you call comment-box from Elisp with only two arguments. That’s obviously a bug but it’s a small one. At least it’s a small one once you figure out what’s happening.

In any event, once I solved that problem it was easy to write the function:

(defun jcs-comment-box ()
  "Draw a box comment around the region but arrange for the region
to extend to at least the fill column. Place the point after the
comment box."
  (interactive)
  (with-region-or-buffer (b e)
    (save-restriction
      (narrow-to-region b e)
      (goto-char b)
      (end-of-line)
      (insert-char ?  (- fill-column (current-column)))
      (comment-box b (point-max) 1)
      (goto-char (point-max)))))

As you can see, we first move to the beginning of the first line, then to its end, and then we pad the line with blanks out to fill-column. A few comments on the rest of the code:

  • It would probably be easier to use
    (interactive "r")
    

    rather than the with-region-or-buffer macro but I write that almost automatically when I’m dealing with a possible region so I just kept it.

  • insert-char does nothing if the count is not positive so I didn’t have to worry about whether or not the first line already extended to or past the fill column.
  • Using the narrow-to-region function makes it really easy to move around the buffer and helps deal with the fact that I’m changing the size of the region as I go. This problem was described by Xah Lee.
  • Notice that even the narrow-to-region doesn’t completely insulate me from the effects of changing the buffer size. I have to use (point-max) as the end of the region (instead of b) in the call to comment-box because b no longer points to the end of the region.
This entry was posted in Programming and tagged . Bookmark the permalink.