11

I often have a situation where I need to apply text around a region. For HTML and simple cases there are libraries for this already. But what about the general case? I'd like to be asked what I'm gonna insert beginning and end of the region.

mike3996
  • 463
  • 4
  • 15
  • Do you want to use it to insert arbitrary content once, or a handful of pairs, many times? In the latter case, libraries such as smartparens could prove useful, and they can be extended with custom pairs. For example, you could add a pair ``, and then, when you select a region and type ` – T. Verron Nov 13 '14 at 15:23
  • @T.Verron: I know that those exist. But for those moments when there's a unique need to go through code and mark stuff a single, general, reusable function fits best – mike3996 Nov 13 '14 at 15:24

3 Answers3

8

I googled around and found this post by John D. Cook. He demonstrates how to build a simple interactive function to get HTML/XML tags around region easily. From that I modified the function to ask twice.

(defun tag-word-or-region (text-begin text-end)
  "Surround current word or region with given text."
  (interactive "sStart tag: \nsEnd tag: ")
  (let (pos1 pos2 bds)
    (if (and transient-mark-mode mark-active)
        (progn
          (goto-char (region-end))
          (insert text-end)
          (goto-char (region-beginning))
          (insert text-begin))
      (progn
        (setq bds (bounds-of-thing-at-point 'symbol))
        (goto-char (cdr bds))
        (insert text-end)
        (goto-char (car bds))
        (insert text-begin)))))

More importantly, this function can be also used as an auxiliary tool, for instance when you want to bind keys to map some specific bits of text.

(defun tag-php ()
  (interactive)
  (tag-word-or-region "<?php " "?>"))
mike3996
  • 463
  • 4
  • 15
  • Btw, the way this handles some boundary cases is not optimal; the search for beginning can fail sometimes and leave both tags at the end. It could be `evil` that I use that interferes here. Perhaps a function of type `interactive "r"` worked more robustly? – mike3996 Nov 13 '14 at 15:20
5

I would suggest a different approach. When there is a region, by definition point must be at the beginning (or end) of the region, and mark will be at the end (or beginning). Which means you can enter the text you want at point, and then use exchange-point-and-mark, i.e. C-x C-x, to jump to the other end of the region. Enter the second half of your wrapper and you're done.

You don't get explicitly asked prompted for the insertion text, but I can't imagine there would be a way to accomplish the same thing with less effort.

Tyler
  • 21,719
  • 1
  • 52
  • 92
  • If the start and end text is the same, I wonder if something like `multiple-cursors` could be tweaked to insert text simultaneously at the beginning and end of the active region. – glucas Nov 13 '14 at 19:02
  • That sounds like the manual way that the users of lesser editors are forced to do anyway. Anyway I read the use of macros as a subtext from your answer, have to say. I like your style – mike3996 Nov 13 '14 at 20:26
  • 1
    @progo - Thanks :) It *sounds* like a more manual approach, but it actually uses **fewer** keystrokes than your alternative. With this approach, other than the wrapping text, you only need `C-x C-x`. With your answer, you need to call your function (`C-c something`), and you also need to hit `return` after each entry. So no matter what you do, you've got two more keystrokes. That said, it doesn't help if you want something to call from elisp. – Tyler Nov 13 '14 at 21:06
3

Just for the sake of variety:

Another way to do this with some arbitrary start and end text would be to use replace-regexp on the region: match .+ and replace with before \& after, or whatever text you want to use for before and after.

You could write a command do to this, but I'm thinking of a case where in a given file you want to apply the same start/end text in multiple places. You can record a macro or just use minibuffer history to repeat on another region.

Of course if the text you want to wrap can be expressed as a regular expression then you won't even need to select a region and can do a query-replace-regexp on the buffer, but presumably the text you want to wrap is not that consistent.

glucas
  • 20,175
  • 1
  • 51
  • 83