From: moore@src.bae.co.UK (Chris Moore)
Subject: adding new functions
Organization: Gatewayed from the GNU Project mailing list help-gnu-emacs@prep.ai.mit.edu
Date: Fri, 24 Jul 1992 12:43:26 GMT


ryanr@lamar.colostate.edu (Richard Ryan) said:

> (defun gt-indent-region ()
>   (shell-command-on-region 4 "sed -e '/^\(.\)/s//> \1/'"))

You need to say the function is interactive before it becomes available to
the user via the M-x way of calling functions.  That's why you were seeing
'no match'.  Adding the interactive will now let you call the function.

ie.

(defun gt-indent-region ()
  (interactive)
  (shell-command-on-region 4 "sed -e '/^\(.\)/s//> \1/'"))

It won't work, since shell-command-on-region needs at least 3 arguments -
the beginning and end of the region and the command to execute.  I can't
quite see what the 4 is supposed to mean in your function.  Fortunately,
interactive provides a way of automatically passing the beginning and end
of the current region to a function.  (See C-h f interactive RET for
details).

(defun gt-indent-region (start end)
  (interactive "r")
  (shell-command-on-region start end "sed -e '/^\(.\)/s//> \1/'"))

This now calls sed on the region with the expression you give.  I don't
know enough sed to understand what this means, but it seems to me that it
just copies input to output.  Emacs puts the output from sed in a "*Shell
Command Output*" buffer.  Giving shell-command-on-region an extra true
argument will cause emacs to replace the region in the original buffer with
the output of sed:

(defun gt-indent-region (start end)
  (interactive "r")
  (shell-command-on-region start end "sed -e '/^\(.\)/s//> \1/'" t))

Another problem:  When you write a string in emacs lisp, backslashes are
used to create certain characters in the string, like the familiar \n, \t
etc of C.  So if you really want a backslash in your string you have to
type two backslashes.  Now your function becomes the following:

(defun gt-indent-region (start end)
  (interactive "r")
  (shell-command-on-region start end "sed -e '/^\\(.\\)/s//> \\1/'" t))

And now it actually works!

> Also, what I'm trying to do with this command is put a "> "
> in the left margin.  If you've got a better function, please
> send me a copy.

Probably something using replace-regexp instead of forking a shell would be
quicker.  Unfortunately, it appears that replace-regexp doesn't work on
regions.  It works from point to end-of-buffer.  One solution to this is to
narrow the buffer to the region of interest before calling replace-regexp.

[later, after much hacking] try this:

(defun gt-indent-region (start end)
  (interactive "r")
  (save-excursion
    ;; Point could currently be at end of region.  We want it at the
    ;; beginning.
    (goto-char start)
    ;; Without this it's possible to insert the '>' halfway along a line.
    (beginning-of-line)
    (save-restriction
      (narrow-to-region (point) end)
      (replace-regexp "^" "> "))))

The save-excursion is used to ensure that point is not moved by the function.
The save-restriction is used to ensure that the buffer is restricted in the
same way after the function returns as it was before it was called.
Although longer, this function is much quicker than forking a shell to run
sed, since all the work's done by lisp.

Chris.
Newsgroups: gnu.emacs.help