(keymap-global-set "C-M-S-q" (lambda ()
(interactive)
(indent-pp-sexp t)))
+(defvar *re/lisp-mode-hooks*
+ '(emacs-lisp-mode-hook
+ lisp-mode-hook
+ slime-mode-hook
+ scheme-mode-hook))
+
+(dolist (hook *re/lisp-mode-hooks*)
+ (add-hook hook
+ (lambda ()
+ (keymap-set lisp-mode-shared-map "]" (re/balance-and-eval-sexp #'newline)))))
+
+(add-hook 'lisp-interaction-mode-hook
+ (lambda ()
+ (keymap-set lisp-interaction-mode-map "]" (re/balance-and-eval-sexp #'eval-print-last-sexp))))
;; IELM
(keymap-global-set "C-c i" #'ielm)
(lambda ()
(keymap-set inferior-emacs-lisp-mode-map "C-l" #'comint-clear-buffer)
(keymap-set inferior-emacs-lisp-mode-map ")" (re/send-on-close-paren #'ielm-send-input))
- (keymap-set inferior-emacs-lisp-mode-map "]" #'re/insert-all-parens)))
+ (keymap-set inferior-emacs-lisp-mode-map "]" (re/balance-and-eval-sexp #'ielm-send-input))))
;; Eshell
(keymap-global-set "C-c e" #'eshell)
(lambda ()
(keymap-set eshell-mode-map "C-d" #'eshell-quit-or-delete-char)
(keymap-set eshell-mode-map ")" (re/send-on-close-paren #'eshell-send-input))
- (keymap-set eshell-mode-map "]" #'re/insert-all-parens)))
+ (keymap-set eshell-mode-map "]" (re/balance-and-eval-sexp #'eshell-send-input))))
;; SLIME REPL
(add-hook 'slime-repl-mode-hook
(lambda ()
(keymap-set slime-repl-mode-map ")" (re/send-on-close-paren #'slime-repl-return))
- (keymap-set slime-repl-mode-map "]" #'re/insert-all-parens)))
+ (keymap-set slime-repl-mode-map "]" (re/balance-and-eval-sexp #'slime-repl-return))))
;; Org Starters
(keymap-global-set "C-c l" #'org-store-link)
;; Any Lisp Repl
(defmacro re/send-on-close-paren (executor)
"Generalizes sending an execute on close paren.
-When a sexp is complete - that is when parens balance as you type - execute it.
-Use this macro as a direct binding"
+ Interactively call `executor'. For `executor', use whatever
+ function is called by `<RET>' in the applicable REPL. For a
+ non-repl, use `newline' or similar."
+ `(lambda (&optional arg)
+ (interactive "p")
+ (insert-char 41 arg)
+ (call-interactively ,executor)))
+
+(defun re/sexp-drop-paren-p ()
+ "Returns t if sexp needs fewer parens to balance."
+ (<0 (car (syntax-ppss))))
+
+(defun re/sexp-need-paren-p ()
+ "Returns t if sexp needs more parens to balance."
+ (>0 (car (syntax-ppss))))
+
+(defun re/sexp-unbalanced-count ()
+ "Returns distance from balanced sexp."
+ (abs (car (syntax-ppss))))
+
+(defun re/balance-sexp ()
+ "Balance the sexp before point.
+Either delete or add as many ')' as needed."
+ (let ((distance (re/sexp-unbalanced-count)))
+ (cond
+ ((re/sexp-need-paren-p) (insert-char 41 distance))
+ ((re/sexp-drop-paren-p) (backward-delete-char distance)))))
+
+(defmacro re/balance-and-eval-sexp (executor)
+ "Balance the sexp before point, then call `executor'.
+Call `re/balance-sexp', then interactively call `executor'. Bind
+this to ']' for the interlisp experience. For `executor', use
+whatever function is called by `<RET>' in the applicable REPL.
+For a non-repl, use `newline' or similar."
`(lambda ()
(interactive)
- (insert-char 41)
- (when (= 0 (car (syntax-ppss)))
- (call-interactively ,executor))))
-
-(defun re/insert-all-parens ()
- "Inserts all the parens needed to balance a sexp.
-If multiple parens are needed at the end to balance a sexp, insert them.
-Bind this to ']' for the interlisp experience."
- (interactive)
- (while (>0 (car (syntax-ppss)))
- (insert-char 41)))
+ (re/balance-sexp)
+ (call-interactively ,executor)))
;; Eshell
(defun eshell-quit-or-delete-char (arg)