3 Interactive Functions that work on Regions

1 Start Point, End Point and Contents of a Region

The following function prints the contents of the selected region, together with its start and end points.

1: (defun region-contents (rStart rEnd)
2:   "Prints region contents of region selected, and the start and end positions of that region"
3:   (interactive "r")
4:   (setq rStr (buffer-substring rStart rEnd))
5:   (message "The string of the selected region is %s.  It starts at  %d and ends at %d"  rStr  rStart rEnd)
6: )

2 Iterate through the words in a region

You might want to iterate through the words in a selected region. The following function gives an example of this, it prints out the words in the region in reverse order.

1: (defun reverse-region-message (rStart rEnd)
2:   "Reverses order of words"
3:   (interactive "r")
4:   (setq myStr (buffer-substring rStart rEnd))
5:   (setq myWords (split-string myStr))
6:   (setq reversed (reverse myWords))
7:   (message (mapconcat 'identity reversed " "))
8: )

Note the use of the following functions:

  • split-string: splits the string into a list of words
  • reverse: reverses a list
  • mapconcat: Convert a list into a string

It’s interesting to compare the mapconcat function with the concat function. Note that concat does not accept lists, as you might expect at first glance. You can get round this using the apply function.

(concat "this" "that" "other") => "thisthatother"
(concat '("this" "that" "other")) => error
(apply 'concat '("this" "that"  "other")) => "thisthatother"
(mapconcat 'identity '("this" "that" "other") " ") => "thisthatother"

Here’s the function, rewritten to delete the selected region and to replace it with the words in reverse order.

1: (defun reverse-region (rStart rEnd)
2:   "Reverses order of words"
3:   (interactive "r")
4:   (setq myStr (buffer-substring rStart rEnd))
5:   (delete-region rStart rEnd)
6:   (setq myWords (split-string myStr))
7:   (setq reversed (reverse myWords))
8:   (insert (mapconcat 'identity reversed " "))
9:  )

3 Iterate through the letters in a region

You can spend a lot of time writing code to solve a problem only to find that Emacs Lisp already provides a function to do what you want.
For example, suppose you want to iterate through the letters in a region to convert SEPARATE to S-E-P-A-R-A-T-E. The easiest way is to use the mapconcat function, as seen in the previous section.

(mapconcat 'string "SEPARATE" "-")

Here’s the separator as a function that operates on the selected region.

1: (defun region-seperator (rStart rEnd)
2:   "Reverses order of words"
3:   (interactive "r")
4:   (setq rStr (buffer-substring rStart rEnd))
5:   (delete-region rStart rEnd)
6:   (insert (mapconcat 'string rStr "-"))
7:  )

3.1 string-to-list, mapcar and apply

The function string-to-list converts a string to characters.

(setq rStr (string-to-list "example")) => (101 120 97 109 112 108 101)

use char-to-string to convert a character code to a string

(char-to-string 101) => "e"

Note the use of apply, as discussed previously.

(apply 'string (reverse (string-to-list "example"))) => "elpmaxe"

Note that apply returns a value. If you want a list of characters, use mapcar

(mapcar 'char-to-string (string-to-list "example")) => ("e" "x" "a" "m" "p" "l" "e")

Remember, mapcar returns a list, apply returns a value. Many of the errors you make when beginning come down to confusing the two return types.
Finally, use a lambda function (discussed later) for complex functions in mapcar

(mapcar 'char-to-string (mapcar '(lambda (x) (+ x 1)) (string-to-list "foo")))

4 Enclose a Region

This tutorial has been written using emacs org mode. The code samples are marked up using the tags

#+BEGIN_SRC emacs-lisp -n
and
#+END_SRC

I wrote the following function to make the markup easier. Now I just select the region and run the function. The appropriate tags are placed at the start and end of the region.

1: (defun myMark-elisp-region (rStart rEnd)
2:   "Mark region as Elisp source code for org mode export."
3:   (interactive "r")
4:   (save-excursion
5:     (goto-char rEnd) (insert "\n#+END_SRC\n")
6:     (goto-char rStart) (insert "#+BEGIN_SRC emacs-lisp -n\n"))
7: )

Note the use of the save-excursion function to return the point to where it was before the function was run. Note also the way the above function is laid out. An interesting exercise is to reverse the order of lines 5 and 6. See if you can figure out why the function doesn’t work as you might expect.

5 Find and Replace in a Region: Strip Smart Quotes in a Region

These are smart quotes: “ ” ‘ ’
For editing purposes I sometimes need to replace these with the plain quotes ” and ‘. I only want this to replacement to occur on a specified region of text, not on the whole buffer.
Here’s a function to do this. It narrows to a region, uses save-restriction to remember how things were before the narrowing and then uses two regex searches to find first double quotes and then single quotes, replacing both with plain quotes.

 1: (defun strip-smart-quotes (rStart rEnd)
 2:   "Replace smart quotes with plain quotes in text"
 3:   (interactive "r")
 4:   (save-restriction
 5:   (narrow-to-region rStart rEnd)
 6:   (goto-char (point-min))
 7:   (while (re-search-forward "[“”]" nil t) (replace-match "\"" nil t))
 8:   (goto-char (point-min))
 9:   (while (re-search-forward "[‘’]" nil t) (replace-match "'" nil t))
10: ))

6 Choosing between Operating on a Region or the Whole Buffer

The previous example only stripped smart quotes from the selected region. This function uses the (when (uses-region-p) …) construction to determine whether or not a region is selected. If no region is selected, the whole buffer is operated upon.

 1: (defun region-or-buffer ()
 2:  "Strip smart quotes from region, or whole buffer if region not set"
 3:  (interactive)
 4:  (save-excursion
 5:    (save-restriction
 6:      (when (use-region-p) (narrow-to-region (region-beginning) (region-end)))
 7:      (goto-char (point-min))
 8:      (while (re-search-forward "[“”]" nil t) (replace-match "\"" nil t))
 9:      (goto-char (point-min))
10:      (while (re-search-forward "[‘’]" nil t) (replace-match "'" nil t))
11: )))

Next: Yodaizer – Manipulating Strings Example

4 Comments

  1. Satya Kumar says:

    Thank you! I was scratching my head on how to apply a function to a given region. Mapconcat does the trick.

    Like

    1. admin says:

      Glad to help!

      Like

  2. prasad iyer says:

    Amazing blog. It helped me a lot thank you very much. With help of your blog I convert sql datatype to java datatype.

    Like

    1. admin says:

      Glad to help!

      Like

Leave a Comment