3

I am using following answer to handle indentations Emacs bulk indent for Python

C-c > or C-c C-l shifts the region 4 spaces to the right

C-c < or C-c C-r shifts the region 4 spaces to the left


If I do C-c > and >; the selected text becomes >; or if I do C-c < and <; the selected text becomes <.

I was wondering can we do following:

=> C-c > , > , > to continue to indent 4 space to right one after another

=> C-c < , < , < to continue to indent 4 space to left one after another

Drew
  • 75,699
  • 9
  • 109
  • 225
alper
  • 1,238
  • 11
  • 30

1 Answers1

1

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-keys with global-map (or global-set-keys).)

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.

aplaice
  • 2,126
  • 17
  • 23
  • 1
    Can `"C-c >"` apply for all the language modes? // First solutions says: `Symbol’s value as variable is void: count` but second solution works – alper Nov 06 '20 at 15:53
  • 1
    For make it global should I replace `python-mode-map` with `global-map` ? – alper Nov 06 '20 at 16:10
  • I'm sorry about the first solution. It was caused by an issue with how [Iexical binding](https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html) works — it worked in the `*scratch*` buffer, since lexical binding is turned on there, by default. I think that I've fixed the problem, though. – aplaice Nov 06 '20 at 19:03
  • To make it global, you indeed need to use the `global-map`. However, in order for it to be set both for the global and python maps, I think that you'll need two have two `defhydra`s (or two sets of `define-key`s) — otherwise the default `python-mode-map` definition will override your custom `global-map` one. – aplaice Nov 06 '20 at 19:11
  • Is it possible to apply this solution for `shell-mode` or orher modes as well? – alper Jun 29 '21 at 14:51
  • Yes, there's no reason for either of these solutions not to work with `shell-mode` or most other modes. (It won't work too well with term mode and the like, since there most keybindings are passed directly to `bash` etc.) – aplaice Jun 30 '21 at 20:28
  • I am not sure why but `(defhydra python-indent (global-map "C-c")` does not overwrite to global bindings and in shell-mode its bind to `sh-learn-line-indent`. Is there any way to force to overwrite the keybinding – alper Jul 03 '21 at 14:11
  • I think that dealing with global-map hydras overriding mode-specific bindings should be a separate question. I'm not sure off-the-top-of-my-head and somebody else might have a better idea. (The issue is that in `shell-mode` the "local" (mode-specific) binding takes precedence over the global binding.) – aplaice Jul 03 '21 at 20:25
  • 1
    Please see: https://emacs.stackexchange.com/questions/66587/how-to-define-global-keybinding-for-c-c-and-c-c-using-defhydra – alper Jul 04 '21 at 12:23