2

Part of Emacs's electric-pair-mode functionality (controlled by a variable called electric-pair-delete-adjacent-pairs) is that when the point is between an empty set of delimiters and you delete the opening one, it also deletes the closing one for you: if the point is at the pipe character in (|), pressing backspace will delete both the opening and closing parenthesis.

I like this functionality very much but annoyingly it doesn't work in the Slime REPL. I do have electric-pair-mode enabled in the REPL, and it does do the other things it is supposed to, like insert the closing parenthesis automatically when you insert the opening one, or wrap the region in parenthesis when the mark is active and you press (. Just deleting adjacent pairs doesn't work. How do I get that functionality in the Slime REPL? (I think the answer has to do with keymap precedence. Maybe...)

EDIT: Here is how I'm testing this: I'm using Emacs 26.1 (on both GNU/Linux and on Windows 10, the same problem appears) and Slime 2.23. To test I run emacs -Q and then evaluate this in the scratch buffer:

(package-initialize)
(setq slime-contribs '(slime-repl slime-indentation))
(setq slime-lisp-implementations '((sbcl ("sbcl"))))
(require 'slime)

Then I run M-x slime and when the REPL appears, I run M-x electric-pair-mode. Then typing a ( inserts () leaving the point between the parenthesis. Typing backspace then deletes only the opening parenthesis insetad of both.

  • Works for me with slime version 2.23, emacs version 26.1 i686-pc-linux-gnu, built-in electric-pair-mode. Please give version information and test starting from emacs -Q. – Tobias Mar 25 at 15:09
  • That is weird, @Tobias! I'll add details to the question about how it still happens to me with those same versions and also from emacs -Q. – Omar Mar 25 at 18:28
  • @Tobias: Did you use the slime-repl contrib? Because I just tried without it and their backspace does delete the pair. I forgot to specify I´m using slime-repl. – Omar Mar 25 at 19:40
  • If you don't mind the mind change you could try smartparens instead of electric-pair and see if it works well under slime-repl – Jesse Mar 25 at 20:19
  • Mmh. That does work, @Jesse. Maybe I should switch to smartparens... – Omar Mar 25 at 20:41
1
+150

slime-repl-mode overrides in slime-repl-mode-map the key-binding for electric-pair-delete-pair. The override is not very surprising. It is just backward-delete-char-untabify. The binding in global-map is delete-backward-char. If you do not really need the untabify part in the repl removing the override shouldn't do any harm.

Just put the following Elisp snippet into your init file.

(eval-after-load "slime" '(define-key slime-repl-mode-map (kbd "DEL") nil))

EDIT: After some background check it turns out that you can even make sure that backward-delete-char-untabify is called by the backspace key if you bind it in the local map of slime-repl-mode.

Just add also the following Elisp snippet into your init file:

(add-hook 'slime-repl-mode-hook
      (lambda ()
        (local-set-key (kbd "DEL") #'backward-delete-char-untabify)))

Background:

The reason for the strange behavior are peculiarities of slime-repl-mode and electric-pair-mode.

  1. slime-repl-mode is not defined via define-derived-mode. It does not use its local map as keymap but uses the minor mode map of the minor mode slime-repl-map-mode.

  2. electric-pair-mode is defined as an autoloaded minor mode. Its keymap is put into minor-mode-map-alist as soon as the autoloads are executed. That means the keymap of electric-pair-mode is always already in minor-mode-map-alist when slime-repl-map-mode is defined through (require 'slime). Note, (define-minor-mode slime-repl-map-mode ...) is not autoloaded.

We have here two concurrent minor mode maps. slime-repl-map-mode is defined last and wins.

Note that minor mode maps always override local keymaps. Furthermore electric-pair-mode uses a filter for its DEL entry in electric-pair-mode-map. The filter lets through the original binding (e.g., the local binding) if point is not between two delimiters handled by electric-pair-mode. That means: At points where electric-pair-mode does not kick in you can define the behavior of the backspace key in the local map of slime-repl-mode.

  • Thanks! This works perfectly. I'll award the bounty as soon as I am allowed to. I had looked at slime-repl.el and didn't find the bindings there. I guess I forgot to look at it's parent map, lisp-mode-map. That one also doesn't have the binding directly, but its parent, lisp-mode-shared-map does. I don't understand why the problem occurs in the slime-repl REPL, but not in the inferior-lisp REPL and not in lisp-mode buffers... – Omar Mar 25 at 23:13
  • 1
    @Omar I added some background about the strange behavior. Furthermore I corrected a small error in my answer. Backspace is not bound to backward-delete-char-untabify in global-map but to backward-delete-char. If you need backward-delete-char-untabify you can bind it in slime-repl-mode-hook (see the edit). – Tobias Mar 26 at 4:30
  • Thank you, @Tobias, the edit answers my remaining questions about how this happened. – Omar Mar 26 at 5:26
  • 1
    @Drew Hi Drew, hope I get thru to you with this comment. Do you have any idea why they did not use define-derived-mode for slime-repl-mode? My only guess would be historical reasons (slime being older than define-derived-mode?). In my opinion it is a quite strange method to define the (only) keymap of a major mode through a minor mode and not as buffer local keymap. At least it gives the user a headache as you see on this page. BTW when did they introduce define-derived-mode? – Tobias Mar 26 at 11:05
  • @Drew Looks like define-derived-mode (1993) is older than slime-repl-mode (2003). But, who knows from what time on it was really as usable and as highly promoted as nowadays. Does it make sense to post a separate question about this topic? Note, that this is not the first time that I came across a problem caused by major modes not defined per define-derived-mode. – Tobias Mar 26 at 11:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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