0
votes

I'm trying to write an emacs LISP function to un-indent the region (rigidly). I can pass prefix arguments to indent-code-rigidly or indent-rigidly or indent-region and they all work fine, but I don't want to always have to pass a negative prefix argument to shift things left.

My current code is as below but it seems to do nothing:

(defun undent ()
    "un-indent rigidly."
    (interactive)
    (list
        (setq fline (line-number-at-pos (region-beginning)))
        (setq lline (line-number-at-pos (region-end)))
        (setq curIndent (current-indentation))
        ;;(indent-rigidly fline lline (- curIndent 1))
        (indent-region fline lline 2)
        ;;(message "%d %d" curIndent (- curIndent 1))
    )
)

I gather that (current-indentation) won't get me the indentation of the first line of the region, but of the first line following the region (so a second quesiton is how to get that!). But even when I just use a constant for the column (as shown, I don't see this function do any change. Though if I uncomment the (message) call, it displays reasonable numbers.

GNU Emacs 24.3.1, on Ubuntu. And in case it matters, I use (setq-default indent-tabs-mode nil) and (cua-mode).

I must be missing something obvious... ?

2

2 Answers

2
votes

All of what Tim X said is true, but if you just need something that works, or an example to show you what direction to take your own code, I think you're looking for something like this:

(defun unindent-rigidly (start end arg &optional interactive)
  "As `indent-rigidly', but reversed."
  (interactive "r\np\np")
  (indent-rigidly start end (- arg) interactive))

All this does is call indent-rigidly with an appropriately transformed prefix argument. If you call this with a prefix argument n, it will act as if you had called indent-rigidly with the argument -n. If you omit the prefix argument, it will behave as if you called indent-rigidly with the argument -1 (instead of going into indent-rigidly's interactive mode).

2
votes

There are a number of problems with your function, including some vary fundamental elisp requirements. Highly recommend reading the Emacs Lisp Reference Manual (bundled with emacs). If you are new to programming and lisp, you may also find An Introduction to Emacs Lisp useful (also bundled with Emacs).

A few things to read about which will probably help

  • Read the section on the command loop from the elisp reference. In particular, look at the node which describes how to define a new command and the use of 'interactive', which you will need if you want to bind your function to a key or call it with M-x.

  • Read the section on variables from the lisp reference and understand variable scope (local v global). Look at using 'let' rather than 'setq' and what the difference is.

  • Read the section on 'positions' in the elisp reference. In particular, look at 'save-excursion' and 'save-restriction'. Understanding how to define and use the region is also important.

It isn't clear if your writing this function just as a learning exercise or not. However, just in case you are doing it because it is something you need to do rather than just something to learn elisp, be sure to go through the Emacs manual and index. What you appear to need is a common and fairly well supported requirement. It can get a little complicated if programming modes are involved (as opposed to plain text). However, with emacs, if what you need seems like something which would be a common requirement, you can be fairly confident it is already there - you just need to find it (which can be a challenge at first).

A common convention is for functions/commands to be defined which act 'in reverse' when supplied with a negative or universal argument. Any command which has this ability can also be called as a function in elisp code with the argument necessary to get that behaviour, so understanding the inter-play between commands, functions and calling conventions is important.