As I use Evil (with Doom Emacs), the Vim-like region shifting is already implemented in evil's visual mode with S-v and </> properly.
I'm mostly using insert-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 insert-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)