2010-01-21

duplicating lines and commenting them

Someone on the Emacs Help mailing list asked for an easy way to duplicate a line
and, optionally, comment-out the first one.

Let's first look at simple duplication of a line. This is a common operation,
and vi-users might use something like Yp for that. In emacs, one way to do
this is by typing C-a C-k C-k C-y C-y, which is actually not as bizarre as
it looks if you try it. When using slick copy, as explained in another post,
it's as easy as M-w C-n C-y; and of course it's easy to define a shorter key
binding.

repeat after me
repeat after me

However, neither of these methods works correctly when you're on the last line
of the buffer (by default at least). Also, it puts the point (cursor) below
the duplicated line, while I'd like to put it at the start of it. It seems we
need something a little smarter.

While we're at it, let's also consider the second question: commenting-out the
first line of the duplicates. This is a quite common thing to do when writing
programs or configuration files; you want to try the effect of a small
variation of a line, but want to keep the original so it can be restored when
the variation turns out not to be as good as expected.

/* for (;;) fork (); */
for (;;) fork ();

I hacked up something quickly to solve both questions, and it has evolved a
little bit since – to answer both of the questions. The bit of weirdness in
the end is because of the special case of the last line in a buffer. It
defines key bindings C-c y for duplicating a line, and C-c c for
duplicating + commenting – but of course you can change those.

(defun djcb-duplicate-line (&optional commentfirst)
  "comment line at point; if COMMENTFIRST is non-nil, comment the original" 
  (interactive)
  (beginning-of-line)
  (push-mark)
  (end-of-line)
  (let ((str (buffer-substring (region-beginning) (region-end))))
    (when commentfirst
    (comment-region (region-beginning) (region-end)))
    (insert-string
      (concat (if (= 0 (forward-line 1)) "" "\n") str "\n"))
    (forward-line -1)))

;; or choose some better bindings....

;; duplicate a line
(global-set-key (kbd "C-c y") 'djcb-duplicate-line)

;; duplicate a line and comment the first
(global-set-key (kbd "C-c c") (lambda()(interactive)(djcb-duplicate-line t)))

2010-01-16

rectangles and cua

CUA-mode is a minor-mode that enables the use of Ctrl-X/C/V for cut/copy/paste, as is customary in many computer programs. Of course, it's a bit different in emacs, as it predates CUA and all of those programs. With esp. Ctrl-X (C-x) being in heavy use as a prefix-key already, it's unlikely to change.

CUA-mode has a clever trick to solve that problem – C-x for cut only works when a selection is active, and when no other key is pressed shortly. Otherwise, C-x behaves as usual. This works quite nicely, but personally, I don't use it, as I already have the Emacs key bindings in my muscle memory. Still, it can be useful for people migrating from other CUA-based editors. See e.g. CuaMode for more information.

However, apart from the C-x-trick, CUA also has some nice functionality for rectangular selections. These are sometimes quite useful, and during emacs-fu's prehistory there was already an article about it. The method there works, but with CUA, it is much easier.

So, let's turn it on; put the following in your .emacs:

(setq cua-enable-cua-keys nil) ;; only for rectangles
(cua-mode t)

Now, just put your cursor anywhere, and press C-RET (Ctrl + Enter). You have now started a rectangular selection! There rest is pretty straightforward, you can cut, copy and paste with the normal Emacs key bindings..

I could go into more detail, but the best way is to see it in action in this wonderful screen cast by Mark Mansour. I especially like the way you can add numbered lists.