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?

achalk
  • 579
  • 4
  • 16

4 Answers4

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.

ideasman42
  • 8,375
  • 1
  • 28
  • 105
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.

achalk
  • 579
  • 4
  • 16
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.

Att Righ
  • 725
  • 4
  • 14
-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))
db48x
  • 15,741
  • 1
  • 19
  • 23
  • So if I unbind the `p` key first, it should work? – achalk Mar 14 '17 at 00:21
  • If you unbind the 'p' key then it won't call anything at all; that won't help you. – db48x Mar 14 '17 at 00:23
  • I see. I've tried the above function, but it gives me `Wrong type argument: commandp, paste-and-indent` – achalk Mar 14 '17 at 00:28
  • Sorry, that's what I get for typing this into a textbox instead of into Emacs iteself. – db48x Mar 14 '17 at 00:30
  • 1
    Ok, now it says `Wrong number of arguments: (2 . 3), 0`. – achalk Mar 14 '17 at 00:33
  • I've got it! You just bind `'evil-paste-after` to a key other than `p`, then it works. e.g. `(evil-define-key 'normal global-map (kbd "|") 'evil-paste-after) (fset 'paste-and-indent "|\`[v\`]=") (evil-define-key 'normal global-map (kbd "p") 'paste-and-indent)` A hack, but a functional hack. Thank for for explaining the problem with recursion to me, or else I wouldn't have found it. – achalk Mar 14 '17 at 00:52
  • Scratch that; it causes too many problems. It breaks `put-from-clipboard`, and emacs thinks `p` is two events (it requires two `u` to undo). If you hit `10 p`, that requires 20 `u` to undo. So if you have any further advice on how to write this function properly, I'd really appreciate it. – achalk Mar 14 '17 at 01:51