Vim vs. Emacs: Indenting text before copying
I use Markdown on my blog for posts and comments, and I post at other sites that use Markdown (e.g. Stack Overflow). In Markdown, text indented four spaces is displayed as code, in pre tags.
I find myself often writing code in Vim or Emacs and needing to copy/paste it into a browser in a Markdown-suitable way.it back. This is easy to do in Vim and Emacs, only a few keystrokes. But "a few" is still greater than "one", so the heck with that. Let's script it.
Vim version
This keymapping in Vim will do it all for me:
vmap <Leader>y :s/^/ /<CR>gv"+ygv:s/^ //<CR>
One clumsy thing about Vim is needing to restore the previous visual selection after each regex-replacement. I could use the marks '< and '> as ranges to :s instead, but that's more typing than simply doing gv in the mapping. Copying to the system clipboard is easy because Vim has a register "+ for that purpose.
This took me maybe 45 seconds to write, probably due to being pretty familiar with Vim already. But in Vim, mappings are easy. You just type the characters that you'd type if you were doing it manually.
Emacs version
Trying to do the same in Emacs was painful. My Emacs-fu is sorely inadequate, compared to my Vim-jitsu. This seems to work, but ugh:
;; adapted from http://www.emacswiki.org/emacs/.emacs-ChristianRovner.el
(defun expand-region-linewise ()
(interactive)
(let ((start (region-beginning))
(end (region-end)))
(goto-char start)
(beginning-of-line)
(set-mark (point))
(goto-char end)
(unless (bolp) (end-of-line))))
(defun markdown-copy ()
(interactive)
(save-window-excursion
(save-excursion
(save-restriction
(expand-region-linewise)
(narrow-to-region (region-beginning) (region-end))
(goto-char (point-min))
(replace-regexp "^" " ")
(clipboard-kill-ring-save (point-min) (point-max))
(goto-char (point-min))
(replace-regexp "^ " "")))))
Writing this involved a long journey through the Emacs documentation.
One difficulty was getting Emacs to play friendly with my fat-fingered region-marking. I don't always highlight from the beginning of the first line to the end of the last. That's why Vim's visual-line mode is awesome; the cursor can be anywhere on the line, it still selects the whole line. The handy function above (found on the Emacs wiki) takes care of that. I don't know how long it would've taken me to come up with that on my own.
Then it was a matter of rooting through millions of Emacs functions until I found the ones that move the point around and copy text to the clipboard. Along the way I discovered the wonders of "narrowing", which limits Emacs to work on some region of text, and all those macros to undo the messes I make while moving around.
Maybe I could've done this with an Emacs keyboard macro, and then called apply-macro-to-region-lines. And maybe I could use append-next-kill to build up the indented text one line at a time. But my efforts to do this or anything like it failed horribly.
In any case I thought it was an interesting comparison. Improvements to either version are welcome.
EDIT: This works too (thanks Holger Durer):
(defun markdown-copy ()
(interactive)
(save-excursion
(expand-region-linewise)
(indent-rigidly (region-beginning) (region-end) 4)
(clipboard-kill-ring-save (region-beginning) (region-end))
(indent-rigidly (region-beginning) (region-end) -4)))

7 Comments
Wouldn't using
indent-rigidlybe much easier? It's bound toC-x TABand I use it interactively all the time.The mapping is nice, but you could easily achieve the same with a simple insert. Do ctrl-v for column visual select, select the first column of the chunk of code you want to copy, press
I(capital i), put your 4 spaces in front,ESC, and move anywhere - the chunk has been indented. Now just copy it as usual with visual select, then when you're done just undo a couple times to get it back to what it was like before.How about
>apin normal mode? That will indent a paragraph for you, no matter where in it you are. If you have more than one paragraph2>apand so on.Assuming your
shiftwidthis 4, of course.Hey ! Comparing vim's visual-mode with the emacs documentation is not fair.
What's more fair would be to compare it with emacs' rectangle mode.
Your selected lines will be indented with two spaces. There ! I just saved you from a trip of 45 more minutes to the emacs docs.
I agree with you that Vim wins this one, hands down. But at least I could give you one more hint on Emacs
A nice way to do this is with CUA mode's rectangular selections: with cua-selection-mode enabled, just C-RET at the beginning of a line, then UP/DOWN to select lines, and finally type four spaces. Press C-RET or C-g to finish.
CUA's rectangle mode is how I used to do it in Emacs (and visual block mode is how I used to do it in Vim). But it's tedious to do 6 or 7 times in a row. That's why I want to script it. Is there an easier way to script Emacs to do rectangle mode manipulations via a function call?
@Holger Durer: Thanks, I didn't know about that function. That works well.
Building on Leonel's example, for good measure, wrap those commands in a macro on the macro kill ring:
Now hitting F4 will repeat this macro as often as you like :). These macros are super easy to hack together when you're too lazy to write a function. (C-x C-k C-h for more macro commands.) You can assign it to a key sequence with Elisp too; for example, a hotkey to "Alt-tab" buffers would be:
(global-set-key (kbd "C-x j") (kbd "C-x b RET"))
Speak your Mind
Preview