|
;;; slime-macrostep.el -- fancy macro-expansion via macrostep.el
|
|
|
|
;; Authors: Luís Oliveira <luismbo@gmail.com>
|
|
;; Jon Oddie <j.j.oddie@gmail.com
|
|
;;
|
|
;; License: GNU GPL (same license as Emacs)
|
|
|
|
;;; Description:
|
|
|
|
;; Fancier in-place macro-expansion using macrostep.el (originally
|
|
;; written for Emacs Lisp). To use, position point before the
|
|
;; open-paren of the macro call in a SLIME source or REPL buffer, and
|
|
;; type `C-c M-e' or `M-x macrostep-expand'. The pretty-printed
|
|
;; result of `macroexpand-1' will be inserted inline in the current
|
|
;; buffer, which is temporarily read-only while macro expansions are
|
|
;; visible. If the expansion is itself a macro call, expansion can be
|
|
;; continued by typing `e'. Expansions are collapsed to their
|
|
;; original macro forms by typing `c' or `q'. Other macro- and
|
|
;; compiler-macro calls in the expansion will be font-locked
|
|
;; differently, and point can be moved there quickly by typing `n' or
|
|
;; `p'. For more details, see the documentation of
|
|
;; `macrostep-expand'.
|
|
|
|
;;; Code:
|
|
|
|
(require 'slime)
|
|
(eval-and-compile
|
|
(require 'macrostep nil t)
|
|
;; Use bundled version if not separately installed
|
|
(require 'macrostep "../lib/macrostep"))
|
|
(eval-when-compile (require 'cl-lib))
|
|
|
|
(defvar slime-repl-mode-hook)
|
|
(defvar slime-repl-mode-map)
|
|
|
|
(define-slime-contrib slime-macrostep
|
|
"Interactive macro expansion via macrostep.el."
|
|
(:authors "Luís Oliveira <luismbo@gmail.com>"
|
|
"Jon Oddie <j.j.oddie@gmail.com>")
|
|
(:license "GPL")
|
|
(:swank-dependencies swank-macrostep)
|
|
(:on-load
|
|
(easy-menu-add-item slime-mode-map '(menu-bar SLIME Debugging)
|
|
["Macro stepper..." macrostep-expand (slime-connected-p)]
|
|
"Create Trace Buffer")
|
|
(add-hook 'slime-mode-hook #'macrostep-slime-mode-hook)
|
|
(define-key slime-mode-map (kbd "C-c M-e") #'macrostep-expand)
|
|
(eval-after-load 'slime-repl
|
|
'(progn
|
|
(add-hook 'slime-repl-mode-hook #'macrostep-slime-mode-hook)
|
|
(define-key slime-repl-mode-map (kbd "C-c M-e") #'macrostep-expand)))))
|
|
|
|
(defun macrostep-slime-mode-hook ()
|
|
(setq macrostep-sexp-at-point-function #'macrostep-slime-sexp-at-point)
|
|
(setq macrostep-environment-at-point-function #'macrostep-slime-context)
|
|
(setq macrostep-expand-1-function #'macrostep-slime-expand-1)
|
|
(setq macrostep-print-function #'macrostep-slime-insert)
|
|
(setq macrostep-macro-form-p-function #'macrostep-slime-macro-form-p))
|
|
|
|
(defun macrostep-slime-sexp-at-point (&rest _ignore)
|
|
(slime-sexp-at-point))
|
|
|
|
(defun macrostep-slime-context ()
|
|
(let (defun-start defun-end)
|
|
(save-excursion
|
|
(while
|
|
(condition-case nil
|
|
(progn (backward-up-list) t)
|
|
(scan-error nil)))
|
|
(setq defun-start (point))
|
|
(setq defun-end (scan-sexps (point) 1)))
|
|
(list (buffer-substring-no-properties
|
|
defun-start (point))
|
|
(buffer-substring-no-properties
|
|
(scan-sexps (point) 1) defun-end))))
|
|
|
|
(defun macrostep-slime-expand-1 (string context)
|
|
(slime-dcase
|
|
(slime-eval
|
|
`(swank-macrostep:macrostep-expand-1
|
|
,string ,macrostep-expand-compiler-macros ',context))
|
|
((:error error-message)
|
|
(error "%s" error-message))
|
|
((:ok expansion positions)
|
|
(list expansion positions))))
|
|
|
|
(defun macrostep-slime-insert (result _ignore)
|
|
"Insert RESULT at point, indenting to match the current column."
|
|
(cl-destructuring-bind (expansion positions) result
|
|
(let ((start (point))
|
|
(column-offset (current-column)))
|
|
(insert expansion)
|
|
(macrostep-slime--propertize-macros start positions)
|
|
(indent-rigidly start (point) column-offset))))
|
|
|
|
(defun macrostep-slime--propertize-macros (start-offset positions)
|
|
"Put text properties on macro forms."
|
|
(dolist (position positions)
|
|
(cl-destructuring-bind (operator type start)
|
|
position
|
|
(let ((open-paren-position
|
|
(+ start-offset start)))
|
|
(put-text-property open-paren-position
|
|
(1+ open-paren-position)
|
|
'macrostep-macro-start
|
|
t)
|
|
;; this assumes that the operator starts right next to the
|
|
;; opening parenthesis. We could probably be more robust.
|
|
(let ((op-start (1+ open-paren-position)))
|
|
(put-text-property op-start
|
|
(+ op-start (length operator))
|
|
'font-lock-face
|
|
(if (eq type :macro)
|
|
'macrostep-macro-face
|
|
'macrostep-compiler-macro-face)))))))
|
|
|
|
(defun macrostep-slime-macro-form-p (string context)
|
|
(slime-dcase
|
|
(slime-eval
|
|
`(swank-macrostep:macro-form-p
|
|
,string ,macrostep-expand-compiler-macros ',context))
|
|
((:error error-message)
|
|
(error "%s" error-message))
|
|
((:ok result)
|
|
result)))
|
|
|
|
|
|
|
|
(provide 'slime-macrostep)
|