When I'm not using an automatic code formatter (ie. clang-format, gofmt, etc.), I often find myself using Emacs region marking commands like mark-defun, er/expand-region, and mark-whole-buffer prior to pressing <tab>, which is bound to indent-for-tab-command.
This is all working as expected: the selection gets indented and the point is left in the current location.
Say we have the following snippet we'd like to indent.
Mark region with C-M-h (mark-defun)
Indent with <tab> (indent-for-tab-command)
We're done. The selected function is now indented as expected.
But… I always wished the point returned to the location prior to initiating the region-marking command, in this case mark-defun.
In short, I wish the point had ended in the following location.
I'm not aware of an existing package that helps with this, so here's a tiny minor mode (divert-mode) to help with restoring point location after indenting a region. The diverted-events variable can be used to track specific region selecting commands and associate breadcrumb functions to replace the point location as needed.
;;; diverted.el --- Identify temporary diversions and automatically
;;; move point back to original location.
;;; Commentary:
;; Automatically come back to a original location prior to diversion.
;;; Code:
(require 'cl)
(require 'seq)
(defstruct diverted-event
from ;; Initial function (eg. 'mark-defun)
to ;; Follow-up function (eg. 'indent-for-tab-command)
breadcrumb)
(defvar diverted-events
(list
(make-diverted-event :from 'mark-defun
:to 'indent-for-tab-command
:breadcrumb (lambda ()
(diverted--pop-to-mark-command 2)))
(make-diverted-event :from 'er/expand-region
:to 'indent-for-tab-command
:breadcrumb (lambda ()
(diverted--pop-to-mark-command 2)))
(make-diverted-event :from 'mark-whole-buffer
:to 'indent-for-tab-command
:breadcrumb (lambda ()
(diverted--pop-to-mark-command 2))))
"Diversion events to look for.")
(defun diverted--resolve (symbol)
"Resolve SYMBOL to event."
(seq-find (lambda (event)
(equal symbol
(diverted-event-from event)))
diverted-events))
(defun diverted--pop-to-mark-command (n)
"Invoke `pop-to-mark-command' N number of times."
(dotimes (_ n)
(pop-to-mark-command)))
(defun diverted--advice-fun (orig-fun &rest r)
"Get back to location prior to diversion using advice around `diverted-events' (ORIG-FUN and R)."
(let ((recognized-event (diverted--resolve last-command)))
(when recognized-event
(funcall (diverted-event-breadcrumb recognized-event))
(message "Breadcrumbed prior to `%s'"
(diverted-event-from recognized-event)))))
(defun diverted-mode-enable ()
"Enable diverted-mode."
(interactive)
(diverted-mode-disable)
(mapc (lambda (event)
(advice-add (diverted-event-to event)
:after
'diverted--advice-fun)
(message "Looking for `%s' after `%s' diversions."
(diverted-event-to event)
(diverted-event-from event)))
diverted-events)
(message "diverted-mode enabled"))
(defun diverted-mode-disable ()
"Disable diverted-mode."
(interactive)
(mapc (lambda (event)
(advice-remove (diverted-event-to event)
'diverted--advice-fun)
(message "Ignoring `%s' after `%s' diversions."
(diverted-event-to event)
(diverted-event-from event)))
diverted-events)
(message "diverted-mode disabled"))
(define-minor-mode diverted-mode
"Detect temporary diversions and restore point location."
:init-value nil
:lighter " diverted"
:global t
(if diverted-mode
(diverted-mode-enable)
(diverted-mode-disable)))
(provide 'diverted)
;;; diverted.el ends here
UPDATE(2019-04-20): Source on github.