diff --git a/init.el b/init.el index 8e8833d..be6179f 100644 --- a/init.el +++ b/init.el @@ -23,11 +23,8 @@ (setenv "PATH" (concat + (concat (getenv "HOME") "/Development/cando/build/boehmprecise:") ; Need so that *compiliation* works like *shell* "/usr/local/bin:" - (concat (getenv "HOME") "/anaconda/bin:") ; Need so that *compiliation* works like *shell* - (concat (getenv "HOME") "/miniconda2/bin:") ; Need so that *compiliation* works like *shell* - "/usr/local/bin:" - "/usr/local/opt/llvm@14/bin:" "/usr/bin:" "/bin:" "/usr/sbin:" @@ -99,7 +96,7 @@ '(gdb-non-stop-setting nil) '(magit-pull-arguments nil) '(package-selected-packages - '(indent-bars which-key highlight-indentation highlight-indent-guides rust-mode slime-repl-ansi-color ace-window clipetty free-keys load-theme-buffer-local color-theme-buffer-local evil-collection slime-autoloads use-package wgrep-ag ag command-log-mode iedit wgrep clang-format+ git-wip-timemachine realgud-lldb ztree fireplace folding fold-dwim json-mode slime rainbow-blocks paredit magit gnuplot git-timemachine ggtags flylisp evil clang-format)) + '(evil-terminal-cursor-changer indent-bars which-key highlight-indentation highlight-indent-guides rust-mode slime-repl-ansi-color ace-window clipetty free-keys load-theme-buffer-local color-theme-buffer-local evil-collection slime-autoloads use-package wgrep-ag ag command-log-mode iedit wgrep clang-format+ git-wip-timemachine realgud-lldb ztree fireplace folding fold-dwim json-mode slime rainbow-blocks paredit magit gnuplot git-timemachine ggtags flylisp evil clang-format)) '(safe-local-variable-values '((package . rune-dom) (Encoding . utf-8) @@ -114,15 +111,15 @@ (c-toggle-comment-style 1)) (eval c-set-offset 'innamespace 0) (eval c-set-offset 'brace-list-open 0) - (Package . CLPYTHON.APP.REPL) - (Package . CLPYTHON.PARSER) + (Package . CLPYTHON\.APP\.REPL) + (Package . CLPYTHON\.PARSER) (Readtable . PY-AST-USER-READTABLE) (Package . CLPYTHON) (readtable . py-user-readtable) (package . clpython) (Readtable . PY-USER-READTABLE) - (Package . CLPYTHON.TEST) - (Package . CLPYTHON.UTIL) + (Package . CLPYTHON\.TEST) + (Package . CLPYTHON\.UTIL) (Package . CL-INTERPOL) (Package . CLIM-INTERNALS) (Package ITERATE :use "COMMON-LISP" :colon-mode :external) @@ -223,6 +220,7 @@ (use-package cl-lib) +(use-package w3m) (use-package ag) (use-package svg) (use-package yasnippet) @@ -239,6 +237,14 @@ (use-package rust-mode) (use-package ace-window) (use-package which-key) +(use-package evil-terminal-cursor-changer) + +(load "~/.emacs.d/mgl-pax.el") +(mgl-pax-hijack-slime-doc-keys) +(global-set-key (kbd "C-.") 'mgl-pax-document) +;;;(global-set-key (kbd "s-x t") 'mgl-pax-transcribe-last-expression) +;;;(global-set-key (kbd "s-x r") 'mgl-pax-retranscribe-region) + (setq byte-compile-warnings '(cl-functions)) @@ -367,6 +373,9 @@ (setq evil-insert-state-cursor '("chartreuse3" bar)) (setq evil-normal-state-cursor '("white" box)) +(unless (display-graphic-p) + (require 'evil-terminal-cursor-changer) + (evil-terminal-cursor-changer-activate)) (defun my-evil-state-color () "Change mode-line color based on the current Evil state." diff --git a/mgl-pax.el b/mgl-pax.el new file mode 100644 index 0000000..244e6a8 --- /dev/null +++ b/mgl-pax.el @@ -0,0 +1,1268 @@ +;; -*- lexical-binding: t -*- + +;;;; MGL-PAX Emacs integration +;;;; ========================= +;;;; +;;;; SETUP (see MGL-PAX::@EMACS-SETUP) +;;;; --------------------------------- +;;;; +;;;; - `mgl-pax-autoload' +;;;; +;;;; - `mgl-pax-reload' +;;;; +;;;; - `mgl-pax-hijack-slime-doc-keys' +;;;; +;;;; - `mgl-pax-browser-function' +;;;; +;;;; - `mgl-pax-web-server-port' +;;;; +;;;; NAVIGATE (see MGL-PAX::@NAVIGATING-IN-EMACS) +;;;; -------------------------------------------- +;;;; +;;;; - `M-.' (`slime-edit-definition') supports new kinds of +;;;; definitions (e.g. of ASDF/SYSTEMs) and disambiguates based on +;;;; nearby locatives. Just by loading this file, `M-.' shall be +;;;; able to recognize disambiguate based on locatives near point as +;;;; in "function FOO". +;;;; +;;;; - Also, see `mgl-pax-edit-parent-section'. +;;;; +;;;; DOCUMENT (see MGL-PAX::@BROWSING-LIVE-DOCUMENTATION) +;;;; ---------------------------------------------------- +;;;; +;;;; - Browse documentation of definitions in the running Lisp live +;;;; without explicitly generating documentation with +;;;; `mgl-pax-document'. Bind it to `C-.' to parallel `M-.'. +;;;; +;;;; - Also, see `mgl-pax-current-definition-toggle-view'. +;;;; +;;;; - `mgl-pax-apropos', `mgl-pax-apropos-all' and +;;;; `mgl-pax-apropos-package' are replacements for `slime-apropos' +;;;; `slime-apropos-all' and `slime-apropos-package', respectively. +;;;; They are all built on top of `mgl-pax-document'. +;;;; +;;;; TRANSCRIBE (see MGL-PAX::@TRANSCRIBING-WITH-EMACS (press `C-.' on this)) +;;;; ------------------------------------------------------------------------ +;;;; +;;;; - For `mgl-pax-transcribe-last-expression' and +;;;; `mgl-pax-retranscribe-region'. + +(eval-and-compile + (require 'cl-lib nil t) + ;; For emacs 23, look for bundled version + (require 'cl-lib "lib/cl-lib") + (require 'slime)) + + +;;;; Autoloading of MGL-PAX on the Common Lisp side + +(defcustom mgl-pax-autoload t + "If true, then the MGL-PAX ASDF system will be loaded as necessary +via Slime by `slime-edit-definition', `mgl-pax-document' and +other mgl-pax commands. Furthermore, when +`mgl-pax-browser-function' is not 'w3m-browse-url', +`mgl-pax-document' will start a web server on the Common Lisp +side." + :type 'boolean + :group 'mgl-pax) + +(defvar mgl-pax-version) +(setq mgl-pax-version '(0 3 0)) + +(defun mgl-pax-maybe-autoload (no-web cont) + (if (or no-web (mgl-pax-use-w3m)) + (mgl-pax-maybe-autoload-1 cont) + (mgl-pax-ensure-web-server cont))) + +(defun mgl-pax-maybe-autoload-1 (cont) + (let ((check-version-form + `(cl:and (cl:find-package :mgl-pax) + (cl:funcall (cl:find-symbol + (cl:string '#:check-pax-elisp-version) + (cl:find-package :mgl-pax)) + ',mgl-pax-version) + t))) + (if mgl-pax-autoload + (slime-eval-async + `(cl:progn + (cl:unless + (cl:and (cl:find-package :mgl-pax) + ;; Not there if only mgl-pax-bootstrap is loaded. + (cl:find-symbol + (cl:string '#:check-pax-elisp-version) + (cl:find-package :mgl-pax))) + (cl:format t "~&;; Autoloading MGL-PAX for Emacs ~ + (mgl-pax-autoload is t).~%") + (asdf:load-system "mgl-pax") + (cl:format t ";; Done autoloading MGL-PAX for Emacs~%")) + ,check-version-form) + cont) + (slime-eval-async check-version-form cont)))) + +(cl-defmacro mgl-pax-ensure-pax-loaded ((&key no-web) &body body) + (declare (indent 1)) + `(mgl-pax-maybe-autoload ,no-web (lambda (loadedp) + (if (not loadedp) + (mgl-pax-not-loaded) + ,@body)))) + +(defun mgl-pax-not-loaded () + (message "MGL-PAX is not loaded. See the variable mgl-pax-autoload.")) + +(defvar mgl-pax-file-name) +(setq mgl-pax-file-name load-file-name) + +(defun mgl-pax-reload () + "Reload mgl-pax.el. This may be necessary after upgrading MGL-PAX. +See MGL-PAX::@EMACS-SETUP." + (interactive) + (let ((sourcefile (concat (file-name-sans-extension mgl-pax-file-name) + ".el"))) + (load-file sourcefile))) + + +(defun mgl-pax-hijack-slime-doc-keys () + "Make the following changes to `slime-doc-map' (assuming it's +bound to `C-c C-d'). + +- `C-c C-d a': `mgl-pax-apropos' (replaces `slime-apropos') +- `C-c C-d z': `mgl-pax-aproposa-all' (replaces `slime-apropos-all') +- `C-c C-d p': `mgl-pax-apropos-package' (replaces `slime-apropos-package') +- `C-c C-d d': `mgl-pax-document' (replaces `slime-describe-symbol') +- `C-c C-d f': `mgl-pax-document' (replaces `slime-describe-function') +- `C-c C-d c': `mgl-pax-current-definition-toggle-view' + +Also, regardless of whether `w3m' is available, add this: + +- `C-c C-d u': `mgl-pax-edit-parent-section' + +In addition, because it can be almost as useful as `M-.', one may +want to give `mgl-pax-document' a more convenient binding such as +`C-.' or `s-.' if you have a Super key. For example, to bind +`C-.' in all Slime buffers: + + (slime-bind-keys slime-parent-map nil '((\"C-.\" mgl-pax-document))) + +To bind `C-.' globally: + + (global-set-key (kbd \"C-.\") 'mgl-pax-document)" + ;; end-hijack-include + (interactive) + (slime-bind-keys slime-doc-map t + '((?a mgl-pax-apropos) + (?z mgl-pax-apropos-all) + (?p mgl-pax-apropos-package) + (?d mgl-pax-document) + (?f mgl-pax-document) + (?c mgl-pax-current-definition-toggle-view))) + (slime-bind-keys slime-doc-map t + '((?u mgl-pax-edit-parent-section)))) + + +;;;; Browser configuration + +(defcustom mgl-pax-browser-function 'w3m-browse-url + "The name of the function to use to browse URLs. +When nil, the value of `browse-url-browser-function' is used. If +the effective value is `w3m-browse-url', then browsing will take +place in Emacs buffers using `w3m', and no webserver will be run +on the Common Lisp side." + :type 'symbol + :group 'mgl-pax) + +(defcustom mgl-pax-web-server-port nil + "If the web server is started, it will be on this port. +See `mgl-pax-autoload'. If nil, then a free port will be used." + :type 'natnum + :group 'mgl-pax) + +(defun mgl-pax-use-w3m () + (eq (or mgl-pax-browser-function browse-url-browser-function) + 'w3m-browse-url)) + +(defvar mgl-pax-web-server-base-url) + +(defun mgl-pax-ensure-web-server (cont) + (mgl-pax-maybe-autoload-1 + (lambda (loadedp) + (if (not loadedp) + (funcall cont nil) + (slime-eval-async `(mgl-pax::ensure-web-server + :hyperspec-root ',common-lisp-hyperspec-root + :port ,mgl-pax-web-server-port) + (lambda (values) + (if (eq (cl-first values) :error) + (message "%s" (cl-second values)) + (cl-assert (eq (cl-first values) :base-url)) + (setq mgl-pax-web-server-base-url (cl-second values)) + (funcall cont t)))))))) + + + +;;;; Find possible objects and locatives at point (see MGL-PAX::WALL). + +;;; Return a list of of things like (object (locative1 locative2 ...)) +;;; representing the possible references (object locative1), (object +;;; locative2), and so on. MGL-PAX::LOCATE-DEFINITIONS-FOR-EMACS and +;;; MGL-PAX::DOCUMENT-FOR-EMACS take such lists. +;;; +;;; `slime-symbol-at-point' works fine in code, but in printed +;;; representations and docstrings heuristics are needed (just think +;;; "SYM." and "#) generated by +;;;; PAX before definitions (e.g. function signature lines, SECTION +;;;; titles). + +(defun mgl-pax-doc-next-definition () + "Move point to the next PAX definition. +Use it in a PAX doc buffer (see `mgl-pax-document')." + (interactive) + (let ((start (mgl-pax-doc-definition-start))) + (if (and start (< (point) start)) + (goto-char start) + (let ((next (mgl-pax-doc-next-definition-start))) + (if next + (goto-char next) + (unless start + ;; There are no PAX definitions at all. Just move to the + ;; next link. + (w3m-next-anchor))))))) + +(defun mgl-pax-doc-previous-definition () + "Move point to the previous PAX definition. +Use it in a PAX doc buffer (see `mgl-pax-document')." + (interactive) + (let ((start (mgl-pax-doc-definition-start))) + (if (and start (< start (point))) + (goto-char start) + (let ((prev (mgl-pax-doc-prev-definition-start))) + (if prev + (goto-char prev) + (unless start + (w3m-previous-anchor))))))) + +;;; Return the buffer position of the first character of the link +;;; corresponding to the current definition. +(defun mgl-pax-doc-definition-start () + (mgl-pax-definition-link-pos + (previous-single-property-change (if (< (point) (buffer-size)) + (1+ (point)) + (point)) + 'w3m-name-anchor2))) + +(defun mgl-pax-doc-next-definition-start () + (mgl-pax-definition-link-pos + (next-single-property-change (point) 'w3m-name-anchor2))) + +(defun mgl-pax-doc-prev-definition-start () + (let ((this (previous-single-property-change (if (< (point) (buffer-size)) + (1+ (point)) + (point)) + 'w3m-name-anchor2))) + (when this + (mgl-pax-definition-link-pos + (previous-single-property-change this 'w3m-name-anchor2))))) + +(defun mgl-pax-definition-link-pos (pos) + (when pos + (save-excursion + (goto-char pos) + (unless (w3m-anchor) + (w3m-next-anchor)) + (point)))) + +(defun mgl-pax-doc-up-definition () + "Follow the first \"Up:\" link at the top of the PAX documentation if any. + +That is, in a PAX doc buffer (see `mgl-pax-document'), open a new +URL with the documentation of the first containing section and +put point on the definition corresponding the current page. + +When there multiple sections that contain the current object, the +first one will be chosen heuristically based on the similarity of +the names of the SYMBOL-PACKAGEs of their names." + (interactive) + (let ((url (mgl-pax-doc-url-up))) + (when url + (w3m-goto-url url) + t))) + +(defun mgl-pax-doc-up-definition-and-beginning-of-buffer () + "Like `mgl-pax-doc-up-definition', but also move point to +the beginning of the buffer. If there is no \"Up:\" link, then +move point to the beginning of the buffer." + (interactive) + (let ((url (mgl-pax-doc-url-up t))) + (if (null url) + (beginning-of-buffer) + (w3m-goto-url url) + t))) + +(defun mgl-pax-doc-url-up (&optional strip-fragment-p) + (when (mgl-pax-doc-has-up-line-p) + (save-excursion + (beginning-of-buffer) + (w3m-next-anchor) + (let ((url (w3m-anchor))) + (when url + (if strip-fragment-p + (w3m-url-strip-fragment url) + url)))))) + +(defun mgl-pax-doc-has-up-line-p () + (save-excursion + (beginning-of-buffer) + (forward-line) + (and (<= (+ (point) 4) (buffer-size)) + (string= (buffer-substring-no-properties (point) (+ (point) 4)) + "Up: ")))) + +(defun mgl-pax-doc-edit-current-definition () + "Visit the source of the current PAX definition on the page." + (interactive) + (mgl-pax-doc-edit-pax-definition + (or (mgl-pax-doc-current-definition-pax-url) + ;; There is always a current definition unless the point is + ;; before the first definition, so default to that. + (mgl-pax-doc-first-definition-pax-url)))) + +(defun mgl-pax-doc-current-definition-pax-url () + (let ((pos (mgl-pax-doc-definition-start))) + (when pos + (save-excursion + (goto-char pos) + (mgl-pax-doc-pax-url (w3m-anchor)))))) + +(defun mgl-pax-doc-edit-first-definition () + "Visit the source of the first PAX definition on the page." + (interactive) + (mgl-pax-doc-edit-pax-definition (mgl-pax-doc-first-definition-pax-url))) + +(defun mgl-pax-doc-first-definition-pax-url () + (save-excursion + (beginning-of-buffer) + (mgl-pax-doc-next-definition) + (mgl-pax-doc-pax-url (w3m-anchor)))) + + +;;;; Make `M-.' (`slime-edit-definition') work on links in w3m PAX +;;;; doc. + +;;; If over a link in a w3m buffer, then visit the source if it is a +;;; "pax:" or "file:" URL, else do nothing. For "pax:" URLs, the URL +;;; itself identifies the target. For "file:" URLs, the target is the +;;; PAX reference encoded in the fragment part of the URL if any. +(defun mgl-pax-doc-edit-definition (name &optional where) + (let ((url (and (fboundp 'w3m-anchor) + (mgl-pax-doc-pax-url (w3m-anchor))))) + (mgl-pax-doc-edit-pax-definition url))) + +(defun mgl-pax-doc-edit-pax-definition (pax-url) + (when (string-prefix-p "pax:" pax-url) + (slime-eval-async + ;; Silently fail if MGL-PAX is not loaded. + `(cl:when (cl:find-package :mgl-pax) + (cl:funcall + (cl:find-symbol (cl:string '#:locate-pax-url-for-emacs) + :mgl-pax) + ',pax-url)) + 'mgl-pax-visit-locations))) + +(defun mgl-pax-doc-pax-url (url) + (cond ((string-prefix-p "pax:" url) + url) + ((string-prefix-p "file:" url) + (let ((fragment (elt (mgl-pax-parse-path-and-fragment url) 1))) + (when fragment + (concat "pax:" fragment)))))) + +(add-hook 'slime-edit-definition-hooks 'mgl-pax-doc-edit-definition) + + +;;;; Determining the current definition + +(defun mgl-pax-current-definition-possible-names () + (save-excursion + (when (looking-at "(") + (ignore-errors (down-list))) + (cl-loop for name-snippet-and-pos + = (mgl-pax-current-sexp-first-arg-snippet-and-pos) + when name-snippet-and-pos + collect name-snippet-and-pos + while (ignore-errors (backward-up-list 1 t t) + t)))) + +;;; Return 1. the first argument of the current sexp if it's a symbol, +;;; 2. the Slime source location :SNIPPET, 3. the start position of +;;; the sexp. If any movement fails or the first argument is not a +;;; symbol, then return nil. +(defun mgl-pax-current-sexp-first-arg-snippet-and-pos () + (ignore-errors + (save-excursion + (backward-up-list 1 t t) + (let ((snippet (mgl-pax-next-sexp)) + (pos (point))) + (when (< 200 (length snippet)) + (setq snippet (cl-subseq snippet 0 200))) + (down-list) + (slime-forward-sexp) + (forward-char) + ;; `name' can be a symbol or a string ... + (let ((name (mgl-pax-next-sexp))) + ;; ... but currently never a list. + (unless (string-prefix-p "(" name) + (list name snippet pos))))))) + + +(defun mgl-pax-current-definition-toggle-view () + "Document the definition `point' is in with `mgl-pax-document'. +In a PAX doc buffer, it's equivalent to pressing `v' +(`mgl-pax-doc-edit-current-definition')." + (interactive) + (if (mgl-pax-in-doc-buffer-p) + (mgl-pax-doc-edit-current-definition) + (mgl-pax-ensure-pax-loaded () + (mgl-pax-current-definition-pax-url 'mgl-pax-document)))) + +(defun mgl-pax-current-definition-pax-url (cont) + (slime-eval-async + `(cl:if (cl:find-package :mgl-pax) + (cl:funcall + (cl:find-symbol (cl:string + '#:current-definition-pax-url-for-emacs) + :mgl-pax) + ',(buffer-name) + ',(buffer-file-name) + ',(mgl-pax-current-definition-possible-names)) + '(:error "MGL-PAX is not loaded.")) + (lambda (values) + (if (eq (cl-first values) :error) + (message "%s" (cl-second values)) + (apply cont (cl-rest values)))))) + + +(defun mgl-pax-edit-parent-section () + "Look up the definition of parent section of the definition +`point' is in as if with `M-.' (`slime-edit-definition'). If +there are multiple containing sections, then pop up a selection +buffer." + (interactive) + (mgl-pax-ensure-pax-loaded (:no-web t) + (mgl-pax-find-parent-section #'mgl-pax-visit-locations))) + +(defun mgl-pax-find-parent-section (cont) + (slime-eval-async + `(cl:if (cl:find-package :mgl-pax) + (cl:funcall + (cl:find-symbol (cl:string + '#:find-parent-section-for-emacs) + :mgl-pax) + ',(buffer-name) + ',(buffer-file-name) + ',(mgl-pax-current-definition-possible-names)) + '(:error "MGL-PAX is not loaded.")) + cont)) + + +;;;; Apropos + +(defun mgl-pax-apropos (string &optional external-only package + case-sensitive) + "Show all PAX definitions that match the arguments. +This is a wrapper around DREF:DREF-APROPOS. STRING is basically +NAME and LOCATIVE-TYPES concatenated with a space in between. If +STRING or PACKAGE starts with `?'', then only exact matches with +a symbol or package name are accepted. + +- \"print\" matches definitions whose names contain \"print\" as + a substring. + +- \"'print\" matches definitions whose names are \"print\" (still + subject to CASE-SENSITIVE). + +- \"print function\" matches functions whose names contain + \"print\" (e.g. CL:PRINT and CL:PPRINT). + +- \"'print function\" is like the previous example but with exact + name match. + +- \"print variable\" matches for example *PRINT-ESCAPE*. + +- \"print variable function\" matches all variables and functions + with \"print\" in their names. + +- \" pax:section\" (note the leading space) matches all PAX + sections (note that EXTERNAL-ONLY NIL is necessary to see most + of them). + +- \"print :lisp\" matches definitions with + DREF:LISP-LOCATIVE-TYPES, which is the default. + +- \"print :pseudo\" matches definitions with + DREF:PSEUDO-LOCATIVE-TYPES such as PAX:CLHS and DREF:UNKNOWN. + +- \"print :all\" matches definitions with all locative + types (DREF:LOCATIVE-TYPES). + +With a prefix arg, you're interactively asked for parameters of +the search. Without a prefix arg, EXTERNAL-ONLY defaults to T, +packages and locative types are not filtered, and case does not +matter. + +Also, see `mgl-pax-apropos-all'." + (interactive (list nil nil nil nil)) + (mgl-pax-ensure-pax-loaded () + (mgl-pax-with-nlx-barrier + (mgl-pax-document + (mgl-pax-make-pax-eval-url + (if string + `(mgl-pax::pax-apropos* ,string ,external-only + ,package ,case-sensitive) + `(mgl-pax::pax-apropos* + ;; Do the defaulting of arguments here instead of in + ;; `interactive' because + ;; `mgl-pax-read-urllike-from-minibuffer' relies on + ;; `mgl-pax-ensure-pax-loaded' having succeeded. + ,@(if current-prefix-arg + (list (mgl-pax-read-urllike-from-minibuffer + "PAX Apropos: ") + (y-or-n-p "External symbols only? ") + (slime-read-package-name "Package: ") + (y-or-n-p "Case-sensitive? ")) + (list (mgl-pax-read-urllike-from-minibuffer + "PAX Apropos: ") + t nil nil))))))))) + +(defun mgl-pax-make-pax-eval-url (sexp) + (concat "pax-eval:" (url-encode-url (prin1-to-string sexp)))) + +(defun mgl-pax-apropos-all (string) + "Shortcut for invoking `mgl-pax-apropos' with EXTERNAL-ONLY NIL." + (interactive (list nil)) + (mgl-pax-ensure-pax-loaded () + (mgl-pax-with-nlx-barrier + (let ((string (or string (mgl-pax-read-urllike-from-minibuffer + "PAX Apropos All: ")))) + (mgl-pax-apropos string nil "" nil))))) + +(defun mgl-pax-apropos-package (package &optional internal) + "Show apropos listing for symbols in PACKAGE. +With prefix argument include internal symbols. +The empty string means the current package." + (interactive (list (let ((pkg (slime-read-package-name + "PAX Apropos for Package: "))) + (if (string= pkg "") (slime-current-package) pkg)) + current-prefix-arg)) + (mgl-pax-apropos "" (not internal) (concat "'" package) nil)) + + +;;;; Transcribe + +(defun mgl-pax-transcribe-last-expression () + "A bit like C-u C-x C-e (slime-eval-last-expression) that +inserts the output and values of the sexp before the point, this +does the same but with MGL-PAX:TRANSCRIBE. Use a numeric prefix +argument as in index to select one of the Common Lisp +MGL-PAX:*SYNTAXES* as the SYNTAX argument to MGL-PAX:TRANSCRIBE. +Without a prefix argument, the first syntax is used." + (interactive) + (mgl-pax-ensure-pax-loaded (:no-web t) + (save-excursion + (let ((dynenv (mgl-pax-find-cl-transcript-dynenv))) + (let* ((start (progn (backward-sexp) + ;; If the last expression is in a + ;; comment, we need this for + ;; forward-sexp below. + (save-excursion + (move-beginning-of-line nil) + (point)))) + (end (progn (forward-sexp) + (point)))) + (goto-char end) + (insert + (mgl-pax-transcribe start end (mgl-pax-transcribe-syntax-arg) + nil nil nil dynenv)) + ;; The transcript ends with a newline. Delete it if it + ;; would result in a blank line. + (when (looking-at "\n") + (delete-char 1))))))) + +(defun mgl-pax-retranscribe-region (start end) + "Updates the transcription in the current region (as in calling +MGL-PAX:TRANSCRIBE with :UPDATE-ONLY T). Use a numeric prefix +argument as an index to select one of the Common Lisp +MGL-PAX:*TRANSCRIBE-SYNTAXES* as the SYNTAX argument to +MGL-PAX:TRANSCRIBE. Without a prefix argument, the syntax of the +input will not be changed." + (interactive "r") + (mgl-pax-ensure-pax-loaded (:no-web t) + (let ((dynenv (mgl-pax-find-cl-transcript-dynenv))) + (let* ((point-at-start-p (= (point) start)) + (point-at-end-p (= (point) end)) + (transcript (mgl-pax-transcribe start end + (mgl-pax-transcribe-syntax-arg) + t t nil dynenv))) + (if point-at-start-p + (save-excursion + (goto-char start) + (delete-region start end) + (insert transcript)) + (save-excursion + (goto-char start) + (delete-region start end)) + (insert transcript)))))) + +(defun mgl-pax-transcribe-syntax-arg () + (if current-prefix-arg + (prefix-numeric-value current-prefix-arg) + nil)) + +;;; Within the current defun, find the first occurrence of "```" +;;; backwards from point, and if it is followed by "cl-transcript", +;;; return its dynenv argument." +(defun mgl-pax-find-cl-transcript-dynenv () + (save-excursion + (save-restriction + (narrow-to-defun) + (when (search-backward "```" nil t) + (when (looking-at "```cl-transcript") + (save-restriction + (narrow-to-region (point) (save-excursion + (end-of-line) + (point))) + (when (search-forward ":dynenv" nil t) + (mgl-pax-next-sexp)))))))) + +(defun mgl-pax-transcribe (start end syntax update-only echo + first-line-special-p dynenv) + (slime-eval + `(cl:funcall (cl:find-symbol (cl:string '#:transcribe-for-emacs) :mgl-pax) + ,(buffer-substring-no-properties start end) + ',syntax ',update-only ',echo ',first-line-special-p ,dynenv))) + +(provide 'mgl-pax)