17

I'm looking for a way in emacs to shift text to the right or to the left by n spaces. A similar functionality that it in vim << or >>. It should work on a region or if no region is selected on a current line and not move the cursor from its current location.

The solution from EmacsWiki does not work very well as the M-x indent-rigidly since it somewhat remembers the last region used and shifts that one instead. The closest seems to be the one here but I did not managed to make it work. I'm not a lisp developer so it's difficult to modify the code. I will appreciate any help.

Thanks!

Community
  • 1
  • 1
fikovnik
  • 3,473
  • 3
  • 28
  • 29

4 Answers4

38

You could select the region then C-u C-x <tab> will shift 4 spaces. You can type a number after C-u to change 4 to anything else.

ychaouche
  • 4,922
  • 2
  • 44
  • 52
9

Maybe this works the way you want.

(defun shift-text (distance)
  (if (use-region-p)
      (let ((mark (mark)))
        (save-excursion
          (indent-rigidly (region-beginning)
                          (region-end)
                          distance)
          (push-mark mark t t)
          (setq deactivate-mark nil)))
    (indent-rigidly (line-beginning-position)
                    (line-end-position)
                    distance)))

(defun shift-right (count)
  (interactive "p")
  (shift-text count))

(defun shift-left (count)
  (interactive "p")
  (shift-text (- count)))
Nietzche-jou
  • 14,415
  • 4
  • 34
  • 45
6

To achieve this I usually do a trick:

  • activate CUA mode
  • go to the beginning of line
  • C-RET, now if you move the cursor you should see a rectangular red region
  • Move the cursor down the lines and type space until you've obtained the correct shifting.

This can be done also programmatically in some way (in the same way).

EDIT: I've just read the article in emacs wiki, it's the same solution except for the CUA mode that is infinitely more powerful than the "common" rectanguar selection (since it's visual).

pygabriel
  • 9,840
  • 4
  • 41
  • 54
  • 2
    You can enable CUA's excellent rectangle editing facilities separately from its other features with `(cua-selection-mode t)`. Provided you don't have `C-RET` bound to something else, you can put this in your init file and it won't conflict with anything. – phils Jul 01 '10 at 22:14
  • 1
    Quick notes on basic usage for shifting lines: `C-RET` + cursor movements to specify rectangle, `RET` to cycle corners, typing inserts before/after rectangle, `C-RET` to exit. See the notes under "CUA rectangle support" in cua-base.el for the full details on what this minor mode provides, or read http://trey-jackson.blogspot.com/2008/10/emacs-tip-26-cua-mode-specifically.html – phils Jul 01 '10 at 22:35
  • 1
    I have a confession. I use CUA mode. – Prof. Falken Feb 16 '17 at 08:40
  • LOVE THIS TIP (emacs user of 20+ years) – jjohn Sep 24 '18 at 14:30
0

As I use Evil (with Spacemacs), the Vim-like region shifting is already implemented in visual mode with S-v and </> properly.

I'm mostly using hybrid-mode though, and when it's active I also want to be able to shift the region, preferrably by the current language's shift-width.

To achieve this, here's an implementation that re-uses evil's shifting, but does it "properly" in hybrid-mode.

(defun jj/shift-text (beg end shift-block-fun shift-line-fun)
  "shift text in region or line using evil like S-v with < and > do in Vim.
  It takes special care of preserving or even extending the region to the moved text lines."
  (if (use-region-p)
      (progn
        (let ((point-at-end (< (mark) (point))))

          ;; fix up current region end to grab the whole line
          (if point-at-end
              (end-of-line)
            (beginning-of-line))

          ;; then fix up the other region end
          (exchange-point-and-mark)
          (if point-at-end
              (beginning-of-line)
            (end-of-line))

          ;; restore mark-point order
          (exchange-point-and-mark)

          (let ((linebeg (if point-at-end (mark) (point)))
                (lineend (if point-at-end (point) (mark))))
            ;; shift the text
            (save-mark-and-excursion
              (funcall shift-block-fun linebeg lineend)
              ;; "In Transient Mark mode, every buffer-modifying primitive sets deactivate-mark"
              ;; but we wanna keep it active :)
              (setq deactivate-mark nil)))))

    (funcall shift-line-fun 1)))

(defun jj/shift-left (beg end)
  (interactive "r")
  (jj/shift-text beg end #'evil-shift-left #'evil-shift-left-line))

(defun jj/shift-right (beg end)
  (interactive "r")
  (jj/shift-text beg end #'evil-shift-right #'evil-shift-right-line))

and where your keybindings are defined:

  ;; text shifting. evil-normal-state-map has these anyway.
  (define-key evil-hybrid-state-map (kbd "M-<") #'jj/shift-left)
  (define-key evil-hybrid-state-map (kbd "M->") #'jj/shift-right)
TheJJ
  • 931
  • 12
  • 21