Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emacs: Assign a function to a keybinding, and with repeated presses, undo the last press and redo at a wider setting

Tags:

emacs

elisp

Here's the bigger picture of what I'm trying to do:

With a keypress, it will insert an opening and closing parenthesis right after/before the innermost closing/opening delimiter (bracket, brace, parenthesis, whatever).

But if it is pressed immediately after, it will undo the last insertions, seek out the next closing delimiter, and put it after that, and so on.

I have a working version of the first part, but am looking for "best practices" for the second part (where it undoes and moves outwards)

As a motivator and concrete example, this is a typical scenario when coding in Python. I work with list comprehensions a lot, and often I construct the list, and then decide I want to sum all elements, etc. So I would first type:

[x*x for x in some_lst if is_prime(x)]

and then I'll want to encapsulate this list with a "sum" command:

sum([x*x for x in some_lst if is_prime(x)])

I don't want to have to keep moving the cursor to both the beginning and the end just to insert the parentheses. I'd rather have the point in the list, press a keystroke, have it figure out the delimiters, and place the mark just before the opening inserted parenthesis so that I can type "sum". My function below seems to work (piggybacking on the "expand-region" package):

(defun add-paren ()
  (interactive)
  (er/mark-outside-pairs)
  (exchange-point-and-mark)
  (insert-string ")")
  (exchange-point-and-mark)
  (insert-string "(")
  (left-char 1)
  )

What's the best practice for the 2nd step?

(Any suggestions/improvements to the above would also be appreciated. This is my first "real" function in Elisp.)

Thanks.

Update: Thanks everyone for the tips. I'll probably use some of them in my final solution. My original question still stands: Is there a standard pattern of "undoing and redoing at a larger scale", or will each problem have its own custom solution? Suppose I use smartparens as suggested to do it all in one keystroke, but I want it to occur on the 3rd level out. What I want is to press the keystroke 3 times and have it place the parentheses there.

So after the first keystroke, it places the parentheses at the innermost level. Pressing it again should remove the inserted parentheses, and place them in the next level up, and so on...

(BTW, not trying to reinvent the wheel. I suspect some of the packages listed may have exactly what I need - I just want practice coding in Elisp).

Update 2: I guess there is no best practice for this? Anyway, I solved the problem using both expand-region and smartparens:

(defun add-paren ()
  (interactive)
  (if (eq last-command 'add-paren)
      ;; (message "AAAA")
      (delete-paren)
    )
  (setq currpoint (point))
  (er/mark-outside-pairs)
  (if (eq currpoint (point))
      (er/mark-outside-pairs)
    )
  (sp-wrap-with-pair "(")
  (left-char 1)
)

(global-set-key (kbd "<f5>") 'add-paren)

(defun delete-paren ()
  (interactive)
  (setq currloc (point))
  (sp-unwrap-sexp)
  (goto-char currloc)
  (left-char 1)
)
like image 507
user1462309 Avatar asked Feb 03 '14 19:02

user1462309


1 Answers

You're already using expand-region. Why not combine that with one of the many "surround region with..." modes?

I personally like smartparens (available via Marmalade or MELPA), but there are many other similar tools.

  1. Use er/expand-region until you've got an appropriate selection, then
  2. ( to wrap in parentheses.
like image 124
Chris Avatar answered Sep 29 '22 06:09

Chris