I have two suggestions, one, not using any non-default packages, very closely based on the definition of text-scale-adjust
and one using hydra
.
Self-contained
;; -*- lexical-binding: t -*-
;; the previous line needs to be the first in the given file.
(require 'python) ;; alternatively use an appropriate `use-package` declaration
(defun python-indent-adjust (count)
"Shift relevant lines by COUNT columns to the right or left.
If the region is active, then the `relevant' lines are those in
the region. Otherwise, it's just the current line.
COUNT may be passed as a numeric prefix argument. It defaults to
‘python-indent-offset’.
The actual adjustment made depends on the final component of the
key-binding used to invoke the command, with all modifiers removed:
< Shift the indendation to the left
> Shift the indendation to the right
After adjusting, continue to read input events and further adjust
the indentation as long as the input event read \(with all
modifiers removed) is one of the above characters.
This command is a special-purpose wrapper around the
`python-indent-shift-right' and `python-indent-shift-left' commands."
(interactive "P")
(let ((start (if mark-active (region-beginning) (line-beginning-position)))
(end (if mark-active (region-end) (line-end-position)))
(count (if count count python-indent-offset)))
(let ((ev last-command-event)
(echo-keystrokes nil))
(let* ((base (event-basic-type ev))
(func
(pcase base
((or ?> ?.) 'python-indent-shift-right)
((or ?< ?,) 'python-indent-shift-left))))
(funcall func start end count)
(message "Use <,> for further adjustment")
(set-transient-map
(let ((map (make-sparse-keymap)))
(dolist (mods '(() (control)))
(dolist (key '(?> ?< ?, ?.)) ;; ,. are often unshifted <,>.
(define-key map (vector (append mods (list key)))
(lambda () (interactive)
(python-indent-adjust count)))))
map))))))
(define-key python-mode-map (kbd "C-c >") 'python-indent-adjust)
(define-key python-mode-map (kbd "C-c <") 'python-indent-adjust)
Hydra
You need to install hydra
first.
(require 'python) ;; alternatively use an appropriate `use-package` declaration
(defhydra python-indent (python-mode-map "C-c")
"Adjust python indentation."
(">" python-indent-shift-right "right")
("<" python-indent-shift-left "left"))
The hydra solution has the tiny disadvantage that if you explicitly specify the number of columns by which to indent (e.g. with a prefix argument (e.g. C-u N
where N
is an integer)), then that will only apply to the first indentation. Hence, if you wish to continue adjusting by, say 5 columns, you need to do something like C-u 5 C-c > 5 > 5 >
etc. Normally, though, you'll want to adjust the indentation by the default amount, so you won't need to worry about this.
Global bindings
If you want to use the bindings both globally and within python mode, as suggested in the comments, in addition to the previous defhydra
you also need:
(defhydra python-indent (global-map "C-c")
"Adjust python indentation."
(">" python-indent-shift-right "right")
("<" python-indent-shift-left "left"))
(If you wanted to use the stand-alone solution, you'd similarly need two extra define-key
s with global-map
(or global-set-key
s).)
Regarding globabl use: I don't think it's a bad idea, as such but it's possible that the indent functions used for python-mode
might not necessarily play well with all other possible language modes, though I can't currently think of any specific incompatibilities.