;;; bjolang-lsp.el --- Commands for the parts of BjolangLsp that LSP has no request for -*- lexical-binding: t; -*- ;;; Commentary: ;; Macro expansion: `bjo-expand-macro' shows the expansion of the macro call ;; at point all the way down, and `bjo-expand-macro-1' one step of it, in a ;; buffer of their own. With a prefix argument they keep the names hygiene ;; made, such as `tmp__37', which is what tells a template's name from the ;; caller's of the same spelling. ;; ;; The expansion is the compiler's, from its last check of the buffer; a ;; change not yet checked is checked first. `loop', `seql', `->' and the ;; other forms the parser turns into code itself are not macros, and have ;; no expansion to show. ;;; Code: (require 'lsp-mode) (defvar bjo-mode-map) (defvar bjo-mode-hook) (declare-function bjo-mode "bjomode") (defun bjo--field (result key) "The field KEY, a string, of RESULT as `lsp-mode' decoded it. A hash table or a plist, as `lsp-use-plists' says, which can differ from how this file was compiled." (if (hash-table-p result) (gethash key result) (plist-get result (intern (concat ":" key))))) (defun bjo--expand-macro (full hygienic) "Show the expansion of the macro call at point. FULL expands the macro calls in the expansion too; HYGIENIC keeps the names hygiene made." (let* ((params (append (lsp--text-document-position-params) (list :full (if full t :json-false) :hygienic (if hygienic t :json-false)))) (result (lsp-request "bjolang/expandMacro" params))) (if (null result) (message "No macro call at point.") (let ((macro (bjo--field result "macro")) (text (bjo--field result "text")) (buffer (get-buffer-create "*bjo expansion*"))) (with-current-buffer buffer (let ((inhibit-read-only t) ;; The expansion is not a file, and must not start a server. (bjo-mode-hook nil)) (erase-buffer) (bjo-mode) (insert (format ";; (%s ...), %s%s\n\n" macro (if full "expanded all the way" "one step") (if hygienic ", with hygiene's names" "")) text "\n") (font-lock-ensure) (goto-char (point-min))) ;; Read only, and `q' to leave, without binding `q' in the mode's ;; own map, which every Bjolang buffer shares. (view-mode 1)) (display-buffer buffer))))) (defun bjo-expand-macro (&optional hygienic) "Show the macro call at point expanded all the way down. With a prefix argument HYGIENIC, keep the names hygiene made." (interactive "P") (bjo--expand-macro t hygienic)) (defun bjo-expand-macro-1 (&optional hygienic) "Show one step of the expansion of the macro call at point. With a prefix argument HYGIENIC, keep the names hygiene made." (interactive "P") (bjo--expand-macro nil hygienic)) ;; As SLIME binds its macroexpanders. (with-eval-after-load 'bjo-mode (define-key bjo-mode-map (kbd "C-c C-m") #'bjo-expand-macro-1) (define-key bjo-mode-map (kbd "C-c M-m") #'bjo-expand-macro)) (provide 'bjolang-lsp) ;;; bjolang-lsp.el ends here