How to bind keys to indent/unindent region in emacs? - emacs

I want to define two key-bindings to indent/unindent region by 4 spaces.
Before:
hello
world
foo
bar
Visually select world and foo.
Type >
After:
hello
world
foo
bar
I also want to bind < to unindent region.
I'm not familiar with emacs, please help.

There are already keyboard shortcuts for that:
Indent: C-u 4 C-x TAB
Unindent C-u - 4 C-x TAB
If you find that too long to type, you could put the following in your .emacs file:
(defun my-indent-region (N)
(interactive "p")
(if (use-region-p)
(progn (indent-rigidly (region-beginning) (region-end) (* N 4))
(setq deactivate-mark nil))
(self-insert-command N)))
(defun my-unindent-region (N)
(interactive "p")
(if (use-region-p)
(progn (indent-rigidly (region-beginning) (region-end) (* N -4))
(setq deactivate-mark nil))
(self-insert-command N)))
(global-set-key ">" 'my-indent-region)
(global-set-key "<" 'my-unindent-region)
With this code the greater than (>) and less than (<) keys will indent/unindent a marked region by 4 spaces each.

(defun keyboard-indent (&optional arg)
(interactive)
(let ((deactivate-mark nil)
(beg (or (and mark-active (region-beginning))
(line-beginning-position)))
(end (or (and mark-active (region-end)) (line-end-position))))
(indent-rigidly beg end (* (or arg 1) tab-width))))
(defun keyboard-unindent (&optional arg)
(interactive)
(keyboard-indent (* -1 (or arg 1))))

In addition to what #Thomas already wrote, you might not want to use the keys < and > for indenting or unindenting. Just image you need to write some HTML and can't enter those characters anymore. This is why I inserted the following in my init file, as key settings:
(global-set-key (kbd "C-<") 'my-indent-region)
(global-set-key (kbd "C->") 'my-unindent-region)
Note: It doesn't work without the (kbd ...). You will get an error:
global-set-key: Key sequence C - > starts with non-prefix key C

You can use replace 4 with tab-width as
(defun indent-region-shift-right-n (N)
(interactive "p")
(if (use-region-p)
(progn (indent-rigidly (region-beginning) (region-end) (* N tab-width))
(setq deactivate-mark nil))
(self-insert-command N)))
(defun unindent-region-shift-left-n (N)
(interactive "p")
(if (use-region-p)
(progn (indent-rigidly (region-beginning) (region-end) (* N (- tab-width)))
(setq deactivate-mark nil))
(self-insert-command N)))
(global-set-key ">" 'indent-region-shift-right-n)
(global-set-key "<" 'unindent-region-shift-left-n)

Related

Switch parts of adjacent lines

I have this code that switch lines up/down:
;; Moving a line up or down
(defun move-text-internal (arg)
(cond
((and mark-active transient-mark-mode)
(if (> (point) (mark))
(exchange-point-and-mark))
(let ((column (current-column))
(text (delete-and-extract-region (point) (mark))))
(forward-line arg)
(move-to-column column t)
(set-mark (point))
(insert text)
(exchange-point-and-mark)
(setq deactivate-mark nil)))
(t
(let ((column (current-column)))
(beginning-of-line)
(when (or (> arg 0) (not (bobp)))
(forward-line)
(when (or (< arg 0) (not (eobp)))
(transpose-lines arg))
(forward-line -1))
(move-to-column column t)))))
(defun move-text-down (arg)
"Move region (transient-mark-mode active) or current line
arg lines down."
(interactive "*p")
(move-text-internal arg))
(global-set-key [M-S-down] 'move-text-down)
(defun move-text-up (arg)
"Move region (transient-mark-mode active) or current line
arg lines up."
(interactive "*p")
(move-text-internal (- arg)))
(global-set-key [M-S-up] 'move-text-up)
I was wondering if it is possible to tweak the move-text-internal function so it is possible to move part of line "after cursor" up or down.
Here is an example:
Before:
A X B W
Q E O P
If cursor was after X on the first line, after M-S-down:
A X O P
Q E B W
UPDATE:
Thanks to Jordan Biondo for the the his code and function.
I tweaked it to keep line moving as long as you keep invoking the command.
(defun flip-text (&optional arg)
"Flip the text from point to the end of the current line with the text
in the next line from the same column to the end of the next line.
With a prefix arg, flip text with the line above the current."
(interactive "p")
(save-excursion
(let ((tt (delete-and-extract-region (point) (point-at-eol)))
(c (current-column)))
(forward-line arg)
(move-to-column c)
(insert tt)
(let ((ot (delete-and-extract-region (point) (point-at-eol))))
(forward-line (- arg))
(goto-char (point-at-eol))
(insert ot)
))
)
(previous-line (- arg))
)
(global-set-key (kbd "C-M-z") (lambda ()
(interactive)
(flip-text 1)))
(global-set-key (kbd "C-M-c") (lambda ()
(interactive)
(flip-text -1)))
This will do what you specified but does not do multiple lines.
(defun flip-text-to-eol (&optional up)
"Flip the text from point to the end of the current line with the text
in the next line from the same column to the end of the next line.
With a prefix arg, flip text with the line above the current."
(interactive "P")
(save-excursion
(let ((tt (delete-and-extract-region (point) (point-at-eol)))
(c (current-column)))
(forward-line (if up -1 1))
(move-to-column c)
(insert tt)
(let ((ot (delete-and-extract-region (point) (point-at-eol))))
(forward-line (if up 1 -1))
(goto-char (point-at-eol))
(insert ot)))))

Toggle case of next letter in elisp

I'd like to be able to toggle the case of the letter under the point. To that end, I wrote this:
(defun toggle-case-next-letter ()
"Toggles the case of the next letter, then moves the point forward one character"
(interactive)
(let* ((p (point))
(upcased (upcasep (char-after)))
(f (if upcased 'downcase-region 'upcase-region)))
(progn
(f p (+ 1 p))
(forward-char))))
However, when I run it (I've bound it to M-#), I get progn: Symbol's function definition is void: f. I assume this means f isn't bound, but I'm not sure.
Upcasep is defined as:
(defun upcasep (c) (eq c (upcase c)))
Is the problem in the let binding, or something else? (Also, if there's a better way to do this, that'd be nice as well).
Note that originally I had (upcased (upcasep (buffer-substring-no-properties p (+ 1 p)))), which I've corrected to (upcased (upcasep (char-after)), because using upcasep as defined above is always nil for strings (so I couldn't downcase again).
You've got a typical case of lisp-1 / lisp-2 confusion. Here's a fix (just a funcall):
(defun toggle-case-next-letter ()
"Toggles the case of the next letter, then moves the point forward one character"
(interactive)
(let* ((p (point))
(upcased (char-upcasep (buffer-substring-no-properties p (+ 1 p))))
(f (if upcased 'downcase-region 'upcase-region)))
(progn
(funcall f p (+ 1 p))
(forward-char))))
And here's what I have:
(global-set-key (kbd "C->") 'upcase-word-toggle)
(global-set-key (kbd "C-z") 'capitalize-word-toggle)
(defun char-upcasep (letter)
(eq letter (upcase letter)))
(defun capitalize-word-toggle ()
(interactive)
(let ((start (car
(save-excursion
(backward-word)
(bounds-of-thing-at-point 'symbol)))))
(if start
(save-excursion
(goto-char start)
(funcall
(if (char-upcasep (char-after))
'downcase-region
'upcase-region)
start (1+ start)))
(capitalize-word -1))))
(defun upcase-word-toggle ()
(interactive)
(let ((bounds (bounds-of-thing-at-point 'symbol))
beg end
regionp)
(if (eq this-command last-command)
(setq regionp (get this-command 'regionp))
(put this-command 'regionp nil))
(cond
((or (region-active-p) regionp)
(setq beg (region-beginning)
end (region-end))
(put this-command 'regionp t))
(bounds
(setq beg (car bounds)
end (cdr bounds)))
(t
(setq beg (point)
end (1+ beg))))
(save-excursion
(goto-char (1- beg))
(and (re-search-forward "[A-Za-z]" end t)
(funcall (if (char-upcasep (char-before))
'downcase-region
'upcase-region)
beg end)))))
I couldn't get #abo-abo's answer working for me but using his comments I was able to google better and found the following at http://chneukirchen.org/dotfiles/.emacs
(defun chris2-toggle-case ()
(interactive)
(let ((char (following-char)))
(if (eq char (upcase char))
(insert-char (downcase char) 1 t)
(insert-char (upcase char) 1 t)))
(delete-char 1 nil)
(backward-char))
(global-set-key (kbd "M-#") 'chris2-toggle-case)
This answers the original question if you remove (backward-char).
I realize this is a very old question, but having stumbled upon the same problem recently, I'd like to suggest a simpler solution.
I start with a pure function for toggling character case, based on char code property inspection:
(cl-defun toggle-char-case (c)
(cl-case (get-char-code-property c 'general-category)
(Lu (downcase c))
(Ll (upcase c))
(t c)))
I then use it from within an interactive function operating at point:
(cl-defun toggle-char-case-at-point ()
(interactive)
(let ((new (toggle-char-case (char-after))))
(delete-char 1)
(insert new)))
I then bound this interactive function to a keybinding of my choice:
(global-set-key (kbd "C-M-c") 'toggle-char-case-at-point)
The way this function operates is, after toggling the case it advances the point by one. So calling it repeatedly will toggle the cases of a sequence of chars. One could make it keep the point unchanged - that would require adding (backward-char) to the body.

Intelligent auto closing matching characters

In some of the modes I'm using, emacs helps me by auto closing some elements such as quotes, parenthesis.
However some times, out of habit I type the closing element my self and end up with ()) or """.
How can I set up emacs to ignore the extra key?
While it is fun to roll your own, autopair has emerged as the canonical solution to this problem. It does everything you ask, and a few things, you didn't know you wanted. Emacs wiki entry.
Emacs 24 (currently in pretest) will be prepackaged with an electric pairing package. Autopair is still much more sophisticated than the builtin one.
EDIT: I had had the following in my .emacs for a while, and it worked fine so I didn't think too much about it. As event_jr points out in his answer, the same features (and apparently a bit more) can be had with the autopairs.el package, linked from the same page that I got this code from.
I have the following code in my .emacs to do this, taken from the emacs wiki:
(setq skeleton-pair t)
(setq skeleton-pair-alist
'((?\( _ ?\))
(?[ _ ?])
(?{ _ ?})
(?\" _ ?\")))
(defun autopair-insert (arg)
(interactive "P")
(let (pair)
(cond
((assq last-command-char skeleton-pair-alist)
(autopair-open arg))
(t
(autopair-close arg)))))
(defun autopair-open (arg)
(interactive "P")
(let ((pair (assq last-command-char
skeleton-pair-alist)))
(cond
((and (not mark-active)
(eq (car pair) (car (last pair)))
(eq (car pair) (char-after)))
(autopair-close arg))
(t
(skeleton-pair-insert-maybe arg)))))
(defun autopair-close (arg)
(interactive "P")
(cond
(mark-active
(let (pair open)
(dolist (pair skeleton-pair-alist)
(when (eq last-command-char (car (last pair)))
(setq open (car pair))))
(setq last-command-char open)
(skeleton-pair-insert-maybe arg)))
((looking-at
(concat "[ \t\n]*"
(regexp-quote (string last-command-char))))
(replace-match (string last-command-char))
(indent-according-to-mode))
(t
(self-insert-command (prefix-numeric-value arg))
(indent-according-to-mode))))
(defun autopair-backspace (arg)
(interactive "p")
(if (eq (char-after)
(car (last (assq (char-before) skeleton-pair-alist))))
(and (char-after) (delete-char 1)))
(delete-backward-char arg))
(global-set-key [backspace] 'autopair-backspace)
(define-key isearch-mode-map [backspace] 'isearch-delete-char) ;; required to fix behaviour in isearch
(global-set-key "(" 'autopair-insert)
(global-set-key ")" 'autopair-insert)
(global-set-key "[" 'autopair-insert)
(global-set-key "]" 'autopair-insert)
(global-set-key "{" 'autopair-insert)
(global-set-key "}" 'autopair-insert)
(global-set-key "\"" 'autopair-insert)
I'm not sure if it's Emacs 24 feature only, but electric-pair-mode seems to do what you want.

Emacs: how to write a defun which acts on region, but acts on point if there's no region?

I write a simple defun for a region, and I want to apply it even if there's no region – i.e. call it with no selection at all. I thought I could do something like the following:
(defun region-study (strt end)
(interactive "r")
(if (= strt end)
(progn ....) ;; then
(progn ....))) ;; else
But it doesn't work. As it turns out, when you call (interactive "r") with no region it doesn't just set boundaries to be equal. Try this:
(defun region-study (strt end)
(interactive "r")
(message "strt=%d; end=%d" strt end))
So my question is that: "how to write a defun which acts on region, but acts on point if there's no region?"
Edit:
So I wanted to put selection in brackets or just to insert brackets and (backward-char 1). Here's a solution:
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(progn
(setq pP (point))
(setq strt (region-beginning))
(setq end (region-end))
(setq meat (buffer-substring-no-properties strt end))
(setq news (concat lft meat rit))
(delete-region strt end)
(goto-char strt)
(insert news)
(if (= pP strt)
(goto-char strt) ; then
(goto-char (+ end 1)))) ; else
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
(defun bk-put-in-braces ()
(interactive)
(put-in-lft-rit "(" ")"))
(defun bk-put-in-curly-braces ()
(interactive)
(put-in-lft-rit "{" "}"))
(defun bk-put-in-quotes ()
(interactive)
(put-in-lft-rit "'" "'"))
(defun bk-put-in-double-quotes ()
(interactive)
(put-in-lft-rit "\"" "\""))
(defun bk-put-in-square-brackes ()
(interactive)
(put-in-lft-rit "[" "]"))
And then you bind in .emacs:
(global-set-key (kbd "C-<f9>") 'bk-put-in-square-brackes)
(global-set-key (kbd "<f9>") 'bk-put-in-curly-braces)
(global-set-key (kbd "S-<f7>") 'bk-put-in-quotes)
(global-set-key (kbd "S-<f8>") 'bk-put-in-double-quotes)
(global-set-key (kbd "S-<f9>") 'bk-put-in-braces)
That's it! Should be working in all modes.
Edit2:
#phils
Thanks. You are definetely right. One thing though - my code had an additional feature of leaving the point at the beginning or end of the region - depending on where it was in the selection. Here's Your code with this feature added:
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(let ((strt (region-beginning))
(end (region-end))
(pP (point)))
(save-excursion
(goto-char end)
(insert rit)
(goto-char strt)
(insert lft))
(if (= pP strt)
(goto-char strt) ; then
(goto-char (+ end 1)))) ; else
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
A few notes on your solution...
It's good practice to avoid unnecessary global-scope setqs. Use (let) instead to define a temporary scope for your variables.
You are doing a lot more work than required. Instead of copying the region, concatenating that copy and the delimiters into a 'news' variable, deleting the region, and then inserting 'news', all you need to do is insert the delimiter characters at the beginning and end of the region.
(In general, if you try to "think like an editor" when writing elisp, and focus on manipulating buffers rather than variables, you'll generally wind up with more efficient code.)
save-excursion is very useful (along with several other save- and with- forms).
 
(defun put-in-lft-rit (lft rit)
(interactive "k")
(if (use-region-p) ;; act on region
(let ((strt (region-beginning))
(end (region-end)))
(save-excursion
(goto-char end)
(insert rit)
(goto-char strt)
(insert lft)))
(progn ;; act on point
(insert lft rit)
(backward-char 1))))
use-region-p should return t if your function should act on the region instead of at a point.
You may like to use the function region-or-word-at-point defined in thingatpt+.el

how to use a keyboard macro in a lisp function until it fails

right now I have a keyboard macro defined and named, and i want to make a lisp function which goes to the top of the buffer, and does:
i = 1
do{
run macro
if macro hit the end of the buffer, break out of the loop
insert i
i++
}while(true)
here's what's in my .emacs
(fset 'next-id
(lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ([19 73 68 61 34 13 67108896 19 34 13 2 23] 0 "%d")) arg)))
(global-set-key (kbd "C-x n") 'next-id)
how would I go about this?
Srsly. Here's how to do it: C-u 0 F4
This should do the trick:
(defun apply-macro-to-buffer (&optional macro)
"Apply last keyboard macro to the buffer"
(interactive "CEnter the name of the macro to apply: ")
(or macro
(progn
(if (null last-kbd-macro)
(error "No keyboard macro has been defined"))
(setq macro last-kbd-macro)))
(let ((end-marker (copy-marker (point-max)))
(i 1))
(save-excursion
(goto-char (point-min))
(while (and (< (point) end-marker))
(let ((mark-active nil))
(execute-kbd-macro macro))
(insert (format "%d\n" i))
(setq i (1+ i))))))
To do the same for a regular command, try this:
(defun apply-command-to-buffer (command)
"Apply a command to the buffer"
(interactive "CEnter the name of the command to apply: ")
(let ((end-marker (copy-marker (point-max)))
(i 1))
(save-excursion
(goto-char (point-min))
(while (and (< (point) end-marker))
(call-interactively command)
(insert (format "%d\n" i))
(setq i (1+ i))))))