5

When I copy line three below in evil-mode

function foo() {
}
var bar; #copy

and paste it at line two, I get

function foo() {
var bar; #paste
}

instead of the correctly indented

function foo() {
  var bar; #paste
}

I'd like to automatically indent my newly-pasted lines of code, and in vim, the fix for this is to remap the p key to "nnoremap gp p`[v`]=".

I figured that I could use the following in my init.el file to replicate the behaviour:

(fset 'paste-and-indent
  "p`[v`]=")
(define-key evil-normal-state-map (kbd "p") 'paste-and-indent)

But when I try to use the code, upon pressing p I get "After 0 kbd macro iterations: Lisp nesting exceeds 'max-lisp-eval-depth'." I'm confused by this, as max-lisp-eval-depth is set to 800!

Can anyone offer advice on how to implement this behaviour in evil-mode emacs?

4 Answers 4

2

This is possible using evil-indent and evil-get-marker.

This uses emacs-29's with-undo-amagamate macro so the operation doesn't create multiple undo entries.

(defun paste-and-indent-after ()
  (interactive)
  (with-undo-amagamate
    (evil-paste-after 1)
    (evil-indent (evil-get-marker ?\[) (evil-get-marker ?\]))))
(defun paste-and-indent-before ()
  (interactive)
  (with-undo-amagamate
    (evil-paste-before 1)
    (evil-indent (evil-get-marker ?\[) (evil-get-marker ?\]))))

;; Bindings for evil leader
(evil-leader/set-key "p" 'paste-and-indent-after)
(evil-leader/set-key "P" 'paste-and-indent-before)

Note that I couldn't get other answers here working.

0

This isn't a perfect solution, but it's the best I've found so far:

(fset 'indent-pasted-text
  "`[v`]=")
... 
(evil-leader/set-key
  "p" 'indent-pasted-text

This setup means that after pasting, I can hit <leader>p to correctly indent the text. I'm sure there's a better solution, and if you have one feel free to share it—I'll accept your answer instead.

0

You could define a text object based on the fact that (point) and (mark) are set after a past.

(define-key evil-inner-text-objects-map (kbd "P") 'my-evil-pasted-region)

(evil-define-text-object my-evil-pasted-region (count &optional beg end type)
  "Select region that was pasted"
  (evil-range (point) (mark)))

Then >iP will indent the last pasted region.

-1

Your solution failed because it has unbounded recursion; the first thing it does is call whatever is bound to the "p" key, which is itself. Try something like this instead:

(defun paste-and-indent ()
  (interactive)
  (evil-paste-after 0)
  (indent-region))
7
  • So if I unbind the p key first, it should work?
    – achalk
    Commented Mar 14, 2017 at 0:21
  • If you unbind the 'p' key then it won't call anything at all; that won't help you.
    – db48x
    Commented Mar 14, 2017 at 0:23
  • I see. I've tried the above function, but it gives me Wrong type argument: commandp, paste-and-indent
    – achalk
    Commented Mar 14, 2017 at 0:28
  • Sorry, that's what I get for typing this into a textbox instead of into Emacs iteself.
    – db48x
    Commented Mar 14, 2017 at 0:30
  • 1
    Ok, now it says Wrong number of arguments: (2 . 3), 0.
    – achalk
    Commented Mar 14, 2017 at 0:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.