dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

commit ba24e2738838445966beeb3dee50735dcbdaf556
parent 295f7f7458ec38718a9469de8002646a87100d2b
Author: Lukas Henkel <lh@entf.net>
Date:   Fri, 17 Nov 2023 23:04:49 +0100

Upgrade sly

Pin sly to melpa, the stable version on nongnu is super old and seems
to start having problems in newer Emacs.

Diffstat:
Delpa/sly-1.0.43.signed | 2--
Delpa/sly-1.0.43/.travis.yml | 32--------------------------------
Delpa/sly-1.0.43/CONTRIBUTING.md | 332-------------------------------------------------------------------------------
Delpa/sly-1.0.43/Makefile | 114-------------------------------------------------------------------------------
Delpa/sly-1.0.43/NEWS.md | 633-------------------------------------------------------------------------------
Delpa/sly-1.0.43/PROBLEMS.md | 83-------------------------------------------------------------------------------
Delpa/sly-1.0.43/README.md | 196-------------------------------------------------------------------------------
Delpa/sly-1.0.43/contrib/sly-mrepl.el | 1510-------------------------------------------------------------------------------
Delpa/sly-1.0.43/contrib/sly-package-fu.el | 454-------------------------------------------------------------------------------
Delpa/sly-1.0.43/contrib/sly-stickers.el | 1359-------------------------------------------------------------------------------
Delpa/sly-1.0.43/contrib/slynk-mrepl.lisp | 747-------------------------------------------------------------------------------
Delpa/sly-1.0.43/doc/Makefile | 123-------------------------------------------------------------------------------
Delpa/sly-1.0.43/doc/animations/backreferences.gif | 0
Delpa/sly-1.0.43/doc/animations/company-flex-completion.gif | 0
Delpa/sly-1.0.43/doc/animations/reverse-isearch.gif | 0
Delpa/sly-1.0.43/doc/animations/stickers-example.gif | 0
Delpa/sly-1.0.43/doc/contributors.texi | 31-------------------------------
Delpa/sly-1.0.43/doc/sly-refcard.tex | 121-------------------------------------------------------------------------------
Delpa/sly-1.0.43/doc/sly.css | 22----------------------
Delpa/sly-1.0.43/doc/sly.texi | 3208-------------------------------------------------------------------------------
Delpa/sly-1.0.43/doc/texinfo-tabulate.awk | 21---------------------
Delpa/sly-1.0.43/lib/sly-buttons.el | 324-------------------------------------------------------------------------------
Delpa/sly-1.0.43/lib/sly-cl-indent.el | 1782-------------------------------------------------------------------------------
Delpa/sly-1.0.43/lib/sly-completion.el | 764-------------------------------------------------------------------------------
Delpa/sly-1.0.43/lib/sly-messages.el | 156-------------------------------------------------------------------------------
Delpa/sly-1.0.43/lib/sly-tests.el | 1546-------------------------------------------------------------------------------
Delpa/sly-1.0.43/sly-autoloads.el | 142-------------------------------------------------------------------------------
Delpa/sly-1.0.43/sly-pkg.el | 2--
Delpa/sly-1.0.43/sly.el | 7481-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/backend/abcl.lisp | 1536-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/backend/allegro.lisp | 1116-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/backend/clasp.lisp | 713-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/backend/lispworks.lisp | 1026-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/backend/mkcl.lisp | 933-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk-apropos.lisp | 154-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk-backend.lisp | 1675-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk-gray.lisp | 207-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk-source-path-parser.lisp | 246-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk.asd | 115-------------------------------------------------------------------------------
Delpa/sly-1.0.43/slynk/slynk.lisp | 4200-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-autodoc-tests.el | 200-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-cl-indent-test.txt | 1008-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-fontifying-fu-tests.el | 138-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-indentation-tests.el | 114-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-mrepl-tests.el | 97-------------------------------------------------------------------------------
Delpa/sly-1.0.43/test/sly-parse-tests.el | 59-----------------------------------------------------------
Delpa/sly-1.0.43/test/sly-stickers-tests.el | 172-------------------------------------------------------------------------------
Relpa/sly-1.0.43/contrib/sly-autodoc.el -> elpa/sly-20231009.2150/contrib/sly-autodoc.el | 0
Relpa/sly-1.0.43/contrib/sly-fancy-inspector.el -> elpa/sly-20231009.2150/contrib/sly-fancy-inspector.el | 0
Relpa/sly-1.0.43/contrib/sly-fancy-trace.el -> elpa/sly-20231009.2150/contrib/sly-fancy-trace.el | 0
Relpa/sly-1.0.43/contrib/sly-fancy.el -> elpa/sly-20231009.2150/contrib/sly-fancy.el | 0
Relpa/sly-1.0.43/contrib/sly-fontifying-fu.el -> elpa/sly-20231009.2150/contrib/sly-fontifying-fu.el | 0
Relpa/sly-1.0.43/contrib/sly-indentation.el -> elpa/sly-20231009.2150/contrib/sly-indentation.el | 0
Aelpa/sly-20231009.2150/contrib/sly-mrepl.el | 1568+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/contrib/sly-package-fu.el | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/contrib/sly-profiler.el -> elpa/sly-20231009.2150/contrib/sly-profiler.el | 0
Relpa/sly-1.0.43/contrib/sly-retro.el -> elpa/sly-20231009.2150/contrib/sly-retro.el | 0
Relpa/sly-1.0.43/contrib/sly-scratch.el -> elpa/sly-20231009.2150/contrib/sly-scratch.el | 0
Aelpa/sly-20231009.2150/contrib/sly-stickers.el | 1359+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/contrib/sly-trace-dialog.el -> elpa/sly-20231009.2150/contrib/sly-trace-dialog.el | 0
Relpa/sly-1.0.43/contrib/sly-tramp.el -> elpa/sly-20231009.2150/contrib/sly-tramp.el | 0
Relpa/sly-1.0.43/contrib/slynk-arglists.lisp -> elpa/sly-20231009.2150/contrib/slynk-arglists.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-fancy-inspector.lisp -> elpa/sly-20231009.2150/contrib/slynk-fancy-inspector.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-indentation.lisp -> elpa/sly-20231009.2150/contrib/slynk-indentation.lisp | 0
Aelpa/sly-20231009.2150/contrib/slynk-mrepl.lisp | 752+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/contrib/slynk-package-fu.lisp -> elpa/sly-20231009.2150/contrib/slynk-package-fu.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-profiler.lisp -> elpa/sly-20231009.2150/contrib/slynk-profiler.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-retro.lisp -> elpa/sly-20231009.2150/contrib/slynk-retro.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-stickers.lisp -> elpa/sly-20231009.2150/contrib/slynk-stickers.lisp | 0
Relpa/sly-1.0.43/contrib/slynk-trace-dialog.lisp -> elpa/sly-20231009.2150/contrib/slynk-trace-dialog.lisp | 0
Relpa/sly-1.0.43/contrib/sylvesters.txt -> elpa/sly-20231009.2150/contrib/sylvesters.txt | 0
Aelpa/sly-20231009.2150/contributors.info | 44++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/dir | 18++++++++++++++++++
Relpa/sly-1.0.43/doc/images/stickers-1-placed-stickers.png -> elpa/sly-20231009.2150/images/stickers-1-placed-stickers.png | 0
Relpa/sly-1.0.43/doc/images/stickers-2-armed-stickers.png -> elpa/sly-20231009.2150/images/stickers-2-armed-stickers.png | 0
Relpa/sly-1.0.43/doc/images/stickers-3-replay-stickers.png -> elpa/sly-20231009.2150/images/stickers-3-replay-stickers.png | 0
Relpa/sly-1.0.43/doc/images/stickers-4-breaking-stickers.png -> elpa/sly-20231009.2150/images/stickers-4-breaking-stickers.png | 0
Relpa/sly-1.0.43/doc/images/stickers-5-fetch-recordings.png -> elpa/sly-20231009.2150/images/stickers-5-fetch-recordings.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-1.png -> elpa/sly-20231009.2150/images/tutorial-1.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-2.png -> elpa/sly-20231009.2150/images/tutorial-2.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-3.png -> elpa/sly-20231009.2150/images/tutorial-3.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-4.png -> elpa/sly-20231009.2150/images/tutorial-4.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-5.png -> elpa/sly-20231009.2150/images/tutorial-5.png | 0
Relpa/sly-1.0.43/doc/images/tutorial-6.png -> elpa/sly-20231009.2150/images/tutorial-6.png | 0
Relpa/sly-1.0.43/lib/.nosearch -> elpa/sly-20231009.2150/lib/.nosearch | 0
Relpa/sly-1.0.43/lib/hyperspec.el -> elpa/sly-20231009.2150/lib/hyperspec.el | 0
Aelpa/sly-20231009.2150/lib/sly-buttons.el | 324+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/lib/sly-cl-indent.el | 1796+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/lib/sly-common.el -> elpa/sly-20231009.2150/lib/sly-common.el | 0
Aelpa/sly-20231009.2150/lib/sly-completion.el | 798+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/lib/sly-messages.el | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/lib/sly-parse.el -> elpa/sly-20231009.2150/lib/sly-parse.el | 0
Aelpa/sly-20231009.2150/lib/sly-tests.el | 1545+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/sly-autoloads.el | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/sly-pkg.el | 8++++++++
Aelpa/sly-20231009.2150/sly.el | 7500+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/sly.info | 3543+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/backend/abcl.lisp | 1531+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/backend/allegro.lisp | 1116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/backend/ccl.lisp -> elpa/sly-20231009.2150/slynk/backend/ccl.lisp | 0
Aelpa/sly-20231009.2150/slynk/backend/clasp.lisp | 730+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/backend/clisp.lisp -> elpa/sly-20231009.2150/slynk/backend/clisp.lisp | 0
Relpa/sly-1.0.43/slynk/backend/cmucl.lisp -> elpa/sly-20231009.2150/slynk/backend/cmucl.lisp | 0
Relpa/sly-1.0.43/slynk/backend/corman.lisp -> elpa/sly-20231009.2150/slynk/backend/corman.lisp | 0
Relpa/sly-1.0.43/slynk/backend/ecl.lisp -> elpa/sly-20231009.2150/slynk/backend/ecl.lisp | 0
Aelpa/sly-20231009.2150/slynk/backend/lispworks.lisp | 1033+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/backend/mkcl.lisp | 935+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/backend/sbcl.lisp -> elpa/sly-20231009.2150/slynk/backend/sbcl.lisp | 0
Relpa/sly-1.0.43/slynk/backend/scl.lisp -> elpa/sly-20231009.2150/slynk/backend/scl.lisp | 0
Relpa/sly-1.0.43/slynk/metering.lisp -> elpa/sly-20231009.2150/slynk/metering.lisp | 0
Aelpa/sly-20231009.2150/slynk/slynk-apropos.lisp | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/slynk-backend.lisp | 1684+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/slynk-completion.lisp -> elpa/sly-20231009.2150/slynk/slynk-completion.lisp | 0
Aelpa/sly-20231009.2150/slynk/slynk-gray.lisp | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/slynk-loader.lisp -> elpa/sly-20231009.2150/slynk/slynk-loader.lisp | 0
Relpa/sly-1.0.43/slynk/slynk-match.lisp -> elpa/sly-20231009.2150/slynk/slynk-match.lisp | 0
Relpa/sly-1.0.43/slynk/slynk-rpc.lisp -> elpa/sly-20231009.2150/slynk/slynk-rpc.lisp | 0
Relpa/sly-1.0.43/slynk/slynk-source-file-cache.lisp -> elpa/sly-20231009.2150/slynk/slynk-source-file-cache.lisp | 0
Aelpa/sly-20231009.2150/slynk/slynk-source-path-parser.lisp | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/slynk.asd | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aelpa/sly-20231009.2150/slynk/slynk.lisp | 4234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Relpa/sly-1.0.43/slynk/start-slynk.lisp -> elpa/sly-20231009.2150/slynk/start-slynk.lisp | 0
Relpa/sly-1.0.43/slynk/xref.lisp -> elpa/sly-20231009.2150/slynk/xref.lisp | 0
Minit.el | 1+
124 files changed, 31988 insertions(+), 34894 deletions(-)

diff --git a/elpa/sly-1.0.43.signed b/elpa/sly-1.0.43.signed @@ -1 +0,0 @@ -Good signature from 066DAFCB81E42C40 GNU ELPA Signing Agent (2019) <elpasign@elpa.gnu.org> (trust undefined) created at 2020-12-19T15:55:02+0100 using RSA -\ No newline at end of file diff --git a/elpa/sly-1.0.43/.travis.yml b/elpa/sly-1.0.43/.travis.yml @@ -1,32 +0,0 @@ -dist: trusty -# Require sudo because Emacs needs to disable ASLR to dump, only -# possible on sudo-enabled environment. See also -# https://github.com/travis-ci/travis-ci/issues/9061. -sudo: required -language: emacs-lisp -cache: - - directories: - # Cache stable Emacs binaries (saves 1min per job) - - "$HOME/emacs/" - - "$HOME/.roswell/" - -env: - - "TARGET=check-core ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=25.2" - - "TARGET=check-fancy ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=25.2" - - "TARGET=check-fancy ROSWELL_LISP=ccl-bin EMACS_VERSION=26.1" - - "TARGET=check-core ROSWELL_LISP=ccl-bin EMACS_VERSION=26.1" - - "TARGET=check-mrepl ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=26.1" - - "TARGET=check-indentation ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=26.1" - - "TARGET=check-core ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=26.1" - - "TARGET=check-fancy ROSWELL_LISP=sbcl-bin/1.2.14 EMACS_VERSION=26.1" - -before_install: - - wget 'https://raw.githubusercontent.com/flycheck/emacs-travis/master/emacs-travis.mk' - - make -f emacs-travis.mk install_emacs - - export ROSWELL_INSTALL_DIR=~/.roswell && mkdir -p $ROSWELL_INSTALL_DIR - - export PATH=$ROSWELL_INSTALL_DIR/bin:$PATH - - export LISP=$ROSWELL_LISP - - curl -L https://raw.githubusercontent.com/snmsts/roswell/release/scripts/install-for-ci.sh | sh - -script: - - make LISP="ros run -L $ROSWELL_LISP" EMACS=$HOME/emacs/$EMACS_VERSION/src/emacs $TARGET diff --git a/elpa/sly-1.0.43/CONTRIBUTING.md b/elpa/sly-1.0.43/CONTRIBUTING.md @@ -1,332 +0,0 @@ -# The SLY Hacker's Handbook - -## Reporting bugs - -The most important thing when reporting bugs is making sure that the -developer has a way to reproduce it. To do this, he needs to rule out -interference from external factors like other Emacs extensions or -other Lisp-side code. Here's a great example of a bug report - -``` -$ emacs --version -Emacs 24.3 -$ sbcl --version -SBCL 1.2.1 -$ cd sly -sly $ emacs -Q -L . -l sly-autoloads --eval '(setq inferior-lisp-program "sbcl")' -f sly - -I get the REPL but when I try to X, I get Y -OR -I don't get the REPL at all because frankinbogen! -``` - - -## Coding style - -This section is very empty, in the meantime try to be sensible and -emulate or improve on SLY's existing style. - -### Commit messages - -ChangeLog files are gone! However, the syntax of ChangeLogs is very -useful to everybody and Emacs supports it perfectly: - -* in Emacs, for every snippet that you've changed, type `C-x 4 a` (or - `add-change-log-entry-other-window`) - -* Emacs will open up a ChangeLog buffer, but this is just a dummy - buffer that you can ignore. However, the content inside it should be - pasted (sans indentation) to the commit message. - -* As an added bonus, if you are using Emacs >= 24.4 and `vc-dir` to - prepare your commits, Emacs does that for you automatically. - -The benefits of this format are great. One can still use `M-x -vc-print-log` in a source file and browse through its ChangeLog -without the hassle of ChangeLog conflicts. - -### General philosophy - -I keep a sentence of the previous Coding Guide that I like very much. - -> Remember that to rewrite a program better is the sincerest form of -> code appreciation. When you can see a way to rewrite a part of SLY -> better, please do so! - - -## Lisp code file structure - -The code is organized into these files: - -* `slynk/slynk-backend.lisp`: Definition of the interface to non-portable -features. Stand-alone. - -* `slynk/backend/slynk-<cmucl|...>.lisp`: Back-end implementation -for a specific Common Lisp system. Uses slynk-backend.lisp. - -* `slynk/slynk.lisp`: The top-level server program, built from the other -components. Uses `slynk-backend.lisp` as an interface to the actual -backends. - -* `sly.el`: The Emacs front-end that the user actually interacts -with and that connects to the Slynk server to send expressions to, and -retrieve information from the running Common Lisp system. - -* `contrib/sly-<extension>.el`: Elisp code for SLY extensions. - -* `contrib/slynk-<extension>.lisp`: Supporting Common Lisp related -code for a particular extension. - - -## SLY-Slynk RPC protocol - -The info in this section would be something for a future "Slynk -Programmer's Guide" to be included in the regular manual or a separate -one. - -Follows a brief description of the SLY-Slynk protocol. The protocol is -*s-exp messages* over *s-exp primitives* over *UTF-8* over *TCP*. -Let's start top-down: - -### S-exp messages - -Most messages in the top group look like Lisp function calls. The -functions are known as "Slyfuns" and are defined with a `DEFSLYFUN` -operator in the `slynk-*.lisp` side. These are the "remote procedures" -of the RPC protocol. There must be about 100 or so of them, maybe -more, I haven't counted. Slyfuns appear in both Slynk's core and in -supporting contrib's Slynk code. - -For a future reference manual, I think there has to be a way to -automatically harvest the `DEFSLYFUN` definitions and their -docstrings. - -Another type of message contains calls to "channel methods". These are -slightly different from Slyfuns. Their return value is ignored, but -otherwise they also work like function calls. They're good for -expressing a reply-free evaluation in the context of a "channel". - -These are defined with `sly-define-channel-method` and -`DEFINE-CHANNEL-METHOD` and on the SLY and Slynk sides, respectively. - -The only use right now is in `sly-mrepl.el`, - -### S-exp primitives - -This is a much smaller set of primitives, the most common is -`:EMACS-REX`, "rex" is for "Remote EXecution". - -Informally it's saying: "here is Slyfun X's call number 3487 with -argumentss Y, for evaluation in thread Z" ). The asynchronous reply -`:RETURN`, if it ever arrives, will be "your call 3487 returned the -following sexp". - -```lisp -(:emacs-rex - (slynk:connection-info) - nil t 1) -(:return - (:ok - (:pid 16576 :style :spawn :encoding - :lisp-implementation - (:type "International Allegro CL Enterprise Edition" :name "allegro" :version "8.1 [Windows] (Sep 3, 2008 19:38)" :program nil) - :package - (:name "COMMON-LISP-USER" :prompt "CL-USER") - :version "1.0.0-alpha")) - 1) - ``` - -The return value, read into Elisp sexps is what is passed to the -callback argument to the Elisp function `sly-eval-async`. Here's the -way to get the PID of the underlying Slynk process. - -```elisp -(sly-eval-async '(slynk:connection-info) - (lambda (info) (plist-get info :pid))) -``` - -The primitives `:CHANNEL-SEND` and `:EMACS-CHANNEL-SEND` implement -channel methods. Channels are named by number, and normally have a -special serving thread in the Common Lisp implementation of -Slynk. Here is an extract showing the `:PROCESS`, `:WRITE-VALUES` and -`:PROMPT` channel methods for the REPL. - -```lisp -(:emacs-channel-send 1 - (:process "(list 1 2 3)")) -(:channel-send 1 - (:write-values - (("(1 2 3)" 2)))) -(:channel-send 1 - (:prompt "COMMON-LISP-USER" "CL-USER" 0)) -``` - -There are also debugger-specific primitives, like `:DEBUG-ACTIVATE` -and `:DEBUG-RETURN`. Then there are indentation-specific primitives -like `:INDENTATION-UPDATE`. These could/should become -`:EMACS-CHANNEL-SEND`s in the future (but that would probably finally -break Swank compatibility). - -### UTF-8 and TCP - -UTF-8 is relevant because the information in the wire are text-encoded -sexp's that sometimes carry strings with chunks of code, for example, -and these can have funky characters. - -TCP is well TCP, a host and a port and reliable transfer make SLY work -well over any IP network. - -### Common Lisp bias - -*Note: This section is very incomplete* - -SLY has is primarily a Common-Lisp IDE and the supporting Slynk have -strong Common-lisp bias. There have been many attempts, some quite -successful at creating Slynk backends for other languages. - -I believe that some of the Slyfuns will always be Common-Lisp specific -and should be marked as such. Others can perhaps be more naturally -adapted to other languages. - -It's very important that a future reference manual have this in -consideration: remove the CL bias from the protocol's description, at -least from some of its layers, so that things like -[swank-js](https://github.com/swank-js/swank-js) can one day be more -easily implemented. - - -## Architecture changes from SLIME to SLY - -As of time of writing (SLY 1.0, SLIME 2.9) the following list -summarizes the main architecture differences between SLY and SLIME. If -it's not mentioned here, it's a safe bet that some particular -mechanism you're interested in stayed the same and any SLIME -documentation is applicable to SLY. - -### Swank is now called Slynk - -SLY can be loaded alongside SLIME both in the same Emacs or Lisp -image. This interoperability meant that SLY's Lisp server had to be -renamed to "Slynk". - -SLY can still speak the Swank protocol, and should be able to connect -to any other non-Lisp backends such as Christopher Rhodes' [swankr][4] -or have non-SLIME clients connect to it such as Robert Brown's -[swank-client][5]. - -This is done via a contrib called `sly-retro` and its `slynk-retro` -counterpart. The contrib's code is loaded by `M-x sly` or `M-x -sly-connect` *on demand*, meaning that it is possible to start the -Slynk server without the contrib's Lisp counterpart. See the section -called "Slynk-loading method"" for how this works in SLY. - -*If* it is loaded, `sly-retro` ensures that messages leaving SLY still -look like - - (:emacs-rex (swank:connection-info) nil t 1) - -It also ensures that incoming messages are directed to the `SLYNK` and -`SLYNK-BACKEND` packages. This particular redirection is done via -package nicknames and a trick in `lib/lisp/slynk-rpc.lisp`. The trick -is necessary only for the first bootstrapping messages, because on -startup the `sly-retro` contrib hasn't kicked in and nicknames are not -immediately setup. - -The nicknames pose a compatibility hazard if the user tries to load -SLIME's Swank server into the Lisp image where Slynk is already -setup. Therefore, users wishing to run both servers alongside in the -same Lisp image must ensure that the `sly-retro` contrib is not in -`sly-contribs`. - - (setq sly-contribs (delq 'sly-retro sly-contribs)) - -[4]: https://github.com/gigamonkey/swankr -[5]: https://github.com/brown/swank-client - -### Slynk-loading method - -In SLIME, `M-x slime` immediately tells the Lisp process started by -Emacs to use SLIME's own `swank-loader.lisp` program to compile and -load all possibly available lisp under its directory (including -contrib's) before the Swank server is created with -`SWANK:CREATE-SERVER`. - -In SLY, the elisp variable `sly-init-function` is set to -`sly-init-using-asdf` by default, meaning that `M-x sly` will try to -load Slynk (the SLY equivalent to Swank) via `ASDF:LOAD-SYSTEM`. But -this will load only Slynk and no contribs. - -Slynk contribs are also represented as ASDF systems. Internally the -function `sly-contrib--load-slynk-dependencies` will ask Slynk to put -the contrib's path to the ASDF load path. The `SLYNK:REQUIRE-MODULE` -abstraction will then call `ASDF:LOAD-SYSTEM`. - -In SLY, a contrib's associated Slynk modules is loaded on demand, not -forced on the user's Lisp run-time. - -This also allows the developer to write completely independent -third-party extensions to SLY, with both Emacs and Lisp parts. See the -URL http://github.com/joaotavora/sly-hello-world for an example -extension. - -Additionally, if SLY detects that ASDF is not available in the Lisp -run-time, it will fallback to the old `slynk-loader.lisp` mechanism, -which has also been revised to support the previous two use cases. Any -of the two methods is transparent from Emacs's perspective. - -### mREPL - -`slime-mrepl` is an experimental SLIME contrib that inspired -`sly-mrepl`, which is a much enhanced version of it and the default -REPL for SLY. The main difference to the popular `slime-repl` contrib -is that `sly-mrepl` is based on Emacs's own `comint.el` so that that -SLY does not need to worry about functionality like history navigation -and persistent history, which are consistent with other Emacs modes -based on `comint.el`. - -`sly-mrepl` allows multiple REPLs through the use of channels, which -are abstraction pioneered in SLIME. Channels are like separate -mailboxes in the Lisp run-time, and it's slightly different from the -regular `:emacs-rex` RPC calls in that they directly invoke a remote -method but expect no reply. - -In `slynk-mrepl.lisp`, the `mrepl` class multiple inherits from -`slynk:channel` and `slynk:listener`. The first takes care of -channel-based communication and the second has the REPL-specific -context. - -See the section on the "RPC protocl" and switch to the `*sly-events*` -buffer to see what's going on. - -### Display-related code - -SLIME's age and historical compatibility with XEmacs means it -reinvented (and possibly invented) many buffer/window/display managing -techniques that are available today in GNU Emacs's core. Interactive -buttons, display-related and completion-code have all been pruned as -much as possible and now reuse Emacs' own libraries. - -Hopefully this will make SLY's code focus on SLY's "business logic" -and easier to read. - -### Channels - -TODO - -### Listeners - -TODO - - -## Pull requests - -* Read [how to properly contribute to open source projects on Github][1]. -* Use a topic branch to easily amend a pull request later, if necessary. -* Commit messages should use the syntax of GNU ChangeLog entries. -* Open a [pull request][2] that relates to *only* one subject with a - clear title and description in grammatically correct, complete - sentences. - -[1]: http://gun.io/blog/how-to-github-fork-branch-and-pull-request -[2]: https://help.github.com/articles/using-pull-requests -[3]: http://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs diff --git a/elpa/sly-1.0.43/Makefile b/elpa/sly-1.0.43/Makefile @@ -1,114 +0,0 @@ -### Makefile for SLY -# -# This file is in the public domain. - -# Variables -# -EMACS=emacs -LISP=sbcl - -LOAD_PATH=-L . -L contrib/ - -ELFILES := sly.el sly-autoloads.el $(wildcard lib/*.el) -ELCFILES := $(ELFILES:.el=.elc) - -CONTRIBS = $(patsubst contrib/sly-%.el,%,$(wildcard contrib/sly-*.el)) - -CONTRIB_ELFILES := $(wildcard contrib/*.el) -CONTRIB_ELCFILES := $(CONTRIB_ELFILES:.el=.elc) - -TEST_ELFILES := $(wildcard test/*.el) -TEST_ELCFILES := $(TEST_ELFILES:.el=.elc) - -all: compile compile-contrib - -# Compilation -# -sly.elc: sly.el lib/hyperspec.elc - -%.elc: %.el - $(EMACS) -Q $(LOAD_PATH) --batch -f batch-byte-compile $< - -compile: $(ELCFILES) -compile-contrib: $(CONTRIB_ELCFILES) -compile-test: $(TEST_ELCFILES) - -# Automated tests -# -check: check-core check-fancy - -check-core: SELECTOR=t -check-core: compile - $(EMACS) -Q --batch $(LOAD_PATH) \ - --eval "(require 'sly-tests \"lib/sly-tests\")" \ - --eval "(setq inferior-lisp-program \"$(LISP)\")" \ - --eval '(sly-batch-test (quote $(SELECTOR)))' - -check-%: CONTRIB_NAME=$(patsubst check-%,sly-%,$@) -check-%: SELECTOR=(tag contrib) -check-%: compile contrib/sly-%.elc test/sly-%-tests.elc - $(EMACS) -Q --batch $(LOAD_PATH) -L test \ - --eval "(require (quote sly))" \ - --eval "(setq sly-contribs (quote ($(CONTRIB_NAME))))" \ - --eval "(require \ - (intern \ - (format \ - \"%s-tests\" (quote $(CONTRIB_NAME)))))" \ - --eval "(setq inferior-lisp-program \"$(LISP)\")" \ - --eval '(sly-batch-test (quote $(SELECTOR)))' - -check-fancy: SELECTOR=(tag contrib) -check-fancy: compile compile-contrib - $(EMACS) -Q --batch $(LOAD_PATH) -L test \ - --eval "(require (quote sly))" \ - --eval "(sly-setup (quote (sly-fancy)))" \ - --eval "(mapc (lambda (sym) \ - (require \ - (intern (format \"%s-tests\" sym)) \ - nil t)) \ - (sly-contrib--all-dependencies \ - (quote sly-fancy)))" \ - --eval '(setq inferior-lisp-program "$(LISP)")' \ - --eval '(sly-batch-test (quote $(SELECTOR)))' - - -# Cleanup -# -FASLREGEX = .*\.\(fasl\|ufasl\|sse2f\|lx32fsl\|abcl\|fas\|lib\|trace\)$$ - -clean-fasls: - find . -regex '$(FASLREGEX)' -exec rm -v {} \; - [ -d ~/.sly/fasl ] && rm -rf ~/.sly/fasl || true - -clean: clean-fasls - find . -iname '*.elc' -exec rm {} \; - -# Doc -# -doc-%: - $(MAKE) -C doc $(@:doc-%=%) -doc: doc-help - -# Help -# -help: - @printf "\ -Main targets\n\ -all -- compile all .el files\n\ -compile -- compile just core SLY\n\ -compile-contrib -- compile just contribs\n\ -check -- run tests in batch mode\n\ -clean -- delete generated files\n\ -doc-help -- print help about doc targets\n\ -help-vars -- print info about variables\n\ -help -- print this message\n" - -help-vars: - @printf "\ -Main make variables:\n\ -EMACS -- program to start Emacs ($(EMACS))\n\ -LISP -- program to start Lisp ($(LISP))\n\ -SELECTOR -- selector for ERT tests ($(SELECTOR))\n" - -.PHONY: all clean compile compile-contrib check check-core \ - check-fancy dochelp help-vars diff --git a/elpa/sly-1.0.43/NEWS.md b/elpa/sly-1.0.43/NEWS.md @@ -1,633 +0,0 @@ -Upcoming version ----------------- - -SLY 1.0.42 (December 2020) ------------------------------- - -### Much improved company completion - -If you haven't yet, just `M-x package-install RET company-mode`, to -enable `company`. It should start working in every SLY buffer. - -Moreover, a new "lazy" way to SLY collect completions from the Lisp -backend means that user input is not blocked if this takes a long -time. The result is a much more responsive on-the-fly completion -experience. - -If you don't like company, you can still use the normal on-request TAB -completion, which also features an improved UI. - -### Fixed Helm conflicts - -Helm users should now get access to the full capabilities of SLY's -completion function without needing to install any extra packages. -Just need to `M-x sly-symbol-completion-mode` to turn off SLY's -default completion UI. (https://github.com/joaotavora/sly/issues/303) - -### Redesigned completion backend - -A redesigned completion backend replaces the legacy contribs -`sly-fuzzy` and `sly-c-p-c`. The new "flex" backend considers package -designators in the search string so you can complete other package's -symbols even when outside a package. - -Here's an example: - -``` -CL-USER> (quiloa) -> (ql:quickload) -CL-USER> (scan) -> (ppcre:scan) -CL-USER> (setf locadirs) -> (setf ql:*local-project-directories*) -CL-USER> (mvbind) -> (multiple-value-bind) -``` - -Flex completion is on by default, but that can be changed via -`sly-complete-symbol-function`. The -[documentation](http://joaotavora.github.io/sly/#Completion) has more -information. - -Package-local nicknames are also considered (provided your -implementation supports them). - -### Completely rewritten manual - -Manual has been reviewed from top to bottom. It should be much easier -to navigate and nicer read in general, thouch it *still needs quite -some proofreading* - -[A new chapter for existing SLIME users](http://joaotavora.github.io/sly/#A-SLY-tour-for-SLIME-users) -figures prominently in the top-level. - -Presently no major omissions (*except maybe for multiple inspectors*). -Reorganized nodes into a new structure not so focused on core vs -contribs. Deleted stale sections. REPL section heavily rewritten to -explain output types and backreferences. - -### New command M-x sly-import-symbol-at-point bound to C-c i by default - -This is a counterpart to the existing M-x sly-export-symbol-at-point, -bound to C-c e. It strips the symbol under point of its package -designator, and adds it under the :import-from subclause of the -current package's defpackage form. An improvement suggested and -implemented by Alexander Artemenko (github #147). - -### Improved SLY Stickers UI - -The UI for SLY Stickers, particularly for `M-x sly-stickers-replay` -has been cleanup up. Upon entering the replay mode, SLY may ask user -if she wants to delete the previous recordings, a commonly forgotten -but useful pre-step (an idea by Javier Olaechea, github #91). Also, -window management is less random. - -### Stale buffers are killed on connection close - -Inspector, debugger, and other buffers pertaining to a connection are -automatically killed when the connection is closed. The types of -buffers that are exempt from this is configurable in a new -`sly-keep-buffers-on-connection-close` defcustom, which lists -REPL buffers by default. - -### Autodoc function args available in minibuffer prompts - -In minibuffer prompts like the one of `M-x sly-inspect`, autodoc -information is available and displayed temporarily in the mode-line. - -### `C-c C-o` and `C-c M-o` bound in the REPL - -These clear recent output and the whole REPL, respectively. - -### Easy to parse copied calls to REPL - -The "Copy call to REPL" funcionality in the Trace Dialog and SLY-DB -buffers now understands that symbols and numbers can be printed -directly in the reconstructed function call, which eases reading. - -### The .swankrc and .swank.lisp are not loaded by default - -A problem encountered by Zach Beane. - -### Window management with M-. has been fixed - -When finding the definition of a generic function with M-., an *xref* -buffer pops up in a (possibly reused) window, showing code -locations. When selecting one of those locations, make sure to honor -the original intent of M-. of switching buffers in the original -window. Quit the *xref* window should fully restore the previous -window configuration. Thanks to Michael Fiano for insisting on what is -indubitably the correct behavior (github #123). - -### More predictably choose REPL windows - -Calling `sly-mrepl` interactively, either via `M-x` or - `sly-selector-map` switches to buffer in the current - window. Accessing the REPL indirectly (by returning objects into it - form other modes) attempts to pop the buffer in another window. - -A problem encoutered by Zach Beane. - -### Travis CI infrastructure has been revamped - -The Travis CI infrastructure now uses `roswell` and `emacs-travis` -instead of `cl-travis` and `apt`, respectively. Thus no longer needs -Travis's `sudo` and uses directory caching, resulting in much faster -builds. - -### New variables `*SLYNK:ECHO-NUMBER-ALIST*` and `*SLYNK:PRESENT-NUMBER-ALIST*` - -Customize the formats that integer numbers get presented back to SLY, -additionally to the binary, octal and hex defaults. Good when working -with Unix Epoch times, for example. See manual "Other configurables" -for documentation and example. - -### New customization variable `sly-command-switch-to-existing-lisp` - -This makes a prefixless `M-x sly` switch to an active connection -instead of starting a new one. Customize this to `never` to never -prompted, or to `always` to always move to the first live connection. - -### Loading contribs is a more robust process - -If the Slynk-side of a contrib fails to load for whatever -reason, a CONTINUE restart is provided. On the SLY side, the user -can choose to disable that contrib temporarily. - -### A lot of cleanup to SLY sources - -Courtesy of Zach Shaftel <zshaftel@gmail.com>, sly-cl-indent.el no -longer clobbers or conflicts with Emacs's own cl-indent.el. The cl.el -library is no longer required, not even at compile-time or in tests. -A lot of cleanup and performance improvements. - -### On par with SLIME 2.26 - -Where applicable, SLY tracks bug-fixes and improvements contributed to -SLIME: - -- More secure handling of ~/.sly-secret files - -- Compatibility with the latest SBCL and older SBCL. - -- ECL backend now supports threads - -- Function `create-server` now accepts optional `interface` argument. - -- In SBCL, slynk can be bound to IPv6 interface and can work on IPv6-only machines. - -- Clasp/ABCL improvements. - -SLY 1.0.0-beta-2 (March 2016) ------------------------------ - -### Improved `sly-stickers` contrib: - -There is now updated documentation for this contrib. See -http://joaotavora.github.io/sly/#SLY-Stickers - -Added a menu to help discover the functionality. - -Use `M-x sly-stickers-toggle-break-on-stickers` to turn on stepping -for forms. The debugger pops up with useful restarts and interactive -buttons. - -The "replay" interface, `M-x sly-stickers-replay` has been cleaned up. - -### Three new independent SLY contribs - -In https://github.com/joaotavora/sly-named-readtables there is an -external contrib that adds support for Tobias C Rittweiler's -`EDITOR.HINTS-NAMED.READTABLES`. Available on MELPA. - -In https://github.com/joaotavora/sly-macrostep there is a SLY -contrib for expanding CL macros right inside the source -file. Available on MELPA. - -In https://github.com/joaotavora/sly-quicklisp there is a SLY -contrib for expanding CL macros right inside the source -file. Available on MELPA. - -These contribs also showcase how third-party contribs with both Elisp -and CL parts can be written independently of SLY. See -https://github.com/joaotavora/sly-hello-world for how to write such -contribs. - -### Apropos - -Argument list information is present in apropos output like -this. A suggestion of Javier Olaechea (github #53) - -``` -... -SLYNK:TO-LINE - Function: Print OBJECT to a single line. Return the string. - Arglist: (OBJECT &OPTIONAL WIDTH) -... -``` - -`M-- sly-apropos` prompts for just the package. A suggestion of Javier -Olaechea (github #53). - -`C-u sly-apropos` allows searching all packages (github #53) - -### Macroexpansion - -Discovery of the "sexp near point" has been much improved and won't -error cryptically when there is no macroexpansion possible. - -### REPL enhancements - -`paredit-mode` works in the REPL, as does Emacs 24.4's -`electric-pair-mode` or other parenthesis-matching tools, just like in -any other Lisp buffer. - -New variable `sly-mrepl-output-filter-functions` for REPL -output. These work like `comint-preoutput-filter-functions`. Functions -like `ansi-color-apply` are good candidates (github #38). - -When using multiple REPL, frame variables from SLY-DB triggered in -secondary REPLs, when returned with M-RET, appear in the correct REPL. - -mREPL notes are synched displayed correctly at the "process mark", not -"output mark". This avoids some `; note` in wrong -places (github #45). - -Popping up SLY-DB buffers from different threads no longer -misteriously updates a REPL's environment for `*`, `**`, `***`, etc... - -Tearing down a reverse-isearch with `C-g` no longer errors (github -\#39). - -### Manual - -The "Tips and tricks" section was rewritten. - -Keymap documentation was corrected and enhanced by Javier Olaechea -(github #36). - -### Other - -The thread-list buffer can now be properly quit. Reported by Javier -Olaechea (github #51). - -Let user ignore protocol mismatches per connection This issue popped -up in SLIME, where it is still unsolved. See discussion in -https://github.com/slime/slime/issues/250 - -`view-mode` bindings no longer creep into SLY's popup -buffers. By Paul M. Rodriguez. - -`sly-inspect-fetch-all` now actually does something (github #49). - -`sly-expand-1` and friends error less often and less cryptically when -no nearby sexp's can be found. The region about to be macroexpanded is -flashed for visual feedback. - -### On par with SLIME 2.14 and 2.15 - -Where applicable, SLY tracks bugfixes and improvements contributed to -SLIME: - -- Rationals are displayed in the echo area as floats too - -- The sly-c-p-c contrib now takes a better guess at symbol case - (issue https://github.com/slime/slime/issues/233) - -- SBCL backend now able to jump to ir1-translators, declaims and alien types - -- Various updates supporting SBCL 1.2.12 - -- ABCL backend fixed inspection of frame-locals in the debugger - -- Following a suggestion by Attile Lendvai to SLIME, mouse-1 is now -activates SLY's "part buttons". - -- SBCL's M-. can locate forms within PROGN/MACROLET/etc. Needs SBCL 1.2.15 - -### On par with upcoming SLIME 2.16 - -- SBCL and ABCL support completion of package-local nicknames - -- Robustify the indentation cache request handler - (issue https://github.com/slime/slime/issues/280) - - -SLY 1.0.0-beta (March 2015) ----------------------------------------- - -### mREPL enhancements - -In the REPL, a reader macro allows easy backreferences to previously -returned values. `#v3` will access the first value of the fourth -values-form ever returned and `#v3:2` will access the third of those -values. Backreferences are automatically highlighted. - -New customization option `sly-mrepl-eli-like-history-navigation` makes -history navigation via `C-r`, `M-p` and `M-n` keep the current input -and add the history input at point. - -### New commands `sly-next-connection` and `sly-prev-connection` - -Replace deprecated `sly-cycle-connections` and provide more feedback. - -### On par with SLIME 2.13 - -Where applicable, SLY tracks bugfixes and improvements contributed to -SLIME: - -- Experimental CLASP support (see `slynk/backend/clasp.lisp`). - -- Fix a source-path-parsing bug. A problem raised and solved by Gabor - Mélis in http://permalink.gmane.org/gmane.lisp.slime.devel/11543. - -- Hot updating via MELPA on won't cause SLYNK/SLY version mismatch - - SLIME issue [125](https://github.com/slime/slime/issues/125) - -- Allegro's "modern mode" is supported again. - -- Port conflicts can be resolved interactively - - SLIME issue [204](https://github.com/slime/slime/issues/204) - -- New `SLYNK-FUZZY:*FUZZY-DUPLICATE-SYMBOL-FILTER*` option. - - SLIME issue [215](https://github.com/slime/slime/issues/215) - -- `sly-recompile-xrefs` has been fixed. - -- More minor issues. - -SLY 1.0.0-alpha-3 (December 2014) ---------------------------------- - -### sly-stickers: live source-code annotation tool - -`sly-stickers` is a live code annotation tool, a replacement for the -"print" statements that are often included for debugging the value of -a particular variable or expression. See a much more complete -description in http://joaotavora.github.io/sly/#SLY-Stickers. - -Thanks to Artem Malyshev for early testing and ideas. - -### Documentation rewrite - -The documentation rewrite is underway (github issue #9), mentioning -only a residual amount of SLIME-specific functionality. Browse to -http://joaotavora.github.io/sly for the online version in HTML. - -### SLY is available in MELPA - -The README.md file has an updated install recipe, basically `M-x -package-install RET sly RET`. Thanks to Kan-Ru Chen for the idea. - -### mREPL enhancements - -The "comma" shortcut for "sayoonara", "restart lisp", "in-package" -commands has been restored and uses ido-style completion by -default. Suggested by Javier Olaechea (github #18). - -`C-c C-z` switches to the nearest REPL, much like `sly-mrepl-sync`, -but without changing the REPL's state. Suggested by Javier Olaechea -(github #13). - -`sly-mrepl-sync` is now bound to `C-c ~` in any SLY buffer. A `C-u` -prefix will also yank the last expression into the current REPL. - -New customization variable `sly-mrepl-prevent-duplicate-history` -offers finer control over saved history. - -When a connection disconnects, a status line is inserted in the REPL -stating the reason for disconnection (network failure, user abort, -etc...) - -### Other enhancements - -SLY asks the user to confirm the Lisp to kill with `M-x sly-quit` or -disconnect with `M-x sly-disconnect`. It also doesn't ask any -irrelevant questions about switching to a default connection when it -doesn't exist (github #5). - -### Notable bugfixes - -* Closed github #26: `sly-mrepl` history saved when killing - Emacs. Reported by Javier Olaechea. - -* Closed github #24: Common-lisp-style indentation now works in - `sly-mrepl`. Reported by Kan-Ru Chen. - -* Closed github #22: Compilation with `C-u` debug info now - working. Reported by Javier Olaechea. - -* Closed github #21: Enable incremental output with dedicated output - streams. Reported by Paul M. Rodriguez. - -* Closed github #5: `sly-quit` no longer asks irrelevant questions - -### Swank is now called Slynk - -Slynk is SLY's implementation of Swank, the supporting Lisp server -where Emacs connects to. A project-wide rename was performed, -including this NEWS.md file. - -A `sly-retro` contrib, enabled by default ensures that: - -* SLY can still talk to non-lisp Swank backends -* SLY can serve requests for the Swank protocol -* Most user customization in `~/.swankrc` is still valid in SLY. - -For details on the architecture of these changes, see the "Swank is -now called Slynk" in the CONTRIBUTING.md. - -Thanks to Zach Beane for the great name. - -### On par with SLIME 2.10.1 - -Where applicable, SLY tracks bugfixes and improvements contributed to -SLIME: - -- `sly-autodoc` has been rewritten by Helmut Eller. Multiline is - enabled by default. -- Experimental support for MKCL has been added - - -SLY 1.0.0-alpha (September 2014) --------------------------------- - -Since this is the first pre-release of SLY since the fork, this entry -focuses directly on the differences to SLIME. - -### Completely redesigned REPL - -The `sly-mrepl` contrib is a extensively redesigned -Read-Eval-Print-Loop (REPL) for SLY. - -Multiple independent REPLs can be created with the `sly-mrepl-new` -command. - -`sly-mrepl` is fully based on Emacs's `comint.el` and as such has a -more familiar interface for history navigation. `C-r` and `C-M-r`, -when used at the prompt, should provide a bash/zsh-like experience. - -The prompt gives a visual indication of long-running evaluations that -haven't returned yet. - -The prompt gives a visual indication of the number of debugger levels -currently caused by the last evaluation. - -Every return part can be inspected and re-returned as the last value. - -`C-c ~` on any Lisp file switches to the REPL and synchronized its -`*PACKAGE*` and `*DEFAULT-PATHNAME-DEFAULTS*` with the file's. - -Output redirection is automatically setup. The first REPL created is -the target for all threads' output onto the standard output -streams. REPLs created afterward only see their own output. To turn -this off configure the Slynk-side variable -`SLYNK-MREPL:*GLOBALLY-REDIRECT-IO*`. - -A dedicated stream connection for output is automatically set -up. Configure the `SLYNK-MREPL:*USE-DEDICATED-OUTPUT-STREAM*` if it -doesn't suit you. - -There is saner scrolling behavior as provided by the `comint.el` -substrate. The variables `comint-scroll-show-maximum-output`, -`comint-scroll-to-bottom-on-input` and -`comint-scroll-to-bottom-on-output` (which see) are set to `nil` by -default, but the user might reconfigure them to her liking in the -`sly-mrepl-hook`. - -There are Sylvesters. See `sly-mrepl-pop-sylvester`. - -### Regexp-capable M-x sly-apropos - -If SLY detects that [`cl-ppcre`](http://weitz.de/cl-ppcre/) is -available in the Lisp side it will try to use it for "apropos" -searches, otherwise the user is hinted at this possibility. As regexp -searches are slower, this is only triggered if the pattern is a valid -regular-expression. - -This is the default implementation of the new -`SLYNK-BACKEND:MAKE-APROPOS-MATCHER` interface that particular -implementations may wish to override. - -The search pattern, whether regexp-enabled or not, is now also applied -to the package qualifier. - -### Contribs enabled by default - -By default, SLY enables the `sly-fancy` meta-contrib. This contains -`sly-mrepl`, `sly-autodoc`, `sly-fancy-inspector`, `sly-fancy-trace`, -`sly-fuzzy`, `sly-scratch`, `sly-package-fu`, `sly-fontifying-fu`, -`sly-trace-dialog`, `sly-indentation` and `sly-tramp`. - -### SLY uses ASDF and loads contribs on demand. - -If the user sets `sly-contribs` to `sly-mrepl` she can be sure that no -Lisp code related to other contribs appears in your run-time. Even if -ASDF is unavailable, an improved version of the `slynk-loader.lisp` -program will behave in a similar non-intrusive manner. - -This change also enables developers to write completely independent -third-party extensions like -[in this example](http://github.com/joaotavora/sly-hello-world). - -See the CONTRIBUTING.md file for more details on architecture changes. - -### More consistent interface - -The SLY-DB, Inspector, XREF and Apropos buffers have been -redesigned to use a common class of push button with consistent -interfaces. These context-aware commands are known as "part actions" - -For instance, the `i`,`p` and `M-RET` commands (`sly-button-inspect`, -`sly-button-pretty-print` and `sly-mrepl-copy-to-repl`) are available -for every interactive part, regardless of the SLY buffer in -question. A command such as `v` (`sly-button-show-source`) is only -available where it makes sense for the represented object. - -The same interfaces are also available in the "mREPL" and "Trace -Dialog" buffers. - -`sly-mode` is now activated in every buffer related to SLY is now, -meaning global keybindings like `C-c T` and `C-c I` work everywhere. - -### Multiple inspectors - -Interactive commands for inspecting Lisp objects can be prefixed with -`C-u` to prompt the user for an inspector name. Separate inspector -streams are kept. An idea by Olof-Joachim Frahm -(http://macrolet.net/). - -### Copy function call to REPL - -An experimental feature: from the Trace Dialog or SLY-DB buffers, a -new button action called "Copy call to REPL" is offered and bound to - -If SLY can calculate the arguments and the function symbol of the -function call represented in the backtrace of trace entry, it will -return them to the REPL, along with an uncommitted input line that -refers back to them and calls the function. - -### Other miscellaneous enhancements over SLIME - -Faces have been revised and are based on Emacs's standard -faces. Hopefully, SLY will remain readable even when the user -switches themes. - -Popping up windows and buffers has been much improved. Windows are -reused if the buffer names match or are similar, meaning that no -longer will the SLY-DB "jump around" in multi-window configurations when -selecting a restart that signals another error. - -Interactive expression evaluation will use a separate buffer when the -results is too big to fit in the echo area. - -SLY's mode-line string is placed at the right side of the mode-line. - -SLY intrudes less than SLIME in the Emacs name-space, and uses more -standard idioms. Macros like `with-struct` and `sly-define-keys` have -been removed. - -Buffer names have been consolidated: every buffer name related to SLY -obeys the same structure, stating the type, connection name and any -pertinent additional info. - -Reading from the minibuffer has been improved. SLY uses `ido` -completion by default, but it can customized via -`sly-complete-symbol-function`. - -Messages and warnings prefix themselves accordingly with "[sly]". - -Fixed source locations when recompiling from an xref buffer. This is -outstanding https://github.com/slime/slime/pull/175 pull-request in -SLIME. Thanks Bart Botta. - -Fixed bugs in `contrib/sly-package-fu.el`. This is the outstanding -https://github.com/slime/slime/pull/145 pull-request in SLIME. Thanks -Leo Liu. - -### On par with SLIME 2.9 - -Where applicable, SLY tracks bugfixes and improvements contributed to -SLIME. - -### Anti-NEWS (Things removed from SLIME) - -SLY 1.0-Alpha supports Emacs 24.3 only. SLY 1.0 is expected to only -support Emacs 24.4. - -There is very limited backward compatibility SLIME and only in the -SLIME->SLY direction, meaning a contrib-less SLIME may connect to a -Slynk server started by SLY, but any other combination will probably -fail beyond very basic functionality. - -The portable profiling commands have been removed from the SLY menu -`metering.lisp` and the profiling interfaces in `slynk-backend.lisp` -have been kept. SLY 1.0.0 will hopefully have a better integrated -portable profiler. - -The `slime-presentations` has been removed. The consistent button -interface is better. - -The `slime-c-p-c` contrib has been removed, as it contained a lot of -non-standard window-managing code. Its functionality has been merged -into `sly-fuzzy` and setting `sly-complete-symbol-function` to -`sly-c-p-c-complete-symbol` should give you the previous behavior. diff --git a/elpa/sly-1.0.43/PROBLEMS.md b/elpa/sly-1.0.43/PROBLEMS.md @@ -1,83 +0,0 @@ -Known problems with SLY ------------------------ - -## Common to all backends - -### Caution: network security - -The `M-x sly` command has Lisp listen on a TCP socket and wait for -Emacs to connect, which typically takes on the order of one second. If -someone else were to connect to this socket then they could use the -SLY protocol to control the Lisp process. - -The listen socket is bound on the loopback interface in all Lisps that -support this. This way remote hosts are unable to connect. - -### READ-CHAR-NO-HANG is broken - -`READ-CHAR-NO-HANG` doesn't work properly for sly-input-streams. Due -to the way we request input from Emacs it's not possible to repeatedly -poll for input. To get any input you have to call `READ-CHAR` (or a -function which calls `READ-CHAR`). - -## Backend-specific problems - -### CMUCL - -The default communication style `:SIGIO` is reportedly unreliable with -certain libraries (like libSDL) and certain platforms (like Solaris on -Sparc). It generally works very well on x86 so it remains the default. - -### SBCL - -The latest released version of SBCL at the time of packaging should -work. Older or newer SBCLs may or may not work. Do not use -multithreading with unpatched 2.4 Linux kernels. There are also -problems with kernel versions 2.6.5 - 2.6.10. - -The (v)iew-source command can only locate exact source forms for code -compiled at (debug 2) or higher. The default level is lower and SBCL -itself is compiled at a lower setting. Thus only defun-granularity is -available with default policies. - -### LispWorks - -On Windows, SLY hangs when calling foreign functions or certain -other functions. The reason for this problem is unknown. - -We only support latin1 encoding. (Unicode wouldn't be hard to add.) - -### Allegro CL - -Interrupting Allegro with C-c C-b can be slow. This is caused by the -a relatively large process-quantum: 2 seconds by default. Allegro -responds much faster if `MP:*DEFAULT-PROCESS-QUANTUM*` is set to 0.1. - -### CLISP - -We require version 2.49 or higher. We also require socket support, so -you may have to start CLISP with `clisp -K full`. - -Under Windows, interrupting (with C-c C-b) doesn't work. Emacs sends -a SIGINT signal, but the signal is either ignored or CLISP exits -immediately. - -On Windows, CLISP may refuse to parse filenames like -"C:\\DOCUME~1\\johndoe\\LOCALS~1\\Temp\\sly.1424" when we actually -mean C:\Documents and Settings\johndoe\Local Settings\sly.1424. As -a workaround, you could set sly-to-lisp-filename-function to some -function that returns a string that is accepted by CLISP. - -Function arguments and local variables aren't displayed properly in -the backtrace. Changes to CLISP's C code are needed to fix this -problem. Interpreted code is usually easer to debug. - -`M-.` (find-definition) only works if the fasl file is in the same -directory as the source file. - -The arglist doesn't include the proper names only "fake symbols" like -`arg1`. - -### Armed Bear Common Lisp - -The ABCL support is still new and experimental. diff --git a/elpa/sly-1.0.43/README.md b/elpa/sly-1.0.43/README.md @@ -1,196 +0,0 @@ -[![Build Status](https://travis-ci.org/joaotavora/sly.png?branch=master)](https://travis-ci.org/joaotavora/sly) -[![MELPA](http://melpa.org/packages/sly-badge.svg)](http://melpa.org/#/sly) - -```lisp - _____ __ __ __ - / ___/ / / \ \/ / |\ _,,,---,,_ - \__ \ / / \ / /,`.-'`' -. ;-;;,_ - ___/ / / /___ / / |,4- ) )-,_..;\ ( `'-' - /____/ /_____/ /_/ '---''(_/--' `-'\_) - -``` - -SLY is Sylvester the Cat's Common Lisp IDE for Emacs: - -* 🤔 Read [a short illustrated guide][tutorial] -* 📽️ Scroll down this README for some [pretty gifs](#animated_gifs) -* đź“Ł Read the [NEWS][6] file -* đź“š Read the [manual][documentation] - -SLY's highlights are: - -* A [full-featured REPL](#repl) based on Emacs's `comint.el`. Everything can be - copied to the REPL; -* [Stickers](#stickers), or live code annotations that record values as code - traverses them. -* [Flex-style completion](#company-flex-completion) out-of-the-box, using - Emacs's completion API. Company, Helm, and other [supported - natively](#completion), no plugin required; -* An interactive [Trace Dialog][trace-dialog]; -* Cleanly ASDF-loaded by default, including contribs, enabled out-of-the-box; -* Multiple inspectors and multiple REPLs; -* "Presentations" replaced by [interactive backreferences](#repl) which - highlight the object and remain stable throughout the REPL session; -* Support for [NAMED-READTABLES][11], [macrostep.el][12] and [quicklisp][13] -* A [portable, annotation-based stepper][16] in [early][17] but functional - prototype stage. - -SLY is a fork of [SLIME][1]. We tracks its bugfixes, particularly to the -implementation backends. All SLIME's familar features (debugger, inspector, -xref, etc...) are still available, with improved overall UX. - -Installation ------------- - -Ensure that [MELPA][10] is setup as usual and ask `M-x package-install` to -install the package `sly`. - -*That's it*. `sly-mode` will automatically come up in every `.lisp` file. To -fire up SLY, connect to a Lisp and get a friendly REPL, use `M-x sly`. - -Even if you already have SLIME installed, SLY will ask you and temporarily -disable it for the Emacs session. - -<a name="animated_gifs"></a> -_Obligatory animated gif section_ ------------------------------------ - -<a name="company-flex-completion"></a> -[Flex completion](http://joaotavora.github.io/sly/#Completion) - -![company-flex-completion](./doc/animations/company-flex-completion.gif) - -<a name="repl"></a> -[Backreferences](http://joaotavora.github.io/sly/#REPL-backreferences) - -![backreferences](./doc/animations/backreferences.gif) - -[Reverse i-search](http://joaotavora.github.io/sly/#REPL-commands) - -![reverse-isearch](./doc/animations/reverse-isearch.gif) - -<a name="stickers"></a> -[Stickers](http://joaotavora.github.io/sly/#Stickers) - -![stickers-example](./doc/animations/stickers-example.gif) - -Install from git -------------------- - -Clone this repository, add this to your `~/.emacs` file and fill in the -appropriate file names: - -```el -(add-to-list 'load-path "~/dir/to/cloned/sly") -(require 'sly-autoloads) -(setq inferior-lisp-program "/opt/sbcl/bin/sbcl") -``` - -If you wish to byte-compile SLY yourself (not needed generally) you can do `make -compile compile-contrib` in the dir where you cloned SLY. - -Running the server standalone ------------------------------ - -This also works -``` -$ sbcl -... -* (push #p"~/dir/to/sly" asdf:*central-registry*) -* (asdf:load-system :slynk) -* (slynk:create-server :port 4008) -``` - -Now in Emacs you can do `sly-connect` and give it the host and the 4008 port as -a destination. - -Faster startup --------------- - -If the Lisp program doesn't start fast enough for you, look in [the -manual][instasly], for ways to make it faster. - -Additional Contribs -------------------- - -* https://github.com/joaotavora/sly-quicklisp -* https://github.com/joaotavora/sly-named-readtables -* https://github.com/joaotavora/sly-macrostep -* https://github.com/joaotavora/sly-stepper -* https://github.com/mmgeorge/sly-asdf -* https://github.com/40ants/sly-package-inferred - -<a name="completion"></a> -Completion UIs --------------- - -SLY works with most Emacs "completion UIs" out of the box, providing completion -in source files and inputting Common Lisp symbol names from the minibuffer. -[Company][14], Emacs 27's Fido-mode, and Helm are well-supported, as is -"vanilla" completion. For consistency, SLY defaults to its own UI, -`sly-symbol-completion-mode`, useful if you don't have or like any of those. -You can turn it off. Also, if you use Helm and wish to have even more -Helm-based fanciness, you can use [helm-sly][15]. - -License -------- - -SLY is free software. All files, unless explicitly stated otherwise, are public -domain. ASCII artwork is copyright by Felix Lee, Joan G. Stark and Hayley Jane -Wakenshaw. - -Fork ----- - -SLIME is the work of Eric Marsden, Luke Gorrie, Helmut Eller, Tobias -C. Rittweiler and [many others][8]. I forked SLIME because I used it daily, -for work, had a long list of hacks developed for myself, and wanted to share -them with others. - -In 2013, SLIME development was stalling, patches and issues rotting. In early -2014, LuĂ­s Oliveira and myself moved SLIME to Github and set up its Travis CI -system. I brought in the old bug reports from the Launchpad tracker, fixed -long-standing problems and submitted many changes, particularly to the -under-curated but popular "contrib" section. - -Now, the changes that SLY brings to the table are too deep at the Elisp and Lisp -level to be accepted to SLIME, given its current focus on stability (for the -record, I find this perfectly reasonable). The new features, such as stickers or -multiple inspectors, cannot be realized well using only the existing "contrib" -system. Finally, SLY frees itself from the shackles of Emacs 23 and supports -Emacs 24.3+ only, allowing for much cleaner code and liberal use of lexical -binding. - -The list of technical reasons is bigger than this though, and you can read up on -them in the [CONTRIBUTING.md][9] file. - -Contributing ------------- - -[Open an issue or a pull request][4], but at least have a quick look at the -first part [CONTRIBUTING.md][5] file for instructions on how to contribute. - -[1]: http://www.common-lisp.net/project/slime/ -[2]: https://github.com/joaotavora/sly/blob/master/README.md#fork -[4]: https://github.com/joaotavora/sly/issues -[5]: https://github.com/joaotavora/sly/blob/master/CONTRIBUTING.md -[6]: https://github.com/joaotavora/sly/blob/master/NEWS.md -[7]: https://www.youtube.com/watch?v=xqWkVvubnSI -[8]: http://common-lisp.net/project/slime/doc/html/Credits.html#Credits -[9]: https://github.com/joaotavora/sly/blob/master/CONTRIBUTING.md#architecture -[10]: https://github.com/milkypostman/melpa -[11]: https://github.com/joaotavora/sly-named-readtables -[12]: https://github.com/joaotavora/sly-macrostep -[13]: https://github.com/joaotavora/sly-quicklisp -[14]: https://github.com/company-mode/company-mode -[15]: https://github.com/emacs-helm/helm-sly -[16]: https://zenodo.org/record/3742759 -[17]: https://github.com/joaotavora/sly-stepper -[documentation]: http://joaotavora.github.io/sly -[instasly]: http://joaotavora.github.io/sly/#Loading-Slynk-faster -[trace-dialog]: http://joaotavora.github.io/sly/#Trace-Dialog -[tutorial]: http://joaotavora.github.io/sly/#A-SLY-tour-for-SLIME-users - -<!-- Local Variables: --> -<!-- fill-column: 80 --> -<!-- End: --> diff --git a/elpa/sly-1.0.43/contrib/sly-mrepl.el b/elpa/sly-1.0.43/contrib/sly-mrepl.el @@ -1,1510 +0,0 @@ -;; -*- lexical-binding: t -*- An experimental implementation of -;; multiple REPLs multiplexed over a single Slime socket. M-x -;; sly-mrepl or M-x sly-mrepl-new create new REPL buffers. -;; -(require 'sly) -(require 'sly-autodoc) -(require 'cl-lib) -(require 'comint) - -(define-sly-contrib sly-mrepl - "Multiple REPLs." - (:license "GPL") - (:sly-dependencies sly-autodoc) - (:slynk-dependencies slynk/mrepl) - (:on-load - ;; Define a new "part action" for the `sly-part' buttons and change - ;; the `sly-inspector-part', `sly-db-local-variable' and - ;; `sly-trace-dialog-part' to include it. - ;; - (sly-button-define-part-action sly-mrepl-copy-part-to-repl - "Copy to REPL" (kbd "M-RET")) - (sly-button-define-part-action sly-mrepl-copy-call-to-repl - "Copy call to REPL" (kbd "M-S-<return>")) - (button-type-put 'sly-inspector-part - 'sly-mrepl-copy-part-to-repl - 'sly-inspector-copy-part-to-repl) - (button-type-put 'sly-db-local-variable - 'sly-mrepl-copy-part-to-repl - 'sly-db-copy-part-to-repl) - (button-type-put 'sly-apropos-symbol - 'sly-mrepl-copy-part-to-repl - 'sly-apropos-copy-symbol-to-repl) - (button-type-put 'sly-db-frame - 'sly-mrepl-copy-call-to-repl - 'sly-db-copy-call-to-repl) - (eval-after-load "sly-trace-dialog" - `(progn - (button-type-put 'sly-trace-dialog-part - 'sly-mrepl-copy-part-to-repl - 'sly-trace-dialog-copy-part-to-repl) - (button-type-put 'sly-trace-dialog-spec - 'sly-mrepl-copy-call-to-repl - 'sly-trace-dialog-copy-call-to-repl))) - ;; Make C-c ~ bring popup REPL - ;; - (define-key sly-mode-map (kbd "C-c ~") 'sly-mrepl-sync) - (define-key sly-mode-map (kbd "C-c C-z") 'sly-mrepl) - (define-key sly-selector-map (kbd "~") 'sly-mrepl-sync) - (define-key sly-selector-map (kbd "r") 'sly-mrepl) - - ;; Insinuate ourselves in hooks - ;; - (add-hook 'sly-connected-hook 'sly-mrepl-on-connection) - (add-hook 'sly-net-process-close-hooks 'sly-mrepl--teardown-repls) - ;; The connection list is also tweaked - ;; - (setq sly-connection-list-button-action - #'(lambda (process) - (let ((sly-default-connection process)) - (sly-mrepl 'pop-to-buffer))))) - (:on-unload - ;; FIXME: This `:on-unload' is grossly incomplete - ;; - (remove-hook 'sly-connected-hook 'sly-mrepl-on-connection) - (remove-hook 'sly-net-process-close-hooks 'sly-mrepl--teardown-repls))) - - -;; User-visible variables -;; -(defvar sly-mrepl-mode-hook nil - "Functions run after `sly-mrepl-mode' is set up") - -(defvar sly-mrepl-hook nil - "Functions run after `sly-mrepl-new' sets up a REPL.") - -(defvar sly-mrepl-runonce-hook nil - "Functions run once after `sly-mrepl-new' sets up a REPL. - -After running the contents of this hook its default value is -emptied. See also `sly-mrepl-hook'") - -(defvar sly-mrepl-output-filter-functions comint-preoutput-filter-functions - "List of functions filtering Slynk's REPL output. -This variables behaves like `comint-preoutput-filter-functions', -for output printed to the REPL (not for evaluation results)") - -(defvar sly-mrepl-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") 'sly-mrepl-return) - (define-key map (kbd "TAB") 'sly-mrepl-indent-and-complete-symbol) - (define-key map (kbd "C-c C-b") 'sly-interrupt) - (define-key map (kbd "C-c C-c") 'sly-interrupt) - (define-key map (kbd "C-c C-o") 'sly-mrepl-clear-recent-output) - (define-key map (kbd "C-c M-o") 'sly-mrepl-clear-repl) - (define-key map (kbd "M-p") 'sly-mrepl-previous-input-or-button) - (define-key map (kbd "M-n") 'sly-mrepl-next-input-or-button) - (define-key map (kbd "C-M-p") 'sly-button-backward) - (define-key map (kbd "C-M-n") 'sly-button-forward) - map)) - -(defvar sly-mrepl-pop-sylvester 'on-connection) - -(defface sly-mrepl-prompt-face - `((t (:inherit font-lock-builtin-face))) - "Face for the regular MREPL prompt." - :group 'sly-mode-faces) - -(defface sly-mrepl-note-face - `((t (:inherit font-lock-keyword-face))) - "Face for the MREPL notes." - :group 'sly-mode-faces) - -(defface sly-mrepl-output-face - '((((class color) - (background dark)) - (:foreground "VioletRed1")) - (((class color) - (background light)) - (:foreground "steel blue")) - (t - (:bold t :italic t))) - "Face for the regular MREPL prompt." - :group 'sly-mode-faces) - - -;; Internal variables -;; -(defvar sly-mrepl--remote-channel nil) -(defvar sly-mrepl--local-channel nil) -(defvar sly-mrepl--read-mark nil) -(defvar sly-mrepl--output-mark nil) -(defvar sly-mrepl--dedicated-stream nil) -(defvar sly-mrepl--last-prompt-overlay nil) -(defvar sly-mrepl--pending-output nil - "Output that can't be inserted right now.") -(defvar sly-mrepl--dedicated-stream-hooks) -(defvar sly-mrepl--history-separator "####\n") -(defvar sly-mrepl--dirty-history nil) - - -;; Major mode -;; -(define-derived-mode sly-mrepl-mode comint-mode "mrepl" - (sly-mode 1) - (cl-loop for (var value) - in `((comint-use-prompt-regexp nil) - (comint-inhibit-carriage-motion t) - (comint-input-sender sly-mrepl--input-sender) - (comint-output-filter-functions nil) - (comint-input-filter-functions nil) - (comint-history-isearch dwim) - (comint-input-ignoredups t) - (comint-prompt-read-only t) - (comint-process-echoes nil) - (indent-line-function lisp-indent-line) - (sly-mrepl--read-mark nil) - (sly-mrepl--pending-output nil) - (sly-mrepl--output-mark ,(point-marker)) - (sly-mrepl--last-prompt-overlay ,(make-overlay 0 0 nil nil)) - (sly-find-buffer-package-function sly-mrepl-guess-package) - (sly-autodoc-inhibit-autodoc - sly-mrepl-inside-string-or-comment-p) - (mode-line-process nil) - (parse-sexp-ignore-comments t) - (syntax-propertize-function sly-mrepl--syntax-propertize) - (forward-sexp-function sly-mrepl--forward-sexp) - (comint-scroll-show-maximum-output nil) - (comint-scroll-to-bottom-on-input nil) - (comint-scroll-to-bottom-on-output nil) - (inhibit-field-text-motion nil) - (lisp-indent-function sly-common-lisp-indent-function) - (open-paren-in-column-0-is-defun-start nil) - (buffer-file-coding-system utf-8-unix) - ;; Paredit workaround (see - ;; https://github.com/joaotavora/sly/issues/110) - (paredit-override-check-parens-function (lambda (_c) t)) - (comment-start ";")) - do (set (make-local-variable var) value)) - (set-marker-insertion-type sly-mrepl--output-mark nil) - (add-hook 'kill-emacs-hook 'sly-mrepl--save-all-histories) - ;;(set (make-local-variable 'comint-get-old-input) 'ielm-get-old-input) - (set-syntax-table lisp-mode-syntax-table) - (set-keymap-parent sly-mrepl-mode-map nil) - - ;; The REPL buffer has interactive text buttons - (sly-interactive-buttons-mode 1) - - ;; Add hooks to isearch-mode placed strategically after the ones - ;; set by comint.el itself. - ;; - (add-hook 'isearch-mode-hook 'sly-mrepl--setup-comint-isearch t t) - (add-hook 'isearch-mode-end-hook 'sly-mrepl--teardown-comint-isearch t t) - - ;; Add a post-command-handler - ;; - (add-hook 'post-command-hook 'sly-mrepl--highlight-backreferences-maybe t t)) - - -;;; Channel methods -(sly-define-channel-type listener) - -(sly-define-channel-method listener :write-values (results) - (with-current-buffer (sly-channel-get self 'buffer) - (sly-mrepl--insert-results results))) - -(sly-define-channel-method listener :evaluation-aborted (&optional condition) - (with-current-buffer (sly-channel-get self 'buffer) - (sly-mrepl--catch-up) - (sly-mrepl--insert-note (format "Evaluation aborted on %s" condition)))) - -(sly-define-channel-method listener :write-string (string) - (with-current-buffer (sly-channel-get self 'buffer) - (sly-mrepl--insert-output string))) - -(sly-define-channel-method listener :set-read-mode (mode) - (with-current-buffer (sly-channel-get self 'buffer) - (cl-macrolet ((assert-soft - (what) `(unless ,what - (sly-warning - ,(format "Expectation failed: %s" what))))) - (let ((inhibit-read-only t)) - (cl-ecase mode - (:read - (assert-soft (null sly-mrepl--read-mark)) - ;; Give a chance for output to come in before we block it - ;; during the read. - (sly-mrepl--accept-process-output) - (setq sly-mrepl--read-mark (point)) - (add-text-properties (1- (point)) (point) - `(rear-nonsticky t)) - (sly-message "REPL now waiting for input to read")) - (:finished-reading - (assert-soft (integer-or-marker-p sly-mrepl--read-mark)) - (when sly-mrepl--read-mark - (add-text-properties (1- sly-mrepl--read-mark) (point) - `(face bold read-only t))) - (setq sly-mrepl--read-mark nil) - (sly-message "REPL back to normal evaluation mode"))))))) - -(sly-define-channel-method listener :prompt (package prompt - error-level - &optional condition) - (with-current-buffer (sly-channel-get self 'buffer) - (sly-mrepl--insert-prompt package prompt error-level condition))) - -(sly-define-channel-method listener :open-dedicated-output-stream - (port _coding-system) - (with-current-buffer (sly-channel-get self 'buffer) - ;; HACK: no coding system - (set (make-local-variable 'sly-mrepl--dedicated-stream) - (sly-mrepl--open-dedicated-stream self port nil)))) - -(sly-define-channel-method listener :clear-repl-history () - (with-current-buffer (sly-channel-get self 'buffer) - (let ((inhibit-read-only t)) - (erase-buffer) - (sly-mrepl--insert-note "Cleared REPL history")))) - -(sly-define-channel-method listener :server-side-repl-close () - (with-current-buffer (sly-channel-get self 'buffer) - (sly-mrepl--teardown "Server side close" 'dont-signal-server))) - - -;;; Button type -;;; -(define-button-type 'sly-mrepl-part :supertype 'sly-part - 'sly-button-inspect - #'(lambda (entry-idx value-idx) - (sly-eval-for-inspector `(slynk-mrepl:inspect-entry - ,sly-mrepl--remote-channel - ,entry-idx - ,value-idx) - :inspector-name (sly-maybe-read-inspector-name))) - 'sly-button-describe - #'(lambda (entry-idx value-idx) - (sly-eval-describe `(slynk-mrepl:describe-entry ,sly-mrepl--remote-channel - ,entry-idx - ,value-idx))) - 'sly-button-pretty-print - #'(lambda (entry-idx value-idx) - (sly-eval-describe `(slynk-mrepl:pprint-entry ,sly-mrepl--remote-channel - ,entry-idx - ,value-idx))) - 'sly-mrepl-copy-part-to-repl 'sly-mrepl--copy-part-to-repl) - - -;;; Internal functions -;;; -(defun sly-mrepl--buffer-name (connection &optional handle) - (sly-buffer-name :mrepl :connection connection - :suffix handle)) - -(defun sly-mrepl--teardown-repls (process) - (cl-loop for buffer in (buffer-list) - when (buffer-live-p buffer) - do (with-current-buffer buffer - (when (and (eq major-mode 'sly-mrepl-mode) - (eq sly-buffer-connection process)) - (sly-mrepl--teardown (process-get process - 'sly-net-close-reason)))))) - -(defun sly-mrepl--process () (get-buffer-process (current-buffer))) ;stupid - -(defun sly-mrepl--mark () - "Returns a marker to the end of the last prompt." - (let ((proc (sly-mrepl--process))) - (unless proc (sly-user-error "Not in a connected REPL")) - (process-mark proc))) - -(defun sly-mrepl--safe-mark () - "Like `sly-mrepl--mark', but safe if there's no process." - (if (sly-mrepl--process) (sly-mrepl--mark) (point-max))) - -(defmacro sly-mrepl--commiting-text (props &rest body) - (declare (debug (sexp &rest form)) - (indent 1)) - (let ((start-sym (cl-gensym))) - `(let ((,start-sym (marker-position (sly-mrepl--mark))) - (inhibit-read-only t)) - ,@body - (add-text-properties ,start-sym (sly-mrepl--mark) - (append '(read-only t front-sticky (read-only)) - ,props))))) - -(defun sly-mrepl--forward-sexp (n) - "Just like `forward-sexp' unless point it at prompt start. -In that case, moving a sexp backward does nothing." - (if (or (cl-plusp n) - (/= (point) (sly-mrepl--safe-mark))) - (let ((forward-sexp-function nil)) - (forward-sexp n)))) - -(defun sly-mrepl--syntax-propertize (beg end) - "Make everything up to current prompt comment syntax." - (remove-text-properties beg end '(syntax-table nil)) - (let ((end (min end (sly-mrepl--safe-mark))) - (beg beg)) - (when (> end beg) - (unless (nth 8 (syntax-ppss beg)) - (add-text-properties beg (1+ beg) - `(syntax-table ,(string-to-syntax "!")))) - (add-text-properties (1- end) end - `(syntax-table ,(string-to-syntax "!")))))) - -(defun sly-mrepl--call-with-repl (repl-buffer fn) - (with-current-buffer repl-buffer - (cl-loop - while (not (buffer-local-value 'sly-mrepl--remote-channel - (current-buffer))) - do - (sly-warning "Waiting for a REPL to be setup for %s" - (sly-connection-name (sly-current-connection))) - (sit-for 0.5)) - (funcall fn))) - -(defmacro sly-mrepl--with-repl (repl-buffer &rest body) - (declare (indent 1) (debug (sexp &rest form))) - `(sly-mrepl--call-with-repl ,repl-buffer #'(lambda () ,@body))) - -(defun sly-mrepl--insert (string &optional face) - (sly-mrepl--commiting-text (when face - `(face ,face font-lock-face ,face)) - (comint-output-filter (sly-mrepl--process) - (propertize string 'sly-mrepl-break-output t)))) - -(defun sly-mrepl--break-output-p (pos) - (and (not (eq ?\n (char-after pos))) - (get-char-property pos 'sly-mrepl-break-output))) - -(defun sly-mrepl--insert-output (string &optional face nofilters) - (cond ((and (not sly-mrepl--read-mark) string) - (let ((inhibit-read-only t) - (start (marker-position sly-mrepl--output-mark)) - (face (or face - 'sly-mrepl-output-face))) - - (save-excursion - (goto-char sly-mrepl--output-mark) - (cond ((and (not (bobp)) - (sly-mrepl--break-output-p (1- start)) - (not (zerop (current-column)))) - (insert-before-markers "\n"))) - (setq string - (propertize (concat sly-mrepl--pending-output string) - 'face face - 'font-lock-face face)) - (setq sly-mrepl--pending-output nil) - (unless nofilters - (run-hook-wrapped - 'sly-mrepl-output-filter-functions - (lambda (fn) - (setq string (funcall fn string)) - nil))) - (insert-before-markers string) - (cond ((and (not (zerop (current-column))) - (sly-mrepl--break-output-p (point))) - (save-excursion (insert "\n")))) - (add-text-properties start sly-mrepl--output-mark - `(read-only t front-sticky (read-only) - field sly-mrepl--output))))) - (t - (setq sly-mrepl--pending-output - (concat sly-mrepl--pending-output string)) - (sly-message "Some output saved for later insertion")))) - -(defun sly-mrepl--insert-note (string &optional face) - (let* ((face (or face 'sly-mrepl-note-face)) - (string (replace-regexp-in-string "^" "; " string))) - (cond ((sly-mrepl--process) - ;; notes are inserted "synchronously" with the process mark process - (sly-mrepl--ensure-newline) - (sly-mrepl--insert string face)) - (t - ;; If no process yet, fall back to the simpler strategy. - (sly-mrepl--insert-output string face))))) - -(defun sly-mrepl--send-input-sexp () - (goto-char (point-max)) - (save-excursion - (skip-chars-backward "\n\t\s") - (delete-region (max (point) - (sly-mrepl--mark)) - (point-max))) - (buffer-disable-undo) - (overlay-put sly-mrepl--last-prompt-overlay 'face 'highlight) - (set (make-local-variable 'sly-mrepl--dirty-history) t) - (sly-mrepl--commiting-text - `(field sly-mrepl-input - keymap ,(let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") 'sly-mrepl-insert-input) - (define-key map [return] 'sly-mrepl-insert-input) - (define-key map [mouse-2] 'sly-mrepl-insert-input) - map)) - (comint-send-input)) - (sly-mrepl--ensure-prompt-face)) - -(defun sly-mrepl--ensure-newline () - (unless (save-excursion - (goto-char (sly-mrepl--mark)) - (zerop (current-column))) - (sly-mrepl--insert "\n"))) - -(defun sly-mrepl--accept-process-output () - (when (and sly-mrepl--dedicated-stream - (process-live-p sly-mrepl--dedicated-stream)) - ;; This non-blocking call should be enough to allow asynch calls - ;; to `sly-mrepl--insert-output' to still see the correct value - ;; for `sly-mrepl--output-mark' just before we call - ;; `sly-mrepl--catch-up'. - (while (accept-process-output sly-mrepl--dedicated-stream - 0 - (and (eq (window-system) 'w32) 1))))) - -(defun sly-mrepl--ensure-prompt-face () - "Override `comint.el''s use of `comint-highlight-prompt'." - (let ((inhibit-read-only t)) - (add-text-properties (overlay-start sly-mrepl--last-prompt-overlay) - (overlay-end sly-mrepl--last-prompt-overlay) - '(font-lock-face sly-mrepl-prompt-face)))) - -(defun sly-mrepl--insert-prompt (package prompt error-level &optional condition) - (sly-mrepl--accept-process-output) - (overlay-put sly-mrepl--last-prompt-overlay 'face 'bold) - (when condition - (sly-mrepl--insert-note (format "Debugger entered on %s" condition))) - (sly-mrepl--ensure-newline) - (sly-mrepl--catch-up) - (let ((beg (marker-position (sly-mrepl--mark)))) - (sly-mrepl--insert - (propertize - (concat - (when (cl-plusp error-level) - (concat (sly-make-action-button - (format "[%d]" error-level) - #'sly-db-pop-to-debugger-maybe) - " ")) - (propertize - (concat prompt "> ") - 'face 'sly-mrepl-prompt-face - 'font-lock-face 'sly-mrepl-prompt-face)) - 'sly-mrepl--prompt (downcase package))) - (move-overlay sly-mrepl--last-prompt-overlay beg (sly-mrepl--mark))) - (sly-mrepl--ensure-prompt-face) - (buffer-disable-undo) - (buffer-enable-undo)) - -(defun sly-mrepl--copy-part-to-repl (entry-idx value-idx) - (sly-mrepl--copy-objects-to-repl - `(,entry-idx ,value-idx) - :before (format "Returning value %s of history entry %s" - value-idx entry-idx))) - -(cl-defun sly-mrepl--eval-for-repl - (slyfun-and-args - &key insert-p before-prompt after-prompt (pop-to-buffer t)) - "Evaluate SLYFUN-AND-ARGS in Slynk, then call callbacks. - -SLYFUN-AND-ARGS is (SLYFUN . ARGS) and is called in -Slynk. SLYFUN's multiple return values are captured in a list and -passed to the optional unary callbacks BEFORE-PROMPT and -AFTER-PROMPT, called before or after prompt insertion, -respectively. - -If INSERT-P is non-nil, SLYFUN's results are printable -representations of Slynk objects and should be inserted into the -REPL. POP-TO-BUFFER says whether to pop the REPL buffer." - (sly-eval-async `(slynk-mrepl:eval-for-mrepl - ,sly-mrepl--remote-channel - ',(car slyfun-and-args) - ,@(cdr slyfun-and-args)) - (lambda (prompt-args-and-results) - (cl-destructuring-bind (prompt-args results) - prompt-args-and-results - (goto-char (sly-mrepl--mark)) - (let ((saved-text (buffer-substring (point) (point-max)))) - (delete-region (point) (point-max)) - (sly-mrepl--catch-up) - (when before-prompt - (funcall before-prompt results)) - (when insert-p - (sly-mrepl--insert-results results)) - (apply #'sly-mrepl--insert-prompt prompt-args) - (when pop-to-buffer - (pop-to-buffer (current-buffer))) - (goto-char (sly-mrepl--mark)) - (insert saved-text) - (when after-prompt - (funcall after-prompt results))))))) - -(cl-defun sly-mrepl--copy-objects-to-repl - (method-args &key before after (pop-to-buffer t)) - "Recall objects in the REPL history as a new entry. -METHOD-ARGS are SLYNK-MREPL:COPY-TO-REPL's optional args. If nil -, consider the globally saved objects that -SLYNK-MREPL:GLOBALLY-SAVE-OBJECT stored. Otherwise, it is a -list (ENTRY-IDX VALUE-IDX). BEFORE and AFTER as in -`sly-mrepl--save-and-copy-for-repl' POP-TO-BUFFER as in -`sly-mrepl--eval-for-repl'." - (sly-mrepl--eval-for-repl - `(slynk-mrepl:copy-to-repl - ,@method-args) - :before-prompt (if (stringp before) - (lambda (objects) - (sly-mrepl--insert-note before) - (sly-mrepl--insert-results objects)) - before) - :after-prompt after - :pop-to-buffer pop-to-buffer)) - -(defun sly-mrepl--make-result-button (result idx) - (sly--make-text-button (car result) nil - :type 'sly-mrepl-part - 'part-args (list (cadr result) idx) - 'part-label (format "REPL Result") - 'sly-mrepl--result result - 'sly-button-search-id (sly-button-next-search-id))) - -(defun sly-mrepl--insert-results (results) - (let* ((comint-preoutput-filter-functions nil)) - (if (null results) - (sly-mrepl--insert-note "No values") - (cl-loop for result in results - for idx from 0 - do - (sly-mrepl--ensure-newline) - (sly-mrepl--insert - (sly-mrepl--make-result-button result idx)))))) - -(defun sly-mrepl--catch-up () - "Synchronize the output mark with the REPL process mark." - (set-marker sly-mrepl--output-mark (sly-mrepl--mark))) - -(defun sly-mrepl--input-sender (_proc string) - (sly-mrepl--send-string (substring-no-properties string))) - -(defun sly-mrepl--send-string (string &optional _command-string) - (sly-mrepl--send `(:process ,string))) - -(defun sly-mrepl--send (msg) - "Send MSG to the remote channel." - (sly-send-to-remote-channel sly-mrepl--remote-channel msg)) - -(defun sly-mrepl--find-buffer (&optional connection thread) - "Find the shortest-named (default) `sly-mrepl' buffer for CONNECTION." - ;; CONNECTION defaults to the `sly-default-connection' passing - ;; through `sly-connection'. Seems to work OK... - ;; - (let* ((connection (or connection - (let ((sly-buffer-connection nil) - (sly-dispatching-connection nil)) - (sly-connection)))) - (repls (cl-remove-if-not - (lambda (x) - (with-current-buffer x - (and (eq major-mode 'sly-mrepl-mode) - (eq sly-buffer-connection connection) - (or (not thread) - (eq thread sly-current-thread))))) - (buffer-list))) - (sorted (cl-sort repls #'< :key (sly-compose #'length #'buffer-name)))) - (car sorted))) - -(defun sly-mrepl--find-create (connection) - (or (sly-mrepl--find-buffer connection) - (sly-mrepl-new connection))) - -(defun sly-mrepl--busy-p () - (>= sly-mrepl--output-mark (sly-mrepl--mark))) - -(defcustom sly-mrepl-history-file-name (expand-file-name "~/.sly-mrepl-history") - "File used to store SLY REPL's input history across sessions." - :type 'file - :group 'sly) - -(defun sly-mrepl--read-input-ring () - (let ((comint-input-ring-separator sly-mrepl--history-separator) - (comint-input-ring-file-name sly-mrepl-history-file-name)) - (comint-read-input-ring))) - -(defcustom sly-mrepl-prevent-duplicate-history 'move - "If non-nil, prevent duplicate entries in input history. - -Otherwise (if nil), input entry are always added to the end of -the history, even if they already occur in the history. - -If the non-nil value is `move', the previously occuring entry is -discarded, i.e. moved to a more recent spot. Any other non-nil -value laves the previous entry untouched and it is the more -recent entry that is discarded." - :type 'symbol - :group 'sly) - -(defun sly-mrepl--merge-and-save-history () - (let* - ;; To merge the file's history with the current buffer's - ;; history, sntart by deep-copying `comint-input-ring' to a - ;; separate variable. - ;; - ((current-ring (copy-tree comint-input-ring 'vectors-too)) - (index (ring-length current-ring)) - (comint-input-ring-separator sly-mrepl--history-separator) - (comint-input-ring-file-name sly-mrepl-history-file-name)) - ;; this sets `comint-input-ring' from the file - ;; - (sly-mrepl--read-input-ring) - ;; loop `current-ring', which potentially contains new entries and - ;; re-add entries to `comint-input-ring', which is now synched - ;; with the file and will be written to disk. Respect - ;; `sly-mrepl-prevent-duplicate-history'. - ;; - (cl-loop for i from (1- index) downto 0 - for item = (ring-ref current-ring i) - for existing-index = (ring-member comint-input-ring item) - do (cond ((and existing-index - (eq sly-mrepl-prevent-duplicate-history 'move)) - (ring-remove comint-input-ring existing-index) - (ring-insert comint-input-ring item)) - ((and existing-index - (not sly-mrepl-prevent-duplicate-history)) - (ring-insert comint-input-ring item)) - (t - (ring-insert comint-input-ring item))) - unless (ring-member comint-input-ring item) - do (ring-insert comint-input-ring item)) - ;; Now save `comint-input-ring' - (let ((coding-system-for-write 'utf-8-unix)) - (comint-write-input-ring)) - (set (make-local-variable 'sly-mrepl--dirty-history) nil))) - -(defun sly-mrepl--save-all-histories () - (cl-loop for buffer in (buffer-list) - do - (with-current-buffer buffer - (when (and (eq major-mode 'sly-mrepl-mode) - sly-mrepl--dirty-history) - (sly-mrepl--merge-and-save-history))))) - -(defun sly-mrepl--teardown (&optional reason dont-signal-server) - (remove-hook 'kill-buffer-hook 'sly-mrepl--teardown t) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (let ((start (point))) - (unless (zerop (current-column)) (insert "\n")) - (insert (format "; %s" (or reason "REPL teardown"))) - (unless (zerop (current-column)) (insert "\n")) - (insert "; --------------------------------------------------------\n") - (add-text-properties start (point) '(read-only t)))) - (sly-mrepl--merge-and-save-history) - (when sly-mrepl--dedicated-stream - (process-put sly-mrepl--dedicated-stream 'sly-mrepl--channel nil) - (kill-buffer (process-buffer sly-mrepl--dedicated-stream))) - (sly-close-channel sly-mrepl--local-channel) - ;; signal lisp that we're closingq - (unless dont-signal-server - (ignore-errors - ;; uses `sly-connection', which falls back to - ;; `sly-buffer-connection'. If that is closed it's probably - ;; because lisp died from (SLYNK:QUIT-LISP) already, and so - (sly-mrepl--send `(:teardown)))) - (set (make-local-variable 'sly-mrepl--remote-channel) nil) - (when (sly-mrepl--process) - (delete-process (sly-mrepl--process)))) - -(defun sly-mrepl--dedicated-stream-output-filter (process string) - (let* ((channel (process-get process 'sly-mrepl--channel)) - (buffer (and channel - (sly-channel-get channel 'buffer)))) - (if (buffer-live-p buffer) - (with-current-buffer buffer - (when (and (cl-plusp (length string)) - (eq (process-status sly-buffer-connection) 'open)) - (sly-mrepl--insert-output string))) - (sly-warning "No channel in process %s, probably torn down" process)))) - -(defun sly-mrepl--open-dedicated-stream (channel port coding-system) - (let* ((name (format "sly-dds-%s-%s" - (process-get sly-buffer-connection - 'sly--net-connect-counter) - (sly-channel.id channel))) - (stream (open-network-stream - name - (generate-new-buffer - (format " *%s*" name)) - (car (process-contact sly-buffer-connection)) - port)) - (emacs-coding-system (car (cl-find coding-system - sly-net-valid-coding-systems - :key #'cl-third)))) - (set-process-query-on-exit-flag stream nil) - (set-process-plist stream `(sly-mrepl--channel ,channel)) - (set-process-filter stream 'sly-mrepl--dedicated-stream-output-filter) - (set-process-coding-system stream emacs-coding-system emacs-coding-system) - (sly--when-let (secret (sly-secret)) - (sly-net-send secret stream)) - (run-hook-with-args 'sly-mrepl--dedicated-stream-hooks stream) - stream)) - -(cl-defun sly-mrepl--save-and-copy-for-repl - (slyfun-and-args &key repl before after) - "Evaluate SLYFUN-AND-ARGS in Slynk and prepare to copy to REPL. -BEFORE is a string inserted as a note, or a nullary function -which is run just before the object is copied to the -REPL. Optional BEFORE and AFTER are unary functions called with a -list of the saved values' presentations strings and run before -and after the the the prompt are inserted, respectively. BEFORE -can also be a string in which case it is inserted via -`sly-insert-note' followed by the saved values' presentations. -REPL is the REPL buffer to return the objects to." - (sly-eval-async - `(slynk-mrepl:globally-save-object ',(car slyfun-and-args) - ,@(cdr slyfun-and-args)) - #'(lambda (_ignored) - (sly-mrepl--copy-globally-saved-to-repl :before before - :after after - :repl repl)))) - -(cl-defun sly-mrepl--copy-globally-saved-to-repl - (&key before after repl (pop-to-buffer t)) - "Copy last globally saved values to REPL, or active REPL. -BEFORE and AFTER as described in -`sly-mrepl--save-and-copy-for-repl'." - (sly-mrepl--with-repl (or repl - (sly-mrepl--find-create (sly-connection))) - (sly-mrepl--copy-objects-to-repl nil - :before before - :after after - :pop-to-buffer pop-to-buffer))) - -(defun sly-mrepl--insert-call (spec results) - (delete-region (sly-mrepl--mark) (point-max)) - (insert (format - "%s" - `(,spec - ,@(cl-loop for (_object j constant) in results - for i from 0 - collect - (or constant - (make-symbol (format "#v%d:%d" j i)))))))) - -(defun sly-mrepl--assert-mrepl () - (unless (eq major-mode 'sly-mrepl-mode) - (sly-error "Not in a mREPL buffer"))) - - -;;; ELI-like history (and a bugfix) -;;; -;;; -(defcustom sly-mrepl-eli-like-history-navigation nil - "If non-NIL navigate history like ELI. -When this option is active, previous history entries navigated to -by M-p and M-n keep the current input and use it to surround the -history entry navigated to." - :type 'boolean - :group 'sly) - -(defvar sly-mrepl--eli-input nil) - -(defun sly-mrepl--set-eli-input () - (setq sly-mrepl--eli-input - (and sly-mrepl-eli-like-history-navigation - (let* ((offset (- (point) (sly-mrepl--mark))) - (existing (and (> offset 0) - (buffer-substring (sly-mrepl--mark) - (point-max))))) - (when existing - (cons (substring existing 0 offset) - (substring existing offset))))))) - -(defun sly-mrepl--keep-eli-input-maybe () - (when sly-mrepl--eli-input - (save-excursion - (goto-char (sly-mrepl--mark)) - (insert (car sly-mrepl--eli-input)) - (goto-char (point-max)) - (insert (cdr sly-mrepl--eli-input))))) - -(defvar sly-mrepl--eli-input-overlay nil) - -(defun sly-mrepl--surround-with-eli-input-overlay () - (if sly-mrepl--eli-input-overlay - (move-overlay sly-mrepl--eli-input-overlay - (sly-mrepl--mark) (point-max)) - (setq sly-mrepl--eli-input-overlay - (make-overlay (sly-mrepl--mark) (point-max)))) - (overlay-put sly-mrepl--eli-input-overlay - 'before-string (car sly-mrepl--eli-input)) - (overlay-put sly-mrepl--eli-input-overlay - 'after-string (cdr sly-mrepl--eli-input))) - -(defun sly-mrepl--setup-comint-isearch () - ;; Defeat Emacs bug 19572 in Emacs whereby comint refuses to - ;; i-search multi-line history entries. The doc of - ;; `isearch-search-fun-function' should explain the need for this - ;; lambda madness. - ;; - (unless (eq isearch-search-fun-function - 'isearch-search-fun-default) - (set (make-local-variable 'isearch-search-fun-function) - #'(lambda () - #'(lambda (&rest args) - (cl-letf - (((symbol-function - 'comint-line-beginning-position) - #'field-beginning)) - (apply (comint-history-isearch-search) - args)))))) - (sly-mrepl--set-eli-input) - (when sly-mrepl-eli-like-history-navigation - (set (make-local-variable 'isearch-push-state-function) - #'sly-mrepl--isearch-push-state))) - -(defun sly-mrepl--isearch-push-state (&rest args) - (apply #'comint-history-isearch-push-state args) - (unless (memq this-command - '(isearch-backward isearch-forward)) - (sly-mrepl--surround-with-eli-input-overlay))) - -(defun sly-mrepl--teardown-comint-isearch () - (set (make-local-variable 'isearch-search-fun-function) - 'isearch-search-fun-default) - (when (overlayp sly-mrepl--eli-input-overlay) - (delete-overlay sly-mrepl--eli-input-overlay) - (setq sly-mrepl--eli-input-overlay nil)) - (sly-mrepl--keep-eli-input-maybe)) - - -;;; Interactive commands -;;; -(defun sly-mrepl-indent-and-complete-symbol (arg) - "Indent the current line, perform symbol completion or show arglist. -Completion performed by `completion-at-point' or -`company-complete'. If there's no symbol at the point, show the -arglist for the most recently enclosed macro or function." - (interactive "P") - (let ((pos (point)) - (fn (if (bound-and-true-p company-mode) - 'company-complete - 'completion-at-point))) - (indent-for-tab-command arg) - (when (= pos (point)) - (cond ((save-excursion (re-search-backward "[^() \n\t\r]+\\=" nil t)) - (funcall fn)) - ((memq (char-before) '(?\t ?\ )) - (sly-show-arglist)))))) - -(defun sly-mrepl-return (&optional end-of-input) - "If the input is a whole expression, evaluate it and return the result." - (interactive "P") - (cl-assert (sly-connection)) - (cl-assert (process-live-p (sly-mrepl--process)) nil - "No local live process, cannot use this REPL") - (accept-process-output) - (cond ((and - (not sly-mrepl--read-mark) - (sly-mrepl--busy-p)) - (sly-message "REPL is busy")) - ((and (not sly-mrepl--read-mark) - (or (sly-input-complete-p (sly-mrepl--mark) (point-max)) - end-of-input)) - (sly-mrepl--send-input-sexp) - (sly-mrepl--catch-up)) - (sly-mrepl--read-mark - (unless end-of-input - (goto-char (point-max)) - (newline)) - (let ((comint-input-filter (lambda (_s) nil))) - (comint-send-input 'no-newline)) - (sly-mrepl--catch-up)) - (t - (newline-and-indent) - (sly-message "Input not complete")))) - -(defun sly-mrepl-previous-input-or-button (n) - (interactive "p") - (if (>= (point) (sly-mrepl--mark)) - (progn - (unless (memq last-command - '(sly-mrepl-previous-input-or-button - sly-mrepl-next-input-or-button)) - (sly-mrepl--set-eli-input)) - (comint-previous-input n) - (sly-mrepl--keep-eli-input-maybe)) - (sly-button-backward n))) - -(defun sly-mrepl-next-input-or-button (n) - (interactive "p") - (sly-mrepl-previous-input-or-button (- n))) - -(put 'sly-mrepl-next-input-or-button 'sly-button-navigation-command t) -(put 'sly-mrepl-previous-input-or-button 'sly-button-navigation-command t) - -(defun sly-mrepl (&optional display-action) - "Find or create the first useful REPL for the default connection. -If supplied, DISPLAY-ACTION is called on the -buffer. Interactively, DISPLAY-ACTION defaults to using -`switch-to-buffer' unless the intended buffer is already visible -in some window, in which case that window is selected." - (interactive (list (lambda (buf) - (let ((w (get-buffer-window buf))) - (if w (select-window w) (switch-to-buffer buf)))))) - (let* ((buffer - (sly-mrepl--find-create (sly-current-connection)))) - (when display-action - (funcall display-action buffer)) - buffer)) - -(defun sly-mrepl-on-connection () - (let* ((inferior-buffer - (and (sly-process) (process-buffer (sly-process)))) - (inferior-window - (and inferior-buffer (get-buffer-window inferior-buffer t)))) - (let ((sly-mrepl-pop-sylvester - (or (eq sly-mrepl-pop-sylvester 'on-connection) - sly-mrepl-pop-sylvester))) - (sly-mrepl 'pop-to-buffer)) - (when inferior-window - (bury-buffer inferior-buffer) - (delete-window inferior-window)) - (goto-char (point-max)))) - -(defun sly-mrepl-new (connection &optional handle) - "Create and setup a new REPL buffer for CONNECTION. -CONNECTION defaults to the current SLY connection. If such a -buffer already exists, or a prefix arg is given, prompt for a -handle to distinguish the new buffer from the existing." - (interactive - ;; FIXME: Notice a subtle bug/feature than when calling - ;; interactively in a buffer which has a connection, but not the - ;; default connection, the new REPL will be for that connection. - (let ((connection (sly-connection))) - (list connection - (if (or (get-buffer (sly-mrepl--buffer-name connection)) - current-prefix-arg) - (sly-read-from-minibuffer - "Nickname for this new REPL? "))))) - (let* ((name (sly-mrepl--buffer-name connection handle)) - (existing (get-buffer name))) - (when (and handle existing) - (sly-user-error "A REPL with that handle already exists")) - ;; Take this oportunity to save any other REPL histories so that - ;; the new REPL will see them. - (sly-mrepl--save-all-histories) - (let* ((local (sly-make-channel sly-listener-channel-methods)) - (buffer (pop-to-buffer name)) - (default-directory (if (file-readable-p default-directory) - default-directory - (expand-file-name "~/")))) - (with-current-buffer buffer - (sly-mrepl-mode) - (when (and (not existing) - (eq sly-mrepl-pop-sylvester t)) - (sly-mrepl--insert-note - (concat "\n" (sly-mrepl-random-sylvester) "\n\n") - 'sly-mrepl-output-face)) - (setq sly-buffer-connection connection) - (start-process (format "sly-pty-%s-%s" - (process-get connection - 'sly--net-connect-counter) - (sly-channel.id local)) - (current-buffer) - nil) - (set-process-query-on-exit-flag (sly-mrepl--process) nil) - (setq header-line-format - (format "Waiting for REPL creation ack for channel %d..." - (sly-channel.id local))) - (sly-channel-put local 'buffer (current-buffer)) - (add-hook 'kill-buffer-hook 'sly-mrepl--teardown nil 'local) - (set (make-local-variable 'sly-mrepl--local-channel) local)) - (sly-eval-async - `(slynk-mrepl:create-mrepl ,(sly-channel.id local)) - (lambda (result) - (cl-destructuring-bind (remote thread-id) result - (with-current-buffer buffer - (sly-mrepl--read-input-ring) - (setq header-line-format nil) - (setq sly-current-thread thread-id) - (set (make-local-variable 'sly-mrepl--remote-channel) remote) - (unwind-protect - (run-hooks 'sly-mrepl-hook 'sly-mrepl-runonce-hook) - (set-default 'sly-mrepl-runonce-hook nil)))))) - buffer))) - -(defun sly-mrepl-insert-input (pos) - (interactive (list (if (mouse-event-p last-input-event) - (posn-point (event-end last-input-event)) - (point)))) - (sly-mrepl--assert-mrepl) - (let* ((pos (if (eq (field-at-pos pos) 'sly-mrepl-input) - pos - (1+ pos))) - (new-input (and - (eq (field-at-pos (1+ pos)) 'sly-mrepl-input) - (field-string-no-properties pos))) - (offset (and new-input - (- (point) (field-beginning pos))))) - (cond (new-input - (goto-char (sly-mrepl--mark)) - (delete-region (point) (point-max)) - (insert (sly-trim-whitespace new-input)) - (goto-char (+ (sly-mrepl--mark) offset))) - (t - (sly-user-error "No input at point"))))) - -(defun sly-mrepl-guess-package (&optional point interactive) - (interactive (list (point) t)) - (let* ((point (or point (point))) - (probe - (previous-single-property-change point - 'sly-mrepl--prompt)) - (package (and probe - (or (get-text-property probe 'sly-mrepl--prompt) - (let ((probe2 - (previous-single-property-change - probe 'sly-mrepl--prompt))) - (and probe2 - (get-text-property probe2 - 'sly-mrepl--prompt))))))) - (when interactive - (sly-message "Guessed package \"%s\"" package)) - package)) - -(define-obsolete-function-alias - 'sly-mrepl-sync-package-and-default-directory 'sly-mrepl-sync - "1.0.0-alpha-3") - -(defun sly-mrepl-sync (&optional package directory expression) - "Go to the REPL, and set Slynk's PACKAGE and DIRECTORY. -Also yank EXPRESSION into the prompt. Interactively gather -PACKAGE and DIRECTORY these values from the current buffer, if -available. In this scenario EXPRESSION is only set if a C-u -prefix argument is given." - (interactive (list (sly-current-package) - (and buffer-file-name - default-directory) - (and current-prefix-arg - (sly-last-expression)))) - (sly-mrepl--with-repl (sly-mrepl--find-create (sly-connection)) - (when directory - (cd directory)) - (sly-mrepl--eval-for-repl - `(slynk-mrepl:sync-package-and-default-directory - :package-name ,package - :directory ,(and directory - (sly-to-lisp-filename directory))) - :insert-p nil - :before-prompt - #'(lambda (results) - (cl-destructuring-bind (package-2 directory-2) results - (sly-mrepl--insert-note - (cond ((and package directory) - (format "Synched package to %s and directory to %s" - package-2 directory-2)) - (directory - (format "Synched directory to %s" directory-2)) - (package - (format "Synched package to %s" package-2)) - (t - (format "Remaining in package %s and directory %s" - package-2 directory-2)))))) - :after-prompt - #'(lambda (_results) - (when expression - (goto-char (point-max)) - (let ((saved (point))) - (insert expression) - (when (string-match "\n" expression) - (indent-region saved (point-max))))))))) - -(defun sly-mrepl-clear-repl () - "Clear all this REPL's output history. -Doesn't clear input history." - (interactive) - (sly-mrepl--assert-mrepl) - (sly-mrepl--send `(:clear-repl-history))) - -(defun sly-mrepl-clear-recent-output () - "Clear this REPL's output between current and last prompt." - (interactive) - (sly-mrepl--assert-mrepl) - (cl-loop for search-start = - (set-marker (make-marker) - (1+ (overlay-start sly-mrepl--last-prompt-overlay))) - then pos - for pos = (set-marker - search-start - (previous-single-property-change search-start 'field)) - while (and (marker-position pos) - ;; FIXME: fragile (1- pos), use narrowing - (not (get-text-property (1- pos) 'sly-mrepl--prompt)) - (> pos (point-min))) - when (eq (field-at-pos pos) 'sly-mrepl--output) - do (let ((inhibit-read-only t)) - (delete-region (field-beginning pos) - (+ - (if (eq ?\n (char-before (field-end pos))) 0 1) - (field-end pos))) - (sly-mrepl--insert-output "; Cleared last output" - 'sly-mrepl-note-face)) - and return nil) - (sly-message "Cleared last output")) - -(defun sly-mrepl-next-prompt () - "Go to the beginning of the next REPL prompt." - (interactive) - (let ((pos (next-single-char-property-change (line-beginning-position 2) - 'sly-mrepl--prompt))) - (goto-char pos)) - (end-of-line)) - -(defun sly-mrepl-previous-prompt () - "Go to the beginning of the previous REPL prompt." - (interactive) - ;; This has two wrinkles around the first prompt: (1) when going to - ;; the first prompt it leaves point at column 0 (1) when called from - ;; frist prompt goes to beginning of buffer. The correct fix is to - ;; patch comint.el's comint-next-prompt and comint-previous-prompt - ;; anyway... - (let* ((inhibit-field-text-motion t) - (pos (previous-single-char-property-change (1- (line-beginning-position)) - 'sly-mrepl--prompt))) - (goto-char pos) - (goto-char (line-beginning-position))) - (end-of-line)) - - -;;; "External" non-interactive functions for plugging into -;;; other parts of SLY -;;; -(defun sly-inspector-copy-part-to-repl (number) - "Evaluate the inspector slot at point via the REPL (to set `*')." - (sly-mrepl--save-and-copy-for-repl - ;; FIXME: Using SLYNK:EVAL-FOR-INSPECTOR here repeats logic from - ;; sly.el's `sly-eval-for-inspector', but we can't use that here - ;; because we're already using `sly-mrepl--save-and-copy-for-repl'. - ;; Investigate if these functions could maybe be macros instead. - `(slynk:eval-for-inspector - ,sly--this-inspector-name - nil - 'slynk:inspector-nth-part-or-lose - ,number) - :before (format "Returning inspector slot %s" number))) - -(defun sly-db-copy-part-to-repl (frame-id var-id) - "Evaluate the frame var at point via the REPL (to set `*')." - (sly-mrepl--save-and-copy-for-repl - `(slynk-backend:frame-var-value ,frame-id ,var-id) - :repl (sly-mrepl--find-buffer (sly-current-connection) sly-current-thread) - :before (format "Returning var %s of frame %s" var-id frame-id))) - -(defun sly-apropos-copy-symbol-to-repl (name _type) - (sly-mrepl--save-and-copy-for-repl - `(common-lisp:identity ',(car (read-from-string name))) - :before (format "Returning symbol %s" name))) - -(defun sly-trace-dialog-copy-part-to-repl (id part-id type) - "Eval the Trace Dialog entry under point in the REPL (to set *)" - (sly-mrepl--save-and-copy-for-repl - `(slynk-trace-dialog:trace-part-or-lose ,id ,part-id ,type) - :before (format "Returning part %s (%s) of trace entry %s" part-id type id))) - -(defun sly-db-copy-call-to-repl (frame-id spec) - (sly-mrepl--save-and-copy-for-repl - `(slynk-backend:frame-arguments ,frame-id) - :before (format "The actual arguments passed to frame %s" frame-id) - :after #'(lambda (objects) - (sly-mrepl--insert-call spec objects)))) - -(defun sly-trace-dialog-copy-call-to-repl (trace-id spec) - (sly-mrepl--save-and-copy-for-repl - `(slynk-trace-dialog:trace-arguments-or-lose ,trace-id) - :before (format "The actual arguments passed to trace %s" trace-id) - :after #'(lambda (objects) - (sly-mrepl--insert-call spec objects)))) - -(defun sly-mrepl-inside-string-or-comment-p () - (let ((mark (and (process-live-p (sly-mrepl--process)) - (sly-mrepl--mark)))) - (when (and mark (> (point) mark)) - (let ((ppss (parse-partial-sexp mark (point)))) - (or (nth 3 ppss) (nth 4 ppss)))))) - - -;;; The comma shortcut -;;; -(defvar sly-mrepl-shortcut-history nil "History for sly-mrepl-shortcut.") - -(defun sly-mrepl-reset-shortcut (key-sequence) - "Set `sly-mrepl-shortcut' and reset REPL keymap accordingly." - (interactive "kNew shortcut key sequence? ") - (when (boundp 'sly-mrepl-shortcut) - (define-key sly-mrepl-mode-map sly-mrepl-shortcut nil)) - (set-default 'sly-mrepl-shortcut key-sequence) - (define-key sly-mrepl-mode-map key-sequence - '(menu-item "" sly-mrepl-shortcut - :filter (lambda (cmd) - (if (and (eq major-mode 'sly-mrepl-mode) - (sly-mrepl--shortcut-location-p)) - cmd))))) - -(defcustom sly-mrepl-shortcut (kbd ",") - "Keybinding string used for the REPL shortcut commands. -When setting this variable outside of the Customize interface, -`sly-mrepl-reset-shortcut' must be used." - :group 'sly - :type 'key-sequence - :set (lambda (_sym value) - (sly-mrepl-reset-shortcut value))) - -(defun sly-mrepl--shortcut-location-p () - (or (< (point) (sly-mrepl--mark)) - (and (not (let ((state (syntax-ppss))) - (or (nth 3 state) (nth 4 state)))) - (or (not (equal sly-mrepl-shortcut ",")) - (not (save-excursion - (search-backward "`" (sly-mrepl--mark) 'noerror))))))) - -(defvar sly-mrepl-shortcut-alist - ;; keep this alist ordered by the key value, in order to make it easier to see - ;; the identifying prefixes and keep them short - '(("cd" . sly-mrepl-set-directory) - ("clear repl" . sly-mrepl-clear-repl) - ("disconnect" . sly-disconnect) - ("disconnect all" . sly-disconnect-all) - ("in-package" . sly-mrepl-set-package) - ("restart lisp" . sly-restart-inferior-lisp) - ("quit lisp" . sly-quit-lisp) - ("sayoonara" . sly-quit-lisp) - ("set directory" . sly-mrepl-set-directory) - ("set package" . sly-mrepl-set-package))) - - -(defun sly-mrepl-set-package () - (interactive) - (let ((package (sly-read-package-name "New package: "))) - (sly-mrepl--eval-for-repl `(slynk-mrepl:guess-and-set-package ,package)))) - -(defun sly-mrepl-set-directory () - (interactive) - (let ((directory (read-directory-name "New directory: " - default-directory nil t))) - (sly-mrepl--save-and-copy-for-repl - `(slynk:set-default-directory ,directory) - :before (format "Setting directory to %s" directory)) - (cd directory))) - -(defun sly-mrepl-shortcut () - (interactive) - (let* ((string (sly-completing-read "Command: " - (mapcar #'car sly-mrepl-shortcut-alist) - nil 'require-match nil - 'sly-mrepl-shortcut-history - (car sly-mrepl-shortcut-history))) - (command (and string - (cdr (assoc string sly-mrepl-shortcut-alist))))) - (call-interactively command))) - - -;;; Backreference highlighting -;;; -(defvar sly-mrepl--backreference-overlays nil - "List of overlays on top of REPL result buttons.") -(make-variable-buffer-local 'sly-mrepl--backreference-overlays) - -(defun sly-mrepl-highlight-results (&optional entry-idx value-idx) - "Highlight REPL results for ENTRY-IDX and VALUE-IDX. -If VALUE-IDX is nil or `all', highlight all results for entry -ENTRY-IDX. If ENTRY-IDX is nil, highlight all results. Returns -a list of result buttons thus highlighted" - (interactive) - (cl-loop - for button in (sly-button-buttons-in (point-min) (point-max)) - for e-idx = (car (button-get button 'part-args)) - for v-idx = (cadr (button-get button 'part-args)) - when (and (button-type-subtype-p (button-type button) 'sly-mrepl-part) - (eq (button-get button 'sly-connection) (sly-current-connection)) - (not (button-get button 'sly-mrepl--highlight-overlay)) - (and (or (not entry-idx) - (= e-idx entry-idx)) - (or (not value-idx) - (eq value-idx 'all) - (= v-idx value-idx)))) - collect button and - do (let ((overlay (make-overlay (button-start button) (button-end button)))) - (push overlay sly-mrepl--backreference-overlays) - (overlay-put overlay 'before-string - (concat - (propertize - (format "%s:%s" - (car (button-get button 'part-args)) - (cadr (button-get button 'part-args))) - 'face 'highlight) - " "))))) - -(defun sly-mrepl-unhighlight-results () - "Unhighlight all repl results" - (interactive) - (mapc #'delete-overlay sly-mrepl--backreference-overlays) - (setq sly-mrepl--backreference-overlays nil)) - -(defvar sly-mrepl--backreference-overlay nil) -(defvar sly-mrepl--backreference-prefix "#v") - -(defun sly-mrepl--highlight-backreferences-maybe () - "Intended to be placed in `post-command-hook'." - (sly-mrepl-unhighlight-results) - (when sly-mrepl--backreference-overlay - (delete-overlay sly-mrepl--backreference-overlay)) - (let* ((match (save-excursion - (sly-beginning-of-symbol) - (looking-at - (format "%s\\([[:digit:]]+\\)?\\(:\\([[:digit:]]+\\)\\|:\\)?" - sly-mrepl--backreference-prefix)))) - (m0 (and match (match-string 0))) - (m1 (and m0 (match-string 1))) - (m2 (and m1 (match-string 2))) - (m3 (and m2 (match-string 3))) - (entry-idx (and m1 (string-to-number m1))) - (value-idx (and match - (or (and m3 (string-to-number m3)) - (and (not m2) - 'all))))) - (if (null match) - (set (make-local-variable 'sly-autodoc-preamble) nil) - (let ((buttons (sly-mrepl-highlight-results entry-idx value-idx)) - (overlay - (or sly-mrepl--backreference-overlay - (set (make-local-variable 'sly-mrepl--backreference-overlay) - (make-overlay 0 0)))) - (message-log-max nil) - (message-text)) - (move-overlay sly-mrepl--backreference-overlay - (match-beginning 0) (match-end 0)) - (cond - ((null buttons) - (overlay-put overlay 'face 'font-lock-warning-face) - (setq message-text (format "No history references for backreference `%s'" m0))) - ((and buttons - entry-idx - value-idx) - (overlay-put overlay 'face 'sly-action-face) - (let* ((prefix (if (numberp value-idx) - (format "Matched history value %s of entry %s: " - value-idx - entry-idx) - (format "Matched history entry %s%s: " - entry-idx - (if (cl-rest buttons) - (format " (%s values)" (length buttons)) - "")))) - (hint (propertize - (truncate-string-to-width - (replace-regexp-in-string "\n" " " - (button-label - (cl-first buttons))) - (- (window-width (minibuffer-window)) - (length prefix) 10) - nil - nil - "...") - 'face - 'sly-action-face))) - (setq message-text (format "%s" (format "%s%s" prefix hint))))) - (buttons - (setq message-text (format "Ambiguous backreference `%s', %s values possible" - m0 (length buttons))) - (overlay-put overlay 'face 'font-lock-warning-face)) - (t - (overlay-put overlay 'face 'font-lock-warning-face) - (setq message-text (format "Invalid backreference `%s'" m0)))) - (sly-message "%s" message-text) - (set (make-local-variable 'sly-autodoc-preamble) message-text))))) - - -;;;; Menu -;;;; -(easy-menu-define sly-mrepl--shortcut-menu nil - "Menu for accessing the mREPL anywhere in sly." - (let* ((C '(sly-connected-p))) - `("mREPL" - ["Go to default REPL" sly-mrepl ,C] - ["New REPL" sly-mrepl-new ,C] - ["Sync Package & Directory" sly-mrepl-sync - (and sly-editing-mode ,C)]))) - -(easy-menu-add-item sly-menu nil sly-mrepl--shortcut-menu "Documentation") - -(easy-menu-define sly-mrepl--menu sly-mrepl-mode-map - "Menu for SLY's MREPL" - (let* ((C '(sly-connected-p))) - `("SLY-mREPL" - [ " Complete symbol at point " sly-mrepl-indent-and-complete-symbol ,C ] - [ " Interrupt " sly-interrupt ,C ] - [ " Isearch history backward " isearch-backward ,C] - "----" - [ " Clear REPL" sly-mrepl-clear-repl ,C ] - [ " Clear last output" sly-mrepl-clear-recent-output ,C ]))) - - -(defvar sly-mrepl--debug-overlays nil) - -(defun sly-mrepl--debug (&rest ignored) - (interactive) - (mapc #'delete-overlay sly-mrepl--debug-overlays) - (let ((overlay (make-overlay sly-mrepl--output-mark - (sly-mrepl--mark))) - (color (if (< sly-mrepl--output-mark (sly-mrepl--mark)) - "green" - "orange")) - (marker-color (if (= sly-mrepl--output-mark (sly-mrepl--mark)) - "red" - "purple"))) - (overlay-put overlay - 'face `(:background ,color)) - (overlay-put overlay - 'after-string (propertize "F" 'face - `(:background ,marker-color))) - (push overlay sly-mrepl--debug-overlays))) - -(defun sly-mrepl--turn-on-debug () - (interactive) - (add-hook 'after-change-functions 'sly-mrepl--debug nil 'local) - (add-hook 'post-command-hook 'sly-mrepl--debug nil 'local)) - -(defun sly-mrepl--turn-off-debug () - (interactive) - (remove-hook 'after-change-functions 'sly-mrepl--debug 'local) - (remove-hook 'post-command-hook 'sly-mrepl--debug 'local)) - - -;;; A hack for Emacs Bug#32014 (Sly gh#165) -;;; -(when (version<= "26.1" emacs-version) - (advice-add - #'lisp-indent-line - :around - (lambda (&rest args) - (let ((beg (save-excursion (progn (beginning-of-line) (point))))) - (cl-letf (((symbol-function #'indent-line-to) - (lambda (indent) - (let ((shift-amt (- indent (current-column)))) - (if (zerop shift-amt) - nil - (delete-region beg (point)) - (indent-to indent)))))) - ;; call original - (apply args)))) - '((name . sly-workaround-for-emacs-bug-32014)))) - - -;;; Sylvesters -;;; -(defvar sly-mrepl--sylvesters - (with-temp-buffer - (insert-file-contents-literally - (expand-file-name "sylvesters.txt" - (file-name-directory load-file-name))) - (cl-loop while (< (point) (point-max)) - for start = (point) - do (search-forward "\n\n" nil 'noerror) - collect (buffer-substring-no-properties start (- (point) 2))))) - -(defun sly-mrepl-random-sylvester () - (let* ((sylvester (nth (random (length sly-mrepl--sylvesters)) - sly-mrepl--sylvesters)) - (woe (sly-random-words-of-encouragement)) - (uncommented - (replace-regexp-in-string "@@@@" woe sylvester))) - uncommented)) - -(provide 'sly-mrepl) diff --git a/elpa/sly-1.0.43/contrib/sly-package-fu.el b/elpa/sly-1.0.43/contrib/sly-package-fu.el @@ -1,454 +0,0 @@ -;; -*- lexical-binding: t; -*- -(require 'sly) -(require 'sly-parse "lib/sly-parse") - -(define-sly-contrib sly-package-fu - "Exporting/Unexporting symbols at point." - (:authors "Tobias C. Rittweiler <tcr@freebits.de>") - (:license "GPL") - (:slynk-dependencies slynk/package-fu) - (:on-load - (define-key sly-mode-map "\C-cx" 'sly-export-symbol-at-point) - (define-key sly-mode-map "\C-ci" 'sly-import-symbol-at-point)) - (:on-unload - ;; FIXME: To properly support unloading, this contrib should be - ;; made a minor mode with it's own keymap. The minor mode - ;; activation function should be added to the proper sly-* hooks. - ;; - )) - -(defvar sly-package-file-candidates - (mapcar #'file-name-nondirectory - '("package.lisp" "packages.lisp" "pkgdcl.lisp" - "defpackage.lisp"))) - -(defvar sly-export-symbol-representation-function - #'(lambda (n) (format "#:%s" n))) - -(defvar sly-import-symbol-package-transform-function - 'identity - "String transformation used by `sly-import-symbol-at-point'. - -This function is applied to a package name before it is inserted -into the defpackage form. By default, it is `identity' but you -may wish redefine it to do some tranformations, for example, to -replace dots with slashes to conform to a package-inferred ASDF -system-definition style.") - -(defvar sly-export-symbol-representation-auto t - "Determine automatically which style is used for symbols, #: or : -If it's mixed or no symbols are exported so far, -use `sly-export-symbol-representation-function'.") - -(define-obsolete-variable-alias 'sly-export-save-file - 'sly-package-fu-save-file "1.0.0-beta-3") - -(defvar sly-package-fu-save-file nil - "Save the package file after each automatic modification") - -(defvar sly-defpackage-regexp - "^(\\(cl:\\|common-lisp:\\|uiop:\\|\\uiop/package:\\)?\\(defpackage\\|define-package\\)\\>[ \t']*") - -(put 'uiop:define-package 'sly-common-lisp-indent-function '(as defpackage)) - -(defun sly-find-package-definition-rpc (package) - (sly-eval `(slynk:find-definition-for-thing - (slynk::guess-package ,package)))) - -(defun sly-find-package-definition-regexp (package) - (save-excursion - (save-match-data - (goto-char (point-min)) - (cl-block nil - (while (re-search-forward sly-defpackage-regexp nil t) - (when (sly-package-equal package (sly-sexp-at-point)) - (backward-sexp) - (cl-return (make-sly-file-location (buffer-file-name) - (1- (point)))))))))) - -(defun sly-package-equal (designator1 designator2) - ;; First try to be lucky and compare the strings themselves (for the - ;; case when one of the designated packages isn't loaded in the - ;; image.) Then try to do it properly using the inferior Lisp which - ;; will also resolve nicknames for us &c. - (or (cl-equalp (sly-cl-symbol-name designator1) - (sly-cl-symbol-name designator2)) - (sly-eval `(slynk:package= ,designator1 ,designator2)))) - -(defun sly-export-symbol (symbol package) - "Unexport `symbol' from `package' in the Lisp image." - (sly-eval `(slynk:export-symbol-for-emacs ,symbol ,package))) - -(defun sly-unexport-symbol (symbol package) - "Export `symbol' from `package' in the Lisp image." - (sly-eval `(slynk:unexport-symbol-for-emacs ,symbol ,package))) - - -(defun sly-find-possible-package-file (buffer-file-name) - (cl-labels ((file-name-subdirectory (dirname) - (expand-file-name - (concat (file-name-as-directory (sly-to-lisp-filename dirname)) - (file-name-as-directory "..")))) - (try (dirname) - (cl-dolist (package-file-name sly-package-file-candidates) - (let ((f (sly-to-lisp-filename - (concat dirname package-file-name)))) - (when (file-readable-p f) - (cl-return f)))))) - (when buffer-file-name - (let ((buffer-cwd (file-name-directory buffer-file-name))) - (or (try buffer-cwd) - (try (file-name-subdirectory buffer-cwd)) - (try (file-name-subdirectory - (file-name-subdirectory buffer-cwd)))))))) - -(defun sly-goto-package-source-definition (package) - "Tries to find the DEFPACKAGE form of `package'. If found, -places the cursor at the start of the DEFPACKAGE form." - (cl-labels ((try (location) - (when (sly-location-p location) - (sly-move-to-source-location location) - t))) - (or (try (sly-find-package-definition-rpc package)) - (try (sly-find-package-definition-regexp package)) - (try (sly--when-let - (package-file (sly-find-possible-package-file - (buffer-file-name))) - (with-current-buffer (find-file-noselect package-file t) - (sly-find-package-definition-regexp package)))) - (sly-error "Couldn't find source definition of package: %s" package)))) - -(defun sly-at-expression-p (pattern) - (when (ignore-errors - ;; at a list? - (= (point) (progn (down-list 1) - (backward-up-list 1) - (point)))) - (save-excursion - (down-list 1) - (sly-in-expression-p pattern)))) - -(defun sly-goto-next-export-clause () - ;; Assumes we're inside the beginning of a DEFPACKAGE form. - (let ((point)) - (save-excursion - (cl-block nil - (while (ignore-errors (sly-forward-sexp) t) - (skip-chars-forward " \n\t") - (when (sly-at-expression-p '(:export *)) - (setq point (point)) - (cl-return))))) - (if point - (goto-char point) - (error "No next (:export ...) clause found")))) - -(defun sly-search-exports-in-defpackage (symbol-name) - "Look if `symbol-name' is mentioned in one of the :EXPORT clauses." - ;; Assumes we're inside the beginning of a DEFPACKAGE form. - (cl-labels ((target-symbol-p (symbol) - (string-match-p (format "^\\(\\(#:\\)\\|:\\)?%s$" - (regexp-quote symbol-name)) - symbol))) - (save-excursion - (cl-block nil - (while (ignore-errors (sly-goto-next-export-clause) t) - (let ((clause-end (save-excursion (forward-sexp) (point)))) - (save-excursion - (while (search-forward symbol-name clause-end t) - (when (target-symbol-p (sly-symbol-at-point)) - (cl-return (if (sly-inside-string-p) - ;; Include the following " - (1+ (point)) - (point)))))))))))) - - -(defun sly-package-fu--read-symbols () - "Reads sexps as strings from the point to end of sexp. - -For example, in this situation. - - (for<point> bar minor (again 123)) - -this will return (\"bar\" \"minor\" \"(again 123)\")" - (cl-labels ((read-sexp () - (ignore-errors - (forward-comment (point-max)) - (buffer-substring-no-properties - (point) (progn (forward-sexp) (point)))))) - (save-excursion - (cl-loop for sexp = (read-sexp) while sexp collect sexp)))) - -(defun sly-package-fu--normalize-name (name) - (if (string-prefix-p "\"" name) - (read name) - (replace-regexp-in-string "^\\(\\(#:\\)\\|:\\)" - "" name))) - -(defun sly-defpackage-exports () - "Return a list of symbols inside :export clause of a defpackage." - ;; Assumes we're inside the beginning of a DEFPACKAGE form. - (save-excursion - (mapcar #'sly-package-fu--normalize-name - (cl-loop while (ignore-errors (sly-goto-next-export-clause) t) - do (down-list) (forward-sexp) - append (sly-package-fu--read-symbols) - do (up-list) (backward-sexp))))) - -(defun sly-symbol-exported-p (name symbols) - (cl-member name symbols :test 'cl-equalp)) - -(defun sly-frob-defpackage-form (current-package do-what symbols) - "Adds/removes `symbol' from the DEFPACKAGE form of `current-package' -depending on the value of `do-what' which can either be `:export', -or `:unexport'. - -Returns t if the symbol was added/removed. Nil if the symbol was -already exported/unexported." - (save-excursion - (sly-goto-package-source-definition current-package) - (down-list 1) ; enter DEFPACKAGE form - (forward-sexp) ; skip DEFPACKAGE symbol - ;; Don't or will fail if (:export ...) is immediately following - ;; (forward-sexp) ; skip package name - (let ((exported-symbols (sly-defpackage-exports)) - (symbols (if (consp symbols) - symbols - (list symbols))) - (number-of-actions 0)) - (cl-ecase do-what - (:export - (sly-add-export) - (dolist (symbol symbols) - (let ((symbol-name (sly-cl-symbol-name symbol))) - (unless (sly-symbol-exported-p symbol-name exported-symbols) - (cl-incf number-of-actions) - (sly-package-fu--insert-symbol symbol-name))))) - (:unexport - (dolist (symbol symbols) - (let ((symbol-name (sly-cl-symbol-name symbol))) - (when (sly-symbol-exported-p symbol-name exported-symbols) - (sly-remove-export symbol-name) - (cl-incf number-of-actions)))))) - (when sly-package-fu-save-file - (save-buffer)) - (cons number-of-actions - (current-buffer))))) - -(defun sly-add-export () - (let (point) - (save-excursion - (while (ignore-errors (sly-goto-next-export-clause) t) - (setq point (point)))) - (cond (point - (goto-char point) - (down-list) - (sly-end-of-list)) - (t - (sly-end-of-list) - (unless (looking-back "^\\s-*" (line-beginning-position) nil) - (newline-and-indent)) - (insert "(:export ") - (save-excursion (insert ")")))))) - -(defun sly-determine-symbol-style () - ;; Assumes we're inside :export - (save-excursion - (sly-beginning-of-list) - (sly-forward-sexp) - (let ((symbols (sly-package-fu--read-symbols))) - (cond ((null symbols) - sly-export-symbol-representation-function) - ((cl-every (lambda (x) - (string-match "^:" x)) - symbols) - (lambda (n) (format ":%s" n))) - ((cl-every (lambda (x) - (string-match "^#:" x)) - symbols) - (lambda (n) (format "#:%s" n))) - ((cl-every (lambda (x) - (string-prefix-p "\"" x)) - symbols) - (lambda (n) (prin1-to-string (upcase (substring-no-properties n))))) - (t - sly-export-symbol-representation-function))))) - -(defun sly-format-symbol-for-defpackage (symbol-name) - (funcall (if sly-export-symbol-representation-auto - (sly-determine-symbol-style) - sly-export-symbol-representation-function) - symbol-name)) - -(defun sly-package-fu--insert-symbol (symbol-name) - ;; Assumes we're at the inside :export or :import-from form - ;; after the last symbol - (let ((symbol-name (sly-format-symbol-for-defpackage symbol-name))) - (unless (looking-back "^\\s-*" (line-beginning-position) nil) - (newline-and-indent)) - (insert symbol-name))) - -(defun sly-remove-export (symbol-name) - ;; Assumes we're inside the beginning of a DEFPACKAGE form. - (let ((point)) - (while (setq point (sly-search-exports-in-defpackage symbol-name)) - (save-excursion - (goto-char point) - (backward-sexp) - (delete-region (point) point) - (beginning-of-line) - (when (looking-at "^\\s-*$") - (join-line) - (delete-trailing-whitespace (point) (line-end-position))))))) - -(defun sly-export-symbol-at-point () - "Add the symbol at point to the defpackage source definition -belonging to the current buffer-package. With prefix-arg, remove -the symbol again. Additionally performs an EXPORT/UNEXPORT of the -symbol in the Lisp image if possible." - (interactive) - (let* ((symbol (sly-symbol-at-point)) - (package (or (and (string-match "^\\([^:]+\\):.*" symbol) - (match-string 1 symbol)) - (sly-current-package)))) - (unless symbol (error "No symbol at point.")) - (cond (current-prefix-arg - (let* ((attempt (sly-frob-defpackage-form package :unexport symbol)) - (howmany (car attempt)) - (where (buffer-file-name (cdr attempt)))) - (if (cl-plusp howmany) - (sly-message "Symbol `%s' no longer exported from `%s' in %s" - symbol package where) - (sly-message "Symbol `%s' is not exported from `%s' in %s" - symbol package where))) - (sly-unexport-symbol symbol package)) - (t - (let* ((attempt (sly-frob-defpackage-form package :export symbol)) - (howmany (car attempt)) - (where (buffer-file-name (cdr attempt)))) - (if (cl-plusp howmany) - (sly-message "Symbol `%s' now exported from `%s' in %s" - symbol package where) - (sly-message "Symbol `%s' already exported from `%s' in %s" - symbol package where))) - (sly-export-symbol symbol package))))) - -(defun sly-export-class (name) - "Export acessors, constructors, etc. associated with a structure or a class" - (interactive (list (sly-read-from-minibuffer "Export structure named: " - (sly-symbol-at-point)))) - (let* ((package (sly-current-package)) - (symbols (sly-eval `(slynk:export-structure ,name ,package)))) - (sly-message "%s symbols exported from `%s'" - (car (sly-frob-defpackage-form package :export symbols)) - package))) - -(defalias 'sly-export-structure 'sly-export-class) - -;; -;; Dealing with import-from -;; - -(defun sly-package-fu--search-import-from (package) - ;; Suppose, we are in the defpackage sexp - (let* ((normalized-package (sly-package-fu--normalize-name package)) - (regexp (format "(:import-from[ \t']*\\(:\\|#:\\)?%s" - (regexp-quote (regexp-quote normalized-package)))) - (search-result (re-search-forward regexp nil t))) - (message "Normalized: %s, regex: %s" normalized-package - regexp) - (when search-result - ;; import-from clause was found - t))) - - -(defun sly-package-fu--create-new-import-from (package symbol) - (sly-goto-package-source-definition (sly-current-package)) - (forward-sexp) - ;; Now, search last :import-from or :use form - (cond - ((re-search-backward "(:\\(use\\|import-from\\)" nil t) - ;; Skip found expression: - (forward-sexp) - ;; and insert a new (:import-from <package> <symbol>) form. - (newline-and-indent) - (let ((symbol-name (sly-format-symbol-for-defpackage symbol)) - (package-name (sly-format-symbol-for-defpackage package))) - (insert "(:import-from )") - (backward-char) - (insert package-name) - (newline-and-indent) - (insert symbol-name))) - (t (error "Unable to find :use form in the defpackage form.")))) - - -(defun sly-package-fu--add-or-update-import-from-form (symbol) - "Do the heavy-lifting for `sly-import-symbol-at-point'. - -Accept a string or a symbol like \"alexandria:with-gensyms\", -and add it to existing (import-from #:alexandria ...) form, or -create a new one. Return name of the given symbol inside of its -package. For example above, return \"with-gensyms\"." - (save-excursion - ;; First, will go to the package definition - (sly-goto-package-source-definition (sly-current-package)) - - (let* ((package (funcall sly-import-symbol-package-transform-function - (sly-cl-symbol-package symbol))) - (simple-symbol (sly-cl-symbol-name symbol)) - (import-exists (when package - (sly-package-fu--search-import-from package)))) - - ;; We only process symbols in fully qualified form like - ;; weblocks/request:get-parameter - (unless package - (user-error "This only works on symbols with package designator.")) - - ;; First ask CL to actually import the symbol (a synchronized - ;; eval makes sure that an error aborts the rest of the command) - ;; - (sly-eval `(slynk:import-symbol-for-emacs ,symbol - ,(sly-current-package) - ,package)) - - (if import-exists - (let ((imported-symbols (mapcar #'sly-package-fu--normalize-name - (sly-package-fu--read-symbols)))) - (unless (cl-member simple-symbol - imported-symbols - :test 'cl-equalp) - ;; If symbol is not imported yet, then just - ;; add it to the end - (sly-package-fu--insert-symbol simple-symbol) - (when sly-package-fu-save-file (save-buffer)))) - ;; If there is no import from this package yet, - ;; then we'll add it right after the last :import-from - ;; or :use construction - (sly-package-fu--create-new-import-from package - simple-symbol) - (when sly-package-fu-save-file (save-buffer))) - ;; Always return symbol-without-package, because it is useful - ;; to replace symbol at point and change it from fully qualified - ;; form to a simple-form - simple-symbol))) - - -(defun sly-import-symbol-at-point () - "Add a qualified symbol to package's :import-from subclause. - -Takes a package-qualified symbol at point, adds it to the current -package's defpackage form (under its :import-form subclause) and -replaces with a symbol name without the package designator." - (interactive) - (let* ((bounds (sly-bounds-of-symbol-at-point)) - (beg (set-marker (make-marker) (car bounds))) - (end (set-marker (make-marker) (cdr bounds)))) - (when bounds - (let ((non-qualified-name - (sly-package-fu--add-or-update-import-from-form - (buffer-substring-no-properties beg end)))) - (when non-qualified-name - (delete-region beg end) - (insert non-qualified-name)))))) - - -(provide 'sly-package-fu) diff --git a/elpa/sly-1.0.43/contrib/sly-stickers.el b/elpa/sly-1.0.43/contrib/sly-stickers.el @@ -1,1359 +0,0 @@ -;;; sly-stickers.el --- Live-code annotations for SLY -*- lexical-binding: t; -*- - -;; Copyright (C) 2014 JoĂŁo Távora - -;; Author: JoĂŁo Távora <joaotavora@gmail.com> -;; Keywords: convenience, languages, lisp, tools - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: -;;; -;;; There is much in this library that would merit comment. Just some points: -;;; -;;; * Stickers are just overlays that exist on the Emacs side. A lot -;;; of the code is managing overlay nesting levels so that faces -;;; are chosen suitably for making sticker inside stickers -;;; visually recognizable. -;;; -;;; The main entry-point here is the interactive command -;;; `sly-sticker-dwim', which places and removes stickers. -;;; -;;; Stickers are also indexed by an integer and placed in a -;;; connection-global hash-table, `sly-stickers--stickers' It can be -;;; connection-global because the same sticker with the same id -;;; might eventually be sent, multiple times, to many -;;; connections. It's the Slynk side that has to be able to tell -;;; whence the stickers comes from (this is not done currently). -;;; -;;; * The gist of stickers is instrumenting top-level forms. This is -;;; done by hooking onto `sly-compile-region-function'. Two separate -;;; compilations are performed: one for the uninstrumented form and -;;; another for the intrumented form. This is so that warnings and -;;; compilations errors that are due to stickers exclusively can be -;;; sorted out. If the second compilation fails, the stickers dont -;;; "stick", i.e. they are not armed. -;;; -;;; * File compilation is also hooked onto via -;;; `sly-compilation-finished-hook'. The idea here is to first -;;; compile the whole file, then traverse any top-level forms that -;;; contain stickers and instrument those. -;;; -;;; * On the emacs-side, the sticker overlays are very ephemeral -;;; objects. They are not persistently saved in any way. Deleting or -;;; modifying text inside them automatically deletes them. -;;; -;;; The slynk side eventually must be told to let go of deleted -;;; stickers. Before this happens these stickers are known as -;;; zombies. Reaping happens on almost every SLY -> Slynk call. -;;; Killing the buffer they live in doesn't automatically delete -;;; them, but reaping eventually happens anyway via -;;; `sly-stickers--sticker-by-id'. -;;; -;;; Before a zombie sticker is reaped, some code may still be -;;; running that adds recordings to these stickers, and some of -;;; these recordings make it to the Emacs side. The user can ignore -;;; them in `sly-stickers--replay', being notified that a deleted -;;; sticker is being referenced. -;;; -;;; This need to communicate dead stickers to Slynk is only here -;;; because using weak-hash-tables is impractical for stickers -;;; indexed by integers. Perhaps this could be fixed if the -;;; instrumented forms could reference sticker objects directly. -;;; -;;; * To see the results of sticker-instrumented code, there are the -;;; interactive commands `sly-stickers--replay' and -;;; `sly-stickers--fetch'. If "breaking stickers" is enabled, the -;;; debugger is also invoked before a sticker is reached and after a -;;; sticker returns (if it returns). Auxiliary data-structures like -;;; `sly-stickers--recording' are used here. -;;; -;;; * `sly-stickers--replay-state' and `sly-stickers--replay-map' are -;;; great big hacks just for handling the `sly-stickers--replay' -;;; interactive loop. Should look into recursive minibuffers or -;;; something more akin to `ediff', for example. -;;; -;;; Code: - - -(require 'sly) -(require 'sly-parse "lib/sly-parse") -(require 'sly-buttons "lib/sly-buttons") - -(eval-when-compile - (when (version< emacs-version "26") - ;; Using `cl-defstruct' needs `cl' on older Emacsen. See issue - ;; https://github.com/joaotavora/sly/issues/54 - (require 'cl))) - -(require 'cl-lib) -(require 'hi-lock) ; for the faces -(require 'color) -(require 'pulse) ; pulse-momentary-highlight-overlay - -(define-sly-contrib sly-stickers - "Mark expressions in source buffers and annotate return values." - (:authors "JoĂŁo Távora <joaotavora@gmail.com>") - (:license "GPL") - (:slynk-dependencies slynk/stickers) - (:on-load (add-hook 'sly-editing-mode-hook 'sly-stickers-mode) - (add-hook 'sly-mode-hook 'sly-stickers-shortcut-mode) - (setq sly-compile-region-function - 'sly-stickers-compile-region-aware-of-stickers) - (add-hook 'sly-compilation-finished-hook - 'sly-stickers-after-buffer-compilation t) - (add-hook 'sly-db-extras-hooks 'sly-stickers--handle-break)) - (:on-unload (remove-hook 'sly-editing-mode-hook 'sly-stickers-mode) - (remove-hook 'sly-mode-hook 'sly-stickers-shortcut-mode) - (setq sly-compile-region-function 'sly-compile-region-as-string) - (remove-hook 'sly-compilation-finished-hook - 'sly-stickers-after-buffer-compilation) - (remove-hook 'sly-db-extras-hooks 'sly-stickers--handle-break))) - - - -;;;; Bookeeping for local stickers -;;;; -(defvar sly-stickers--counter 0) - -(defvar sly-stickers--stickers (make-hash-table)) - -(defvar sly-stickers--zombie-sticker-ids nil - "Sticker ids that might exist in Slynk but no longer in Emacs.") - -(defun sly-stickers--zombies () sly-stickers--zombie-sticker-ids) - -(defun sly-stickers--reset-zombies () (setq sly-stickers--zombie-sticker-ids nil)) - - - -;;;; Sticker display and UI logic -;;;; -(defgroup sly-stickers nil - "Mark expressions in source buffers and annotate return values." - :prefix "sly-stickers-" - :group 'sly) - -(when nil - (cl-loop for sym in '(sly-stickers-placed-face - sly-stickers-armed-face - sly-stickers-empty-face - sly-stickers-recordings-face - sly-stickers-exited-non-locally-face) - do - (put sym 'face-defface-spec nil))) - -(defface sly-stickers-placed-face - '((((background dark)) (:background "light grey" :foreground "black")) - (t (:background "light grey"))) - "Face for sticker just set") - -(defface sly-stickers-armed-face - '((t (:strike-through nil :inherit hi-blue))) - "Face for stickers that have been armed") - -(defface sly-stickers-recordings-face - '((t (:strike-through nil :inherit hi-green))) - "Face for stickers that have new recordings") - -(defface sly-stickers-empty-face - '((t (:strike-through nil :inherit hi-pink))) - "Face for stickers that have no recordings.") - -(defface sly-stickers-exited-non-locally-face - '((t (:strike-through t :inherit sly-stickers-empty-face))) - "Face for stickers that have exited non-locally.") - -(defvar sly-stickers-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-s C-s") 'sly-stickers-dwim) - (define-key map (kbd "C-c C-s C-d") 'sly-stickers-clear-defun-stickers) - (define-key map (kbd "C-c C-s C-k") 'sly-stickers-clear-buffer-stickers) - map)) - -(defvar sly-stickers-shortcut-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-s S") 'sly-stickers-fetch) - (define-key map (kbd "C-c C-s F") 'sly-stickers-forget) - (define-key map (kbd "C-c C-s C-r") 'sly-stickers-replay) - map)) - -(define-minor-mode sly-stickers-mode - "Mark expression in source buffers and annotate return values.") - -(define-minor-mode sly-stickers-shortcut-mode - "Shortcuts for navigating sticker recordings.") - -(defvar sly-stickers--sticker-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "M-RET") 'sly-mrepl-copy-part-to-repl) - (define-key map [down-mouse-3] 'sly-button-popup-part-menu) - (define-key map [mouse-3] 'sly-button-popup-part-menu) - map)) - -(define-button-type 'sly-stickers-sticker :supertype 'sly-part - 'sly-button-inspect 'sly-stickers--inspect-recording - 'sly-button-echo 'sly-stickers--echo-sticker - 'keymap sly-stickers--sticker-map) - -(defun sly-stickers--set-tooltip (sticker &optional info) - (let* ((help-base (button-get sticker 'sly-stickers--base-help-echo)) - (text (if info - (concat "[sly] Sticker:" info "\n" help-base) - help-base))) - (button-put sticker 'help-echo text) - (button-put sticker 'sly-stickers--info info))) - -(defun sly-stickers--echo-sticker (sticker &rest more) - (cl-assert (null more) "Apparently two stickers at exact same location") - (sly-message (button-get sticker 'sly-stickers--info)) - (sly-button-flash sticker)) - -(defcustom sly-stickers-max-nested-stickers 4 - "The maximum expected level expected of sticker nesting. -If you nest more than this number of stickers inside other -stickers, the overlay face will be very dark, and probably -render the underlying text unreadable." - :type :integer) - -(defvar sly-stickers-color-face-attribute :background - "Color-capable attribute of sticker faces that represents nesting.") - -(gv-define-setter sly-stickers--level (level sticker) - `(prog1 - (setf (sly-button--level ,sticker) ,level) - (when (button-get ,sticker 'sly-stickers--base-face) - (sly-stickers--set-face ,sticker)))) - -(defun sly-stickers--level (sticker) (sly-button--level sticker)) - -(defun sly-stickers--guess-face-color (face) - (face-attribute-specified-or - (face-attribute face sly-stickers-color-face-attribute nil t) - nil)) - -(defun sly-stickers--set-face (sticker &optional face) - (let* ((face (or face - (button-get sticker 'sly-stickers--base-face))) - (guessed-color (sly-stickers--guess-face-color face))) - (button-put sticker 'sly-stickers--base-face face) - (unless guessed-color - (sly-error "sorry, can't guess color for face %s for sticker %s")) - (button-put sticker 'face - `(:inherit ,face - ,sly-stickers-color-face-attribute - ,(color-darken-name - guessed-color - (* 25 - (/ (sly-stickers--level sticker) - sly-stickers-max-nested-stickers - 1.0))))))) - -(defun sly-stickers--stickers-in (beg end) - (sly-button--overlays-in beg end 'sly-stickers--sticker-id)) -(defun sly-stickers--stickers-at (pos) - (sly-button--overlays-at pos 'sly-stickers--sticker-id)) -(defun sly-stickers--stickers-between (beg end) - (sly-button--overlays-between beg end 'sly-stickers--sticker-id)) -(defun sly-stickers--stickers-exactly-at (beg end) - (sly-button--overlays-exactly-at beg end 'sly-stickers--sticker-id)) - - -(defun sly-stickers--sticker (from to) - "Place a new sticker from FROM to TO" - (let* ((intersecting (sly-stickers--stickers-in from to)) - (contained (sly-stickers--stickers-between from to)) - (not-contained (cl-set-difference intersecting contained)) - (containers nil)) - (unless (cl-every #'(lambda (e) - (and (< (button-start e) from) - (> (button-end e) to))) - not-contained) - (sly-error "Cannot place a sticker that partially overlaps other stickers")) - (when (sly-stickers--stickers-exactly-at from to) - (sly-error "There is already a sticker at those very coordinates")) - ;; by now we know that other intersecting, non-contained stickers - ;; are our containers. - ;; - (setq containers not-contained) - (let* ((label "Brand new sticker") - (sticker - ;;; FIXME: We aren't using sly--make-text-button here - ;;; because it doesn't allow overlay button s - (make-button from to :type 'sly-stickers-sticker - 'sly-connection (sly-current-connection) - 'part-args (list -1 nil) - 'part-label label - 'sly-button-search-id (sly-button-next-search-id) - 'modification-hooks '(sly-stickers--sticker-modified) - 'sly-stickers-id (cl-incf sly-stickers--counter) - 'sly-stickers--base-help-echo - "mouse-3: Context menu"))) - ;; choose a suitable level for ourselves and increase the - ;; level of those contained by us - ;; - (setf (sly-stickers--level sticker) - (1+ (cl-reduce #'max containers - :key #'sly-stickers--level - :initial-value -1))) - (mapc (lambda (s) (cl-incf (sly-stickers--level s))) contained) - ;; finally, set face - ;; - (sly-stickers--set-tooltip sticker label) - (sly-stickers--set-face sticker 'sly-stickers-placed-face) - sticker))) - -(defun sly-stickers--sticker-id (sticker) - (button-get sticker 'sly-stickers-id)) - -(defun sly-stickers--arm-sticker (sticker) - (let* ((id (sly-stickers--sticker-id sticker)) - (label (format "Sticker %d is armed" id))) - (button-put sticker 'part-args (list id nil)) - (button-put sticker 'part-label label) - (button-put sticker 'sly-stickers--last-known-recording nil) - (sly-stickers--set-tooltip sticker label) - (sly-stickers--set-face sticker 'sly-stickers-armed-face) - (puthash id sticker sly-stickers--stickers))) - -(defun sly-stickers--disarm-sticker (sticker) - (let* ((id (sly-stickers--sticker-id sticker)) - (label (format "Sticker %d failed to stick" id))) - (button-put sticker 'part-args (list -1 nil)) - (button-put sticker 'part-label label) - (sly-stickers--set-tooltip sticker label) - (sly-stickers--set-face sticker 'sly-stickers-placed-face))) - -(define-button-type 'sly-stickers--recording-part :supertype 'sly-part - 'sly-button-inspect - 'sly-stickers--inspect-recording - ;; 'sly-button-pretty-print - ;; #'(lambda (id) ...) - ;; 'sly-button-describe - ;; #'(lambda (id) ...) - ;; 'sly-button-show-source - ;; #'(lambda (id) ...) - ) - -(defun sly-stickers--recording-part (label sticker-id recording vindex - &rest props) - (apply #'sly--make-text-button - label nil - :type 'sly-stickers--recording-part - 'part-args (list sticker-id recording vindex) - 'part-label "Recorded value" - props)) - -(cl-defun sly-stickers--describe-recording-values (recording &key - (indent 0) - (prefix "=> ")) - (cl-flet ((indent (str) - (concat (make-string indent ? )str)) - (prefix (str) - (concat prefix str))) - (let ((descs (sly-stickers--recording-value-descriptions recording))) - (cond ((sly-stickers--recording-exited-non-locally-p recording) - (indent (propertize "exited non locally" 'face 'sly-action-face))) - ((null descs) - (indent (propertize "no values" 'face 'sly-action-face))) - (t - (cl-loop for (value-desc . rest) on descs - for vindex from 0 - concat - (indent (prefix - (sly-stickers--recording-part - value-desc - (sly-stickers--recording-sticker-id recording) - recording - vindex))) - when rest - concat "\n")))))) - -(defconst sly-stickers--newline "\n" - "Work around bug #63, actually Emacs bug #21839. -\"25.0.50; can't use newlines in defaults in cl functions\"") - -(cl-defun sly-stickers--pretty-describe-recording - (recording &key (separator sly-stickers--newline)) - (let* ((recording-sticker-id (sly-stickers--recording-sticker-id recording)) - (sticker (gethash recording-sticker-id - sly-stickers--stickers)) - (nvalues (length (sly-stickers--recording-value-descriptions recording)))) - (format "%s%s:%s%s" - (if sticker - (format "Sticker %s on line %s of %s" - (sly-stickers--sticker-id sticker) - (with-current-buffer (overlay-buffer sticker) - (line-number-at-pos (overlay-start sticker))) - (overlay-buffer sticker)) - (format "Deleted or unknown sticker %s" - recording-sticker-id)) - (if (cl-plusp nvalues) - (format " returned %s values" nvalues) "") - separator - (sly-stickers--describe-recording-values recording - :indent 2)))) - -(defun sly-stickers--populate-sticker (sticker recording) - (let* ((id (sly-stickers--sticker-id sticker)) - (total (sly-stickers--recording-sticker-total recording))) - (cond ((cl-plusp total) - (button-put sticker 'part-label - (format "Sticker %d has %d recordings" id total)) - (unless (sly-stickers--recording-void-p recording) - (button-put sticker 'sly-stickers--last-known-recording recording) - (button-put sticker 'part-args (list id recording)) - (sly-stickers--set-tooltip - sticker - (format "Newest of %s sticker recordings:\n%s" - total - (sly-stickers--describe-recording-values recording :prefix ""))) - (sly-stickers--set-face - sticker - (if (sly-stickers--recording-exited-non-locally-p recording) - 'sly-stickers-exited-non-locally-face - 'sly-stickers-recordings-face)))) - (t - (let ((last-known-recording - (button-get sticker 'sly-stickers--last-known-recording))) - (button-put sticker 'part-label - (format "Sticker %d has no recordings" id)) - (when last-known-recording - (sly-stickers--set-tooltip - sticker - (format "No new recordings. Last known:\n%s" - (sly-stickers--describe-recording-values - last-known-recording)))) - (sly-stickers--set-tooltip sticker "No new recordings") - (sly-stickers--set-face sticker 'sly-stickers-empty-face)))))) - -(defun sly-stickers--sticker-substickers (sticker) - (let* ((retval - (remove sticker - (sly-stickers--stickers-between (button-start sticker) - (button-end sticker)))) - ;; To verify an important invariant, and warn (don't crash) - ;; - (exactly-at - (sly-stickers--stickers-exactly-at (button-start sticker) - (button-end sticker)))) - (cond - ((remove sticker exactly-at) - (sly-warning "Something's fishy. More than one sticker at same position") - (cl-set-difference retval exactly-at)) - (t - retval)))) - -(defun sly-stickers--briefly-describe-sticker (sticker) - (let ((beg (button-start sticker)) - (end (button-end sticker))) - (if (< (- end beg) 20) - (format "sticker around %s" (buffer-substring-no-properties beg end)) - (cl-labels ((word (point direction) - (apply #'buffer-substring-no-properties - (sort (list - point - (save-excursion (goto-char point) - (forward-word direction) - (point))) - #'<)))) - (format "sticker from \"%s...\" to \"...%s\"" - (word beg 1) - (word end -1)))))) - -(defun sly-stickers--delete (sticker) - "Ensure that sticker is deleted." - ;; Delete the overlay and take care of levels for contained and - ;; containers, but note that a sticker might have no buffer anymore - ;; if that buffer was killed, for example... - ;; - (when (and (overlay-buffer sticker) - (buffer-live-p (overlay-buffer sticker))) - (mapc (lambda (s) (cl-decf (sly-stickers--level s))) - (sly-stickers--sticker-substickers sticker)) - (delete-overlay sticker)) - ;; We also want to deregister it from the hashtable in case it's - ;; there (it's not there if it has never been armed) - ;; - (let ((id (sly-stickers--sticker-id sticker))) - (when (gethash (sly-stickers--sticker-id sticker) - sly-stickers--stickers) - (remhash id sly-stickers--stickers) - (add-to-list 'sly-stickers--zombie-sticker-ids id)))) - -(defun sly-stickers--sticker-modified (sticker _after? beg end - &optional _pre-change-len) - (unless (save-excursion - (goto-char beg) - (skip-chars-forward "\t\n\s") - (>= (point) end)) - (let ((inhibit-modification-hooks t)) - (sly-message "Deleting %s" - (sly-stickers--briefly-describe-sticker sticker)) - (sly-stickers--delete sticker)))) - -(defun sly-stickers-next-sticker (&optional n) - (interactive "p") - (sly-button-search n 'sly-stickers--sticker-id)) - -(defun sly-stickers-prev-sticker (&optional n) - (interactive "p") - (sly-button-search (- n) 'sly-stickers--sticker-id)) - -(put 'sly-stickers-next-sticker 'sly-button-navigation-command t) -(put 'sly-stickers-prev-sticker 'sly-button-navigation-command t) - -(defun sly-stickers-clear-defun-stickers () - "Clear all stickers in the current top-level form." - (interactive) - (let* ((region (sly-region-for-defun-at-point))) - (sly-stickers-clear-region-stickers (car region) (cadr region)))) - -(defun sly-stickers-clear-buffer-stickers () - "Clear all the stickers in the current buffer." - (interactive) - (sly-stickers-clear-region-stickers (point-min) (point-max))) - -(defun sly-stickers-clear-region-stickers (&optional from to) - "Clear all the stickers between FROM and TO." - (interactive "r") - (let* ((from (or from (region-beginning))) - (to (or to (region-end))) - (stickers (sly-stickers--stickers-in from to))) - (cond (stickers - (mapc #'sly-stickers--delete stickers) - (sly-message "%s stickers cleared" (length stickers))) - (t - (sly-message "no stickers to clear"))))) - -(defun sly-stickers-delete-sticker-at-point (&optional point) - "Delete the topmost sticker at point." - (interactive "d") - (let ((stickers (sly-stickers--stickers-at (or point (point))))) - (cond - (stickers - (sly-stickers--delete (car stickers)) - (if (cdr stickers) - (sly-message "Deleted topmost sticker (%d remain at point)" - (length (cdr stickers))) - (sly-message "Deleted sticker %s" - (sly-stickers--briefly-describe-sticker (car stickers))))) - (t - (sly-user-error "No stickers at point"))))) - -(defun sly-stickers-maybe-add-sticker (&optional point) - "Add of remove a sticker at POINT. -If point is currently at a sticker boundary, delete that sticker, -otherwise, add a sticker to the sexp at point." - (interactive "d") - (save-excursion - (goto-char (or point (point))) - (let* ((bounds (sly-bounds-of-sexp-at-point)) - (beg (car bounds)) - (end (cdr bounds)) - (matching (and bounds - (sly-stickers--stickers-exactly-at beg end)))) - (cond - ((not bounds) - (sly-message "Nothing here to place sticker on, apparently")) - (matching - (sly-stickers--delete (car matching)) - (sly-message "Deleted sticker")) - (t - (let ((sticker (sly-stickers--sticker beg end))) - (sly-message "Added %s" - (sly-stickers--briefly-describe-sticker sticker)))))))) - -(defun sly-stickers-dwim (prefix) - "Set or remove stickers at point. -Set a sticker for the current sexp at point, or delete it if it -already exists. - -If the region is active set a sticker in the current region. - -With interactive prefix arg PREFIX always delete stickers. - -- One C-u means delete the current top-level form's stickers. -- Two C-u's means delete the current buffer's stickers" - (interactive "p") - (cond - ((= prefix 4) - (if (region-active-p) - (sly-stickers-clear-region-stickers) - (sly-stickers-clear-defun-stickers))) - ((>= prefix 16) - (sly-stickers-clear-buffer-stickers)) - ((region-active-p) - (sly-stickers--sticker (region-beginning) (region-end)) - (deactivate-mark t)) - ((not (sly-inside-string-or-comment-p)) - (sly-stickers-maybe-add-sticker)) - (t - (sly-message "No point placing stickers in string literals or comments")))) - -(defun sly-stickers--sticker-by-id (sticker-id) - "Return the sticker for STICKER-ID, or return NIL. -Perform some housecleaning tasks for stickers that have been -properly deleted or brutally killed with the buffer they were in." - (let* ((sticker (gethash sticker-id sly-stickers--stickers))) - (cond ((and sticker (overlay-buffer sticker) - (buffer-live-p (overlay-buffer sticker))) - sticker) - (sticker - ;; `sticker-id' references a sticker that hasn't been - ;; deleted but whose overlay can't be found. One reason for - ;; this is that the buffer it existed in was killed. So - ;; delete it now and mark it a zombie. - (sly-stickers--delete sticker) - nil) - (t - ;; The sticker isn't in the `sly-stickers--stickers' hash - ;; table, so it has probably already been marked zombie, - ;; and possibly already deleted. We're probably just seeing - ;; it because recording playback and breaking stickers may - ;; not filtering these out by user option. - ;; - ;; To be on the safe side, add the id to the table anyway, - ;; so it'll get killed on the Slynk side on the next - ;; request. - ;; - (add-to-list 'sly-stickers--zombie-sticker-ids sticker-id) - nil)))) - -(defvar sly-stickers--flashing-sticker nil - "The sticker currently being flashed.") - -(cl-defun sly-stickers--find-and-flash (sticker-id &key (otherwise nil)) - "Find and flash the sticker referenced by STICKER-ID. -otherwise call OTHERWISE with a single argument, a string stating -the reason why the sticker couldn't be found" - (let ((sticker (sly-stickers--sticker-by-id sticker-id))) - (cond (sticker - (let ((buffer (overlay-buffer sticker))) - (when buffer - (with-current-buffer buffer - (let* ((window (display-buffer buffer t))) - (when window - (with-selected-window window - (push-mark nil t) - (goto-char (overlay-start sticker)) - (sly-recenter (point)) - (setq sly-stickers--flashing-sticker sticker) - (pulse-momentary-highlight-overlay sticker 'highlight) - (run-with-timer - 2 nil - (lambda () - (when (eq sly-stickers--flashing-sticker sticker) - (pulse-momentary-highlight-overlay - sticker 'highlight))))))))))) - (otherwise - (funcall otherwise "Can't find sticker (probably deleted!)"))))) - -;; Work around an Emacs bug, probably won't be needed in Emacs 27.1 -(advice-add 'pulse-momentary-unhighlight - :before (lambda (&rest _args) - (let ((o pulse-momentary-overlay)) - (when (and o (overlay-get o 'sly-stickers-id)) - (overlay-put o 'priority nil)))) - '((name . fix-pulse-momentary-unhighlight-bug))) - - -;;;; Recordings -;;;; -(cl-defstruct (sly-stickers--recording - (:constructor sly-stickers--make-recording-1) - (:conc-name sly-stickers--recording-) - (:copier sly-stickers--copy-recording)) - (sticker-id nil) - (sticker-total nil) - (id nil) - (value-descriptions nil) - (exited-non-locally-p nil) - (sly-connection nil)) - -(defun sly-stickers--recording-void-p (recording) - (not (sly-stickers--recording-id recording))) - -(defun sly-stickers--make-recording (description) - "Make a `sly-stickers--recording' from DESCRIPTION. -A DESCRIPTION is how the Lisp side describes a sticker and -usually its most recent recording. If it doesn't, a recording -veryfying `sly-stickers--recording-void-p' is created." - (cl-destructuring-bind (sticker-id sticker-total . recording-description) - description - (let ((recording (sly-stickers--make-recording-1 - :sticker-id sticker-id - :sticker-total sticker-total - :sly-connection (sly-current-connection)))) - (when recording-description - (cl-destructuring-bind (recording-id _recording-ctime - value-descriptions - exited-non-locally-p) - recording-description - (setf - (sly-stickers--recording-id recording) - recording-id - (sly-stickers--recording-value-descriptions recording) - value-descriptions - (sly-stickers--recording-exited-non-locally-p recording) - exited-non-locally-p))) - recording))) - - -;;;; Replaying sticker recordings -;;;; -(defvar sly-stickers--replay-help nil) - -(defvar sly-stickers--replay-mode-map - (let ((map (make-sparse-keymap))) - (cl-flet - ((def - (key binding &optional desc) - (define-key map (kbd key) binding) - (setf - (cl-getf sly-stickers--replay-help binding) - (cons (cons key (car (cl-getf sly-stickers--replay-help binding))) - (or desc - (cdr (cl-getf sly-stickers--replay-help binding))))))) - (def "n" 'sly-stickers-replay-next - "Scan recordings forward") - (def "SPC" 'sly-stickers-replay-next) - (def "N" 'sly-stickers-replay-next-for-sticker - "Scan recordings forward for this sticker") - (def "DEL" 'sly-stickers-replay-prev - "Scan recordings backward") - (def "p" 'sly-stickers-replay-prev) - (def "P" 'sly-stickers-replay-prev-for-sticker - "Scan recordings backward for this sticker") - (def "j" 'sly-stickers-replay-jump - "Jump to a recording") - (def ">" 'sly-stickers-replay-jump-to-end - "Go to last recording") - (def "<" 'sly-stickers-replay-jump-to-beginning - "Go to first recording") - (def "h" 'sly-stickers-replay-toggle-help - "Toggle help") - (def "v" 'sly-stickers-replay-pop-to-current-sticker - "Pop to current sticker") - (def "V" 'sly-stickers-replay-toggle-pop-to-stickers - "Toggle popping to stickers") - (def "q" 'quit-window - "Quit") - (def "x" 'sly-stickers-replay-toggle-ignore-sticker - "Toggle ignoring a sticker") - (def "z" 'sly-stickers-replay-toggle-ignore-zombies - "Toggle ignoring deleted stickers") - (def "R" 'sly-stickers-replay-reset-ignore-list - "Reset ignore list") - (def "F" 'sly-stickers-forget - "Forget about sticker recordings") - (def "g" 'sly-stickers-replay-refresh - "Refresh current recording") - map))) - -(define-derived-mode sly-stickers--replay-mode fundamental-mode - "SLY Stickers Replay" "Mode for controlling sticker replay sessions Dialog" - (set-syntax-table lisp-mode-syntax-table) - (read-only-mode 1) - (sly-mode 1) - (add-hook 'post-command-hook - 'sly-stickers--replay-postch t t)) - -(defun sly-stickers--replay-postch () - (let ((win (get-buffer-window (current-buffer)))) - (when (and win - (window-live-p win)) - (ignore-errors - (set-window-text-height win (line-number-at-pos (point-max))))))) - -(defvar sly-stickers--replay-expanded-help nil) - -(defun sly-stickers-replay-toggle-help () - (interactive) - (set (make-local-variable 'sly-stickers--replay-expanded-help) - (not sly-stickers--replay-expanded-help)) - (sly-stickers--replay-refresh-1)) - -(sly-def-connection-var sly-stickers--replay-data nil - "Data structure for information related to recordings") - -(defvar sly-stickers--replay-key nil - "A symbol identifying a particular replaying session in the - Slynk server.") - -(defvar sly-stickers--replay-pop-to-stickers t) - -(defun sly-stickers--replay-refresh-1 () - "Insert a description of the current recording into the current -buffer" - (cl-assert (eq major-mode 'sly-stickers--replay-mode) - nil - "%s must be run in a stickers replay buffer" - this-command) - (cl-labels - ((paragraph () (if sly-stickers--replay-expanded-help "\n\n" "\n")) - (describe-ignored-stickers - () - (let ((ignored-ids (cl-getf (sly-stickers--replay-data) - :ignored-ids)) - (ignore-zombies-p (cl-getf (sly-stickers--replay-data) - :ignore-zombies-p))) - (if (or ignored-ids ignore-zombies-p) - (format "%s%s%s" - (paragraph) - (if ignore-zombies-p - "Skipping recordings of deleted stickers. " "") - (if ignored-ids - (format "Skipping recordings of sticker%s %s." - (if (cl-rest ignored-ids) "s" "") - (concat (mapconcat #'pp-to-string - (butlast ignored-ids) - ", ") - (and (cl-rest ignored-ids) " and ") - (pp-to-string - (car (last ignored-ids))))) - "")) - ""))) - (describe-help - () - (format "%s%s" - (paragraph) - (if sly-stickers--replay-expanded-help - (substitute-command-keys "\\{sly-stickers--replay-mode-map}") - "n => next, p => previous, x => ignore, h => help, q => quit"))) - (describe-number-of-recordings - (new-total) - (let* ((old-total (cl-getf (sly-stickers--replay-data) :old-total)) - (diff (and old-total (- new-total old-total)))) - (format "%s total recordings%s" - new-total - (cond ((and diff - (cl-plusp diff)) - (propertize (format ", %s new in the meantime" - diff) - 'face 'bold)) - (t - ""))))) - (describe-playhead - (recording) - (let ((new-total (cl-getf (sly-stickers--replay-data) :total)) - (index (cl-getf (sly-stickers--replay-data) :index))) - (cond - ((and new-total - recording) - (format "Playhead at recording %s of %s" - (ignore-errors (1+ index)) - (describe-number-of-recordings new-total))) - (new-total - (format "Playhead detached (ignoring too many stickers?) on %s" - (describe-number-of-recordings new-total))) - (recording - (substitute-command-keys - "Playhead confused (perhaps hit \\[sly-stickers-replay-refresh])")) - (t - (format - "No recordings! Perhaps you need to run some sticker-aware code first")))))) - (sly-refreshing () - (let ((rec (cl-getf (sly-stickers--replay-data) :recording))) - (insert (describe-playhead rec) (paragraph)) - (when rec - (insert (sly-stickers--pretty-describe-recording - rec - :separator (paragraph))))) - (insert (describe-ignored-stickers)) - (insert (describe-help))))) - -(defun sly-stickers-replay () - "Navigate to WHATth next sticker recording fetched from Slynk" - (interactive) - (let* ((buffer-name (sly-buffer-name :stickers-replay - :connection (sly-current-connection))) - (existing-buffer (get-buffer buffer-name))) - (let ((split-width-threshold nil) - (split-height-threshold 0)) - (sly-with-popup-buffer (buffer-name - :mode 'sly-stickers--replay-mode - :select t) - (setq existing-buffer standard-output))) - (with-current-buffer existing-buffer - (setf (cl-getf (sly-stickers--replay-data) :replay-key) - (cl-gensym "stickers-replay-")) - (let ((old-total (cl-getf (sly-stickers--replay-data) :total)) - (new-total (sly-eval '(slynk-stickers:total-recordings)))) - (setf (cl-getf (sly-stickers--replay-data) :old-total) old-total) - (when (and - old-total - (cl-plusp old-total) - (> new-total old-total) - (sly-y-or-n-p - "Looks like there are %s new recordings since last replay.\n -Forget about old ones before continuing?" (- new-total old-total))) - (sly-stickers-forget old-total))) - - (sly-stickers-replay-refresh 0 - (if existing-buffer nil t) - t) - (set-window-dedicated-p nil 'soft) - (with-current-buffer existing-buffer - (sly-stickers--replay-postch))))) - -(defun sly-stickers-replay-refresh (n command &optional interactive) - "Refresh the current sticker replay session. -N and COMMAND are passed to the Slynk server and instruct what -recording to fetch: - -If COMMAND is nil, navigate to Nth next sticker recording, -skipping ignored stickers. - -If COMMAND is a number, navigate to the Nth next sticker -recording for the sticker with that numeric sticker id. - -If COMMAND is any other value, jump directly to the recording -index N. - -Interactively, N is 0 and and COMMAND is nil, meaning that the -playhead should stay put and the buffer should be refreshed. - -Non-interactively signal an error if no recording was fetched and -INTERACTIVE is the symbol `sly-error'. - -Non-interactively, set the `:recording' slot of -`sly-stickers--replay-data' to nil if no recording was fetched." - (interactive (list 0 nil t)) - (let ((result (sly-eval - `(slynk-stickers:search-for-recording - ',(cl-getf (sly-stickers--replay-data) :replay-key) - ',(cl-getf (sly-stickers--replay-data) :ignored-ids) - ',(cl-getf (sly-stickers--replay-data) :ignore-zombies-p) - ',(sly-stickers--zombies) - ,n - ',command)))) - ;; presumably, Slynk cleaned up the zombies we passed it. - ;; - (sly-stickers--reset-zombies) - (cond ((car result) - (cl-destructuring-bind (total index &rest sticker-description) - result - (let ((rec (sly-stickers--make-recording sticker-description)) - (old-index (cl-getf (sly-stickers--replay-data) :index))) - (setf (cl-getf (sly-stickers--replay-data) :index) index - (cl-getf (sly-stickers--replay-data) :total) total - (cl-getf (sly-stickers--replay-data) :recording) rec) - (if old-index - (if (cl-plusp n) - (if (> old-index index) (sly-message "Rolled over to start")) - (if (< old-index index) (sly-message "Rolled over to end")))) - ;; Assert that the recording isn't void - ;; - (when (sly-stickers--recording-void-p rec) - (sly-error "Attempt to visit a void recording described by %s" - sticker-description)) - (when sly-stickers--replay-pop-to-stickers - (sly-stickers--find-and-flash - (sly-stickers--recording-sticker-id rec)))))) - (interactive - ;; If we were called interactively and got an error, it's - ;; probably because there aren't any recordings, so reset - ;; data - ;; - (setf (sly-stickers--replay-data) nil) - (when (eq interactive 'sly-error) - (sly-error "%s for %s reported an error: %s" - 'slynk-stickers:search-for-recording - n - (cadr result))) - (setf (cl-getf (sly-stickers--replay-data) :recording) nil))) - (if interactive - (sly-stickers--replay-refresh-1) - (cl-getf (sly-stickers--replay-data) :recording )))) - -(defun sly-stickers-replay-next (n) - "Navigate to Nth next sticker recording, skipping ignored stickers" - (interactive "p") - (sly-stickers-replay-refresh n nil 'sly-error)) - -(defun sly-stickers-replay-prev (n) - "Navigate to Nth prev sticker recording, skipping ignored stickers" - (interactive "p") - (sly-stickers-replay-refresh (- n) nil 'sly-error)) - -(defun sly-stickers-replay--current-sticker-interactive (prompt) - (if current-prefix-arg - (read-number (format "[sly] %s " prompt)) - (sly-stickers--recording-sticker-id - (cl-getf (sly-stickers--replay-data) :recording)))) - -(defun sly-stickers-replay-next-for-sticker (n sticker-id) - "Navigate to Nth next sticker recording for STICKER-ID" - (interactive (list - (if (numberp current-prefix-arg) - current-prefix-arg - 1) - (sly-stickers-replay--current-sticker-interactive - "Which sticker?"))) - (sly-stickers-replay-refresh n sticker-id 'sly-error)) - -(defun sly-stickers-replay-prev-for-sticker (n sticker-id) - "Navigate to Nth prev sticker recording for STICKER-ID" - (interactive (list - (- (if (numberp current-prefix-arg) - current-prefix-arg - 1)) - (sly-stickers-replay--current-sticker-interactive - "Which sticker?"))) - (sly-stickers-replay-refresh n sticker-id 'sly-error)) - -(defun sly-stickers-replay-jump (n) - "Fetch and jump to Nth sticker recording" - (interactive (read-number "[sly] jump to which recording? ")) - (sly-stickers-replay-refresh n 'absolute-p 'sly-error)) - -(defun sly-stickers-replay-jump-to-beginning () - "Fetch and jump to the first sticker recording" - (interactive) - (sly-stickers-replay-refresh 0 'absolute-p 'sly-error)) - -(defun sly-stickers-replay-jump-to-end () - "Fetch and jump to the last sticker recording" - (interactive) - (sly-stickers-replay-refresh -1 'absolute-p 'sly-error)) - -(defun sly-stickers-replay-toggle-ignore-sticker (sticker-id) - "Toggle ignoring recordings of sticker with STICKER-ID" - (interactive (list - (sly-stickers-replay--current-sticker-interactive - "Toggle ignoring which sticker id?"))) - (let* ((ignored (cl-getf (sly-stickers--replay-data) :ignored-ids)) - (ignored-p (memq sticker-id ignored))) - (cond (ignored-p - (setf (cl-getf (sly-stickers--replay-data) :ignored-ids) - (delq sticker-id (cdr ignored))) - (sly-message "No longer ignoring sticker %s" sticker-id)) - (t - (setf (cl-getf (sly-stickers--replay-data) :ignored-ids) - (delete-dups ; stupid but safe - (cons sticker-id ignored))) - (sly-message "Now ignoring sticker %s" sticker-id))) - (sly-stickers-replay-refresh (if ignored-p ; was ignored, now isn't - 0 - 1) - nil - t))) - -(defun sly-stickers-replay-toggle-ignore-zombies () - "Toggle ignoring recordings of zombie stickers." - (interactive) - (let ((switch - (setf - (cl-getf (sly-stickers--replay-data) :ignore-zombies-p) - (not (cl-getf (sly-stickers--replay-data) :ignore-zombies-p))))) - (if switch - (sly-message "Now ignoring zombie stickers") - (sly-message "No longer ignoring zombie stickers"))) - (sly-stickers-replay-refresh 0 nil t)) - -(defun sly-stickers-replay-pop-to-current-sticker (sticker-id) - "Pop to sticker with STICKER-ID" - (interactive (list - (sly-stickers-replay--current-sticker-interactive - "Pop to which sticker id?"))) - (sly-stickers--find-and-flash sticker-id - :otherwise #'sly-error)) - -(defun sly-stickers-replay-toggle-pop-to-stickers () - "Toggle popping to stickers when replaying sticker recordings." - (interactive) - (set (make-local-variable 'sly-stickers--replay-pop-to-stickers) - (not sly-stickers--replay-pop-to-stickers)) - (if sly-stickers--replay-pop-to-stickers - (sly-message "Auto-popping to stickers ON") - (sly-message "Auto-popping to stickers OFF"))) - -(defun sly-stickers-replay-reset-ignore-list () - "Reset the sticker ignore specs" - (interactive) - (setf (cl-getf (sly-stickers--replay-data) :ignored-ids) nil) - (sly-stickers-replay-refresh 0 nil t)) - -(defun sly-stickers-fetch () - "Fetch recordings from Slynk and update stickers accordingly. -See also `sly-stickers-replay'." - (interactive) - (sly-eval-async `(slynk-stickers:fetch ',(sly-stickers--zombies)) - #'(lambda (result) - (sly-stickers--reset-zombies) - (let ((message - (format "Fetched recordings for %s armed stickers" - (length result)))) - (cl-loop for sticker-description in result - ;; Although we are analysing sticker descriptions - ;; here, recordings are made to pass to - ;; `sly-stickers--sticker-by-id', even if they are - ;; are `sly-stickers--recording-void-p', which is - ;; the case if the sticker has never been - ;; traversed. - ;; - for recording = - (sly-stickers--make-recording sticker-description) - for sticker = - (sly-stickers--sticker-by-id - (sly-stickers--recording-sticker-id recording)) - when sticker - do (sly-stickers--populate-sticker sticker recording)) - (sly-message message))) - "CL_USER")) - -(defun sly-stickers-forget (&optional howmany interactive) - "Forget about sticker recordings in the Slynk side. -If HOWMANY is non-nil it must be a number stating how many -recordings to forget about. In this cases Because 0 is an index, -in the `nth' sense, the HOWMANYth recording survives." - (interactive (list (and (numberp current-prefix-arg) - current-prefix-arg) - t)) - (when (or (not interactive) - (sly-y-or-n-p "Really forget about sticker recordings?")) - (sly-eval `(slynk-stickers:forget ',(sly-stickers--zombies) ,howmany)) - (sly-stickers--reset-zombies) - (setf (cl-getf (sly-stickers--replay-data) :rec) nil - (cl-getf (sly-stickers--replay-data) :old-total) nil) - (when interactive - (sly-message "Forgot all about sticker recordings.")) - (when (eq major-mode 'sly-stickers--replay-mode) - (sly-stickers-replay-refresh 0 t t)))) - - -;;;; Breaking stickers -(defun sly-stickers--handle-break (extra) - (sly-dcase extra - ((:slynk-after-sticker description) - (let ((sticker-id (cl-first description)) - (recording (sly-stickers--make-recording description))) - (sly-stickers--find-and-flash sticker-id - :otherwise 'sly-message) - (insert - "\n\n" - (sly-stickers--pretty-describe-recording recording - )))) - ((:slynk-before-sticker sticker-id) - (sly-stickers--find-and-flash sticker-id - :otherwise 'sly-message)) - (;; don't do anything if we don't know this "extra" info - t - nil))) - - -(defun sly-stickers-toggle-break-on-stickers () - (interactive) - (let ((break-p (sly-eval '(slynk-stickers:toggle-break-on-stickers)))) - (sly-message "Breaking on stickers is %s" (if break-p "ON" "OFF")))) - - -;;;; Functions for examining recordings -;;;; - - -(eval-after-load "sly-mrepl" - `(progn - (button-type-put 'sly-stickers-sticker - 'sly-mrepl-copy-part-to-repl - 'sly-stickers--copy-recording-to-repl) - (button-type-put 'sly-stickers--recording-part - 'sly-mrepl-copy-part-to-repl - 'sly-stickers--copy-recording-to-repl))) - - -;;; shoosh byte-compiler -(declare-function sly-mrepl--save-and-copy-for-repl nil) -(cl-defun sly-stickers--copy-recording-to-repl - (_sticker-id recording &optional (vindex 0)) - (check-recording recording) - (sly-mrepl--save-and-copy-for-repl - `(slynk-stickers:find-recording-or-lose - ,(sly-stickers--recording-id recording) - ,vindex) - :before (format "Returning values of recording %s of sticker %s" - (sly-stickers--recording-id recording) - (sly-stickers--recording-sticker-id recording)))) - -(defun check-recording (recording) - (cond ((null recording) - (sly-error "This sticker doesn't seem to have any recordings")) - ((not (eq (sly-stickers--recording-sly-connection recording) - (sly-current-connection))) - (sly-error "Recording is for a different connection (%s)" - (sly-connection-name - (sly-stickers--recording-sly-connection recording)))))) - -(cl-defun sly-stickers--inspect-recording - (_sticker-id recording &optional (vindex 0)) - (check-recording recording) - (sly-eval-for-inspector - `(slynk-stickers:inspect-sticker-recording - ,(sly-stickers--recording-id recording) - ,vindex))) - -;;;; Sticker-aware compilation -;;;; - -(cl-defun sly-stickers--compile-region-aware-of-stickers-1 - (start end callback &key sync fallback flash) - "Compile from START to END considering stickers. -After compilation call CALLBACK with the stickers and the -compilation result. If SYNC, use `sly-eval' other wise use -`sly-eval-async'. If FALLBACK, send the uninstrumneted region as -a fallback. If FLASH, flash the compiled region." - (let* ((uninstrumented (buffer-substring-no-properties start end)) - (stickers (sly-stickers--stickers-between start end)) - (original-buffer (current-buffer))) - (cond (stickers - (when flash - (sly-flash-region start end :face 'sly-stickers-armed-face)) - (sly-with-popup-buffer ((sly-buffer-name :stickers :hidden t) - :select :hidden) - (mapc #'delete-overlay (overlays-in (point-min) (point-max))) - (insert uninstrumented) - ;; Use a second set of overlays placed just in the - ;; pre-compilation buffer. We need this to correctly keep - ;; track of the markers because in this buffer we are going - ;; to change actual text - ;; - (cl-loop for sticker in stickers - for overlay = - (make-overlay (- (button-start sticker) (1- start)) - (- (button-end sticker) (1- start))) - do (overlay-put overlay 'sly-stickers--sticker sticker)) - (cl-loop for overlay in (overlays-in (point-min) (point-max)) - for sticker = (overlay-get overlay 'sly-stickers--sticker) - do - (sly-stickers--arm-sticker sticker) - (goto-char (overlay-start overlay)) - (insert (format "(slynk-stickers:record %d " - (sly-stickers--sticker-id sticker))) - (goto-char (overlay-end overlay)) - (insert ")")) - ;; Now send both the instrumented and uninstrumented - ;; string to the Lisp - ;; - (let ((instrumented (buffer-substring-no-properties (point-min) - (point-max))) - (new-ids (mapcar #'sly-stickers--sticker-id stickers))) - (with-current-buffer original-buffer - (let ((form `(slynk-stickers:compile-for-stickers - ',new-ids - ',(sly-stickers--zombies) - ,instrumented - ,(when fallback uninstrumented) - ,(buffer-name) - ',(sly-compilation-position start) - ,(if (buffer-file-name) - (sly-to-lisp-filename (buffer-file-name))) - ',sly-compilation-policy))) - (cond (sync - (funcall callback - stickers - (sly-eval form)) - (sly-stickers--reset-zombies)) - (t (sly-eval-async form - (lambda (result) - (sly-stickers--reset-zombies) - (funcall callback stickers result)))))))))) - (t - (sly-compile-region-as-string start end))))) - -(defun sly-stickers-compile-region-aware-of-stickers (start end) - "Compile region from START to END aware of stickers. -Intended to be placed in `sly-compile-region-function'" - (sly-stickers--compile-region-aware-of-stickers-1 - start end - (lambda (stickers result-and-stuck-p) - (cl-destructuring-bind (result &optional stuck-p) - result-and-stuck-p - (unless stuck-p - (mapc #'sly-stickers--disarm-sticker stickers)) - (sly-compilation-finished - result - nil - (if stuck-p - (format " (%d stickers armed)" (length stickers)) - " (stickers failed to stick)")))) - :fallback t - :flash t)) - -(defun sly-stickers-after-buffer-compilation (success _notes buffer loadp) - "After compilation, compile regions with stickers. -Intented to be placed in `sly-compilation-finished-hook'" - (when (and buffer loadp success) - (save-restriction - (widen) - (let* ((all-stickers (sly-stickers--stickers-between - (point-min) (point-max))) - (regions (cl-loop for sticker in all-stickers - for region = (sly-region-for-defun-at-point - (overlay-start sticker)) - unless (member region regions) - collect region into regions - finally (cl-return regions)))) - (when regions - (cl-loop - with successful - with unsuccessful - for region in regions - do - (sly-stickers--compile-region-aware-of-stickers-1 - (car region) (cadr region) - (lambda (stickers result) - (cond (result - (push (cons region stickers) successful)) - (t - (mapc #'sly-stickers--disarm-sticker stickers) - (push (cons region stickers) unsuccessful)))) - :sync t) - finally - (sly-temp-message - 3 3 - "%s stickers stuck in %s regions, %s disarmed in %s regions" - (cl-reduce #'+ successful :key (lambda (x) (length (cdr x)))) - (length successful) - (cl-reduce #'+ unsuccessful :key (lambda (x) (length (cdr x)))) - (length unsuccessful)))))))) - - -;;;; Menu -;;;; - -(easy-menu-define sly-stickers--shortcut-menu nil - "Placing stickers in `lisp-mode' buffers." - (let* ((in-source-file 'sly-stickers-mode) - (connected '(sly-connected-p))) - `("Stickers" - ["Add or remove sticker at point" - sly-stickers-dwim ,in-source-file] - ["Delete stickers from top-level form" - sly-stickers-clear-defun-stickers ,in-source-file] - ["Delete stickers from buffer" - sly-stickers-clear-buffer-stickers ,in-source-file] - "--" - ["Start sticker recording replay" - sly-stickers-replay ,connected] - ["Fetch most recent recordings" - sly-stickers-fetch ,connected] - ["Toggle breaking on stickers" - sly-stickers-toggle-break-on-stickers ,connected]))) - -(easy-menu-add-item sly-menu nil sly-stickers--shortcut-menu "Documentation") - -(provide 'sly-stickers) -;;; sly-stickers.el ends here - diff --git a/elpa/sly-1.0.43/contrib/slynk-mrepl.lisp b/elpa/sly-1.0.43/contrib/slynk-mrepl.lisp @@ -1,747 +0,0 @@ -;;; slynk-mrepl.lisp -;; -;; Licence: public domain - -(defpackage :slynk-mrepl - (:use :cl :slynk-api) - (:import-from :slynk - #:*globally-redirect-io* - #:*use-dedicated-output-stream* - #:*dedicated-output-stream-port* - #:*dedicated-output-stream-buffering*) - (:export #:create-mrepl - #:globally-save-object - #:eval-for-mrepl - #:sync-package-and-default-directory - #:pprint-entry - #:inspect-entry - #:guess-and-set-package - #:copy-to-repl - #:describe-entry - #:send-prompt - #:copy-to-repl-in-emacs)) -(in-package :slynk-mrepl) - - -;;; MREPL models -(defclass mrepl (channel listener) - ((remote-id :initarg :remote-id :accessor mrepl-remote-id) - (mode :initform :eval :accessor mrepl-mode) - (pending-errors :initform nil :accessor mrepl-pending-errors)) - (:documentation "A listener implemented in terms of a channel.") - (:default-initargs - :initial-env `((cl:*package* . ,(find-package :COMMON-LISP-USER)) - (*) (**) (***) - (/) (//) (///) - (+) (++) (+++) - (*history* . ,(make-array 40 :fill-pointer 0 - :adjustable t))))) - -(defmethod print-object ((r mrepl) stream) - (print-unreadable-object (r stream :type t) - (format stream "mrepl-~a-~a" (channel-id r) (mrepl-remote-id r)))) - -(defmethod initialize-instance :before ((r mrepl) &key) - (setf (slot-value r 'slynk::in) (make-mrepl-input-stream r))) - - -;;; Helpers -;;; -(defvar *history* nil) - -(defvar *saved-objects* nil) - -(defmethod slynk::drop-unprocessed-events ((r mrepl)) - "Empty REPL of events, then send prompt to Emacs." - ;; FIXME: Dropping events should be moved to the library, and this - ;; :DROP nonsense dropped, hence the deliberate SLYNK::. - (with-slots (mode) r - (let ((old-mode mode)) - (setf mode :drop) - (unwind-protect - (process-requests t) - (setf mode old-mode))))) - -(defun mrepl-get-history-entry (entry-idx) - (let ((len (length *history*))) - (assert (and entry-idx - (integerp entry-idx) - (< -1 entry-idx len)) - nil - "Illegal history entry ~a for ~a-long history" - entry-idx - len) - (aref *history* entry-idx))) - -(defun mrepl-get-object-from-history (entry-idx &optional value-idx) - (let* ((entry (mrepl-get-history-entry entry-idx)) - (len (length entry))) - (assert (or (not value-idx) - (and (integerp value-idx) - (< -1 value-idx len))) - nil - "History entry ~a is only ~a elements long." - entry-idx - len - value-idx) - (if (numberp value-idx) - (nth value-idx entry) - (values-list entry)))) - -(defparameter *backreference-character* #\v - "Character used for #v<entry>:<value> backreferences in the REPL. -Set this to some other value if it conflicts with some other reader -macro that you wish to use in the REPL. -Set this to NIL to turn this feature off.") - -(defun backreference-reader (stream subchar arg) - "Reads #rfoo:bar into (MREPL-GET-OBJECT-FROM-HISTORY foo bar)." - (declare (ignore subchar arg)) - (let* ((*readtable* - (let ((table (copy-readtable nil))) - (set-macro-character #\: (lambda (&rest args) nil) nil table) - table)) - (entry-idx - (progn - (when (eq #\: (peek-char nil stream nil nil)) - (error 'reader-error - :stream stream - :format-control "~a found in unexpected place in ~a" - :format-arguments `(#\: backreference-reader))) - (read-preserving-whitespace stream))) - (value-idx (progn - (and (eq #\: (peek-char nil stream nil nil)) - (read-char stream) - (read stream))))) - `(mrepl-get-object-from-history - ,entry-idx ,value-idx))) - -#+nil -(defun backreference-reader-tests () - (let ((expectations - '(("#v:something" error) - ("#vnotanumber:something" (notanumber something)) - ("#vnotanumber" (notanumber nil)) - ("#v2 :something" (2 nil) :something) - ("#v2:99 :something-else" (2 99) :something-else))) - (*readtable* (let ((table (copy-readtable))) - (if *backreference-character* - (set-dispatch-macro-character - #\# - *backreference-character* - #'backreference-reader table)) - table))) - (loop for (input expected-spec following) in expectations - collect - (handler-case - (progn - (with-input-from-string (s input) - (let* ((observed (read s)) - (expected - (progn - (if (eq 'error expected-spec ) - (error "oops, ~a was supposed to have errored, but returned ~a" - input observed)) - `(mrepl-get-object-from-history ,@expected-spec))) - (observed-second (and following - (read s)))) - (unless (equal observed expected) - (error "oops, ~a was supposed to have returned ~a, but returned ~a" - input expected observed)) - (unless (equal observed-second following) - (error "oops, ~a was have read ~a after, but read ~a" - input following observed-second)) - (list observed observed-second)))) - (reader-error (e) - (unless (eq 'error expected-spec) - (error "oops, ~a wasn't supposed to error with ~a" input e))))))) - -(defun make-results (objects) - (loop for value in objects - collect (list (present-for-emacs value #'slynk-pprint) - (1- (length *history*)) - (cond ((symbolp value) - (with-output-to-string (s) - (unless (keywordp value) (princ "'" s)) - (write value :stream s :case :downcase))) - ((numberp value) - (princ-to-string value)))))) - -(defun mrepl-eval (repl string) - (let ((aborted t) - (results) - (error-prompt-sent)) - (setf (mrepl-mode repl) :busy) - (unwind-protect - (let* ((previous-hook *debugger-hook*) - (*debugger-hook* - ;; Here's how this debugger hook handles "debugger - ;; levels". - ;; - ;; (1) This very lambda may be called multiple - ;; times, but *not recursively, for the same - ;; MREPL-EVAL call. That is becasue because SLY's - ;; top-level debugger hook enters a blocking - ;; SLY-DB-LOOP, and letting users invoke all manners - ;; of restarts established in the code they wants us - ;; to evaluate. It's important that we mark the - ;; condition that led to the debugger only once, in - ;; the ERRORRED var. On that occasion, we also send - ;; a prompt to the REPL and increase the debugger - ;; level. If the user selects a restart that - ;; re-runs (but *not* recursively) this very lambda, - ;; we do *not* want to send a prompt again. - ;; - ;; (2) This lambda may also run multiple times, but - ;; recursively, in the very special case of nested - ;; MREPL-EVAL may be nested (if the program calls - ;; PROCESS-REQUESTS explicitly e.g.). We - ;; (hackishly) detect this case by checking by - ;; checking the car of MREPL-PENDING-ERRORS. In - ;; that case, we are sure that calling previous hook - ;; (which is a different copy of this very lambda - ;; but running in a different stack frame) will take - ;; care of the prompt sending and error management - ;; for us, so we just do that. - (lambda (condition hook) - (setq aborted condition) - (cond ((eq condition (car (mrepl-pending-errors repl))) - (funcall previous-hook condition hook)) - (t - (push condition (mrepl-pending-errors repl)) - (unless error-prompt-sent - (setq error-prompt-sent t) - (with-listener-bindings repl - (send-prompt repl condition))) - (unwind-protect - (funcall previous-hook condition hook) - (pop (mrepl-pending-errors repl)))))))) - (setq results (mrepl-eval-1 repl string) - ;; If somehow the form above MREPL-EVAL-1 exited - ;; normally, set ABORTED to nil - aborted nil)) - (unless (eq (mrepl-mode repl) :teardown) - (flush-listener-streams repl) - (saving-listener-bindings repl - (cond (aborted - (send-to-remote-channel (mrepl-remote-id repl) - `(:evaluation-aborted - ,(slynk::without-printing-errors - (:object aborted :stream nil) - (prin1-to-string aborted))))) - (t - (when results - (setq /// // // / / results - *** ** ** * * (car results)) - (vector-push-extend results *history*)) - (send-to-remote-channel - (mrepl-remote-id repl) - `(:write-values ,(make-results results))))) - (send-prompt repl)))))) - -(defun prompt-arguments (repl condition) - `(,(package-name *package*) - ,(package-string-for-prompt *package*) - ,(length (mrepl-pending-errors repl)) - ,@(when condition - (list (write-to-string condition - :escape t - :readably nil))))) - -(defun send-prompt (&optional (repl *channel*) condition) - (send-to-remote-channel (mrepl-remote-id repl) - `(:prompt ,@(prompt-arguments repl condition))) - (setf (mrepl-mode repl) :eval)) - -(defun mrepl-eval-1 (repl string) - "In REPL's environment, READ and EVAL forms in STRING." - (with-sly-interrupts - ;; Use WITH-LISTENER-BINDINGS (not SAVING-LISTENER-BINDINGS) - ;; instead, otherwise, if EVAL pops up an error in STRING's form, - ;; and in the meantime we had some debugging prompts (which make - ;; recursive calls to this function), the variables *, **, *** and - ;; *HISTORY* will get incorrectly clobbered to their pre-debugger - ;; values, whereas we want to serialize this history. - ;; - ;; However, as an exception, we /do/ want *PACKAGE* to be - ;; clobbered if the evaluation of STRING eventually completes. - ;; - ;; Another way to see this is: the forms that the user inputs can - ;; only change the *PACKAGE* binding of the listener's - ;; environment. Everything else in there is handled automatically. - ;; - (with-listener-bindings repl - (prog1 - (with-retry-restart (:msg "Retry SLY mREPL evaluation request.") - (with-input-from-string (in string) - (loop with values - for form = - (let ((*readtable* (let ((table (copy-readtable))) - (if *backreference-character* - (set-dispatch-macro-character - #\# - *backreference-character* - #'backreference-reader table)) - table))) - (read in nil in)) - until (eq form in) - do (let ((- form)) - (setq values (multiple-value-list - (eval - (saving-listener-bindings repl - (setq +++ ++ ++ + + form)))))) - finally - (return values)))) - (setf (cdr (assoc '*package* (slot-value repl 'slynk::env))) - *package*))))) - -(defun set-external-mode (repl new-mode) - (with-slots (mode remote-id) repl - (unless (eq mode new-mode) - (send-to-remote-channel remote-id `(:set-read-mode ,new-mode))) - (setf mode new-mode))) - -(defun read-input (repl) - (with-slots (mode remote-id) repl - ;; shouldn't happen with slynk-gray.lisp, they use locks - (assert (not (eq mode :read)) nil "Cannot pipeline READs") - (let ((tid (slynk-backend:thread-id (slynk-backend:current-thread))) - (old-mode mode)) - (unwind-protect - (cond ((and (eq (channel-thread-id repl) tid) - (eq mode :busy)) - (flush-listener-streams repl) - (set-external-mode repl :read) - (unwind-protect - (catch 'mrepl-read (process-requests nil)) - (set-external-mode repl :finished-reading))) - (t - (setf mode :read) - (with-output-to-string (s) - (format s - (or (slynk::read-from-minibuffer-in-emacs - (format nil "Input for thread ~a? " tid)) - (error "READ for thread ~a interrupted" tid))) - (terpri s)))) - (setf mode old-mode))))) - - -;;; Channel methods -;;; -(define-channel-method :inspect-object ((r mrepl) entry-idx value-idx) - (with-listener-bindings r - (send-to-remote-channel - (mrepl-remote-id r) - `(:inspect-object - ,(slynk::inspect-object - (mrepl-get-object-from-history entry-idx value-idx)))))) - -(define-channel-method :process ((c mrepl) string) - (with-slots (mode) c - (case mode - (:eval (mrepl-eval c string)) - (:read (throw 'mrepl-read string)) - (:drop)))) - -(define-channel-method :teardown ((r mrepl)) - ;; FIXME: this should be a `:before' spec and closing the channel in - ;; slynk.lisp's :teardown method should suffice. - ;; - (setf (mrepl-mode r) :teardown) - (call-next-method)) - -(define-channel-method :clear-repl-history ((r mrepl)) - (saving-listener-bindings r - ;; FIXME: duplication... use reinitialize-instance - (setf *history* (make-array 40 :fill-pointer 0 - :adjustable t) - * nil ** nil *** nil - + nil ++ nil +++ nil - / nil // nil /// nil) - (send-to-remote-channel (mrepl-remote-id r) `(:clear-repl-history)) - (send-prompt r))) - - -;;; slyfuns -;;; -(defslyfun create-mrepl (remote-id) - (let* ((mrepl (make-instance - 'mrepl - :remote-id remote-id - :name (format nil "mrepl-remote-~a" remote-id) - :out (make-mrepl-output-stream remote-id)))) - (let ((target (maybe-redirect-global-io *emacs-connection*))) - (saving-listener-bindings mrepl - (format *standard-output* "~&; SLY ~a (~a)~%" - *slynk-wire-protocol-version* - mrepl) - (cond - ((and target - (not (eq mrepl target))) - (format *standard-output* "~&; Global redirection setup elsewhere~%")) - ((not target) - (format *standard-output* "~&; Global redirection not setup~%")))) - (flush-listener-streams mrepl) - (send-prompt mrepl) - (list (channel-id mrepl) (channel-thread-id mrepl))))) - -(defslyfun globally-save-object (slave-slyfun &rest args) - "Apply SLYFUN to ARGS and save the value. - The saved value should be visible to all threads and retrieved via - the COPY-TO-REPL slyfun." - (setq *saved-objects* (multiple-value-list (apply slave-slyfun args))) - t) - -(defun copy-to-repl-in-emacs (values &key - (blurb "Here are some values") - (pop-to-buffer t)) - "Copy any user object to SLY's REPL. Requires - `sly-enable-evaluate-in-emacs' to be true." - (with-connection ((default-connection)) - (apply #'slynk-mrepl:globally-save-object #'cl:values values) - (slynk:eval-in-emacs `(sly-mrepl--copy-globally-saved-to-repl - :before ,blurb :pop-to-buffer ,pop-to-buffer)) - t)) - -(defmacro with-eval-for-repl ((remote-id &optional mrepl-sym - update-mrepl) &body body) - (let ((mrepl-sym (or mrepl-sym - (gensym)))) - `(let ((,mrepl-sym (find-channel ,remote-id))) - (assert ,mrepl-sym) - (assert - (eq (slynk-backend:thread-id - (slynk-backend:current-thread)) - (channel-thread-id ,mrepl-sym)) - nil - "This SLYFUN can only be called from threads belonging to MREPL") - ,(if update-mrepl - `(saving-listener-bindings ,mrepl-sym - ,@body) - `(with-listener-bindings ,mrepl-sym - ,@body))))) - -(defslyfun eval-for-mrepl (remote-id slave-slyfun &rest args) - "A synchronous form for evaluation in the mREPL context. - -Calls SLAVE-SLYFUN with ARGS in the MREPL of REMOTE-ID. Both the -target MREPL's thread and environment are considered. - -SLAVE-SLYFUN is typically destructive to the REPL listener's -environment. - -This function returns a list of two elements. The first is a list -of arguments as sent in the :PROMPT channel method reply. The second -is the values list returned by SLAVE-SLYFUN transformed into a normal -list." - (with-eval-for-repl (remote-id mrepl 'allow-destructive) - (let ((objects (multiple-value-list (apply slave-slyfun args)))) - (list - (prompt-arguments mrepl nil) - objects)))) - -(defslyfun inspect-entry (remote-id entry-idx value-idx) - (with-eval-for-repl (remote-id) - (slynk::inspect-object - (mrepl-get-object-from-history entry-idx value-idx)))) - -(defslyfun describe-entry (remote-id entry-idx value-idx) - (with-eval-for-repl (remote-id) - (slynk::describe-to-string - (mrepl-get-object-from-history entry-idx value-idx)))) - -(defslyfun pprint-entry (remote-id entry-idx value-idx) - (with-eval-for-repl (remote-id) - (slynk::slynk-pprint - (list (mrepl-get-object-from-history entry-idx value-idx))))) - - -;;; "Slave" slyfuns. -;;; -;;; These are slyfuns intented to be called as the SLAVE-SLYFUN -;;; argument of EVAL-FOR-MREPL. -;;; - -(defslyfun guess-and-set-package (package-name) - (let ((package (slynk::guess-package package-name))) - (if package - (setq *package* package) - (error "Can't find a package for designator ~a" package-name)) - t)) - -(defslyfun copy-to-repl (&optional entry-idx value-idx) - "Recall objects in *HISTORY* or *SAVED-OBJECTS* as the last entry." - (let ((objects - (cond ((and entry-idx value-idx) - (list (mrepl-get-object-from-history entry-idx value-idx))) - (entry-idx - (mrepl-get-history-entry entry-idx)) - (value-idx - (error "Doesn't make sense")) - (t - *saved-objects*)))) - (setq /// // // / / objects - *** ** ** * * (car objects)) - (vector-push-extend objects *history*) - (values-list (make-results objects)))) - -(defslyfun sync-package-and-default-directory (&key package-name directory) - (when directory - (slynk:set-default-directory directory)) - (when package-name - (guess-and-set-package package-name)) - (values (package-name *package*) (slynk-backend:default-directory))) - - -;;;; Dedicated stream -;;;; -(defvar *use-dedicated-output-stream* :started-from-emacs - "When T, dedicate a second stream for sending output to Emacs.") - -(defvar *dedicated-output-stream-port* 0 - "Which port we should use for the dedicated output stream.") - -(defvar *dedicated-output-stream-buffering* - (if (eq slynk:*communication-style* :spawn) :line nil) - "The buffering scheme that should be used for the output stream. -Be advised that some Lisp backends don't support this. -Valid values are nil, t, :line.") - -(defun use-dedicated-output-stream-p () - (case *use-dedicated-output-stream* - (:started-from-emacs slynk-api:*m-x-sly-from-emacs*) - (t *use-dedicated-output-stream*))) - -(defun make-mrepl-output-stream (remote-id) - (or (and (use-dedicated-output-stream-p) - (open-dedicated-output-stream remote-id)) - (slynk-backend:make-output-stream - (make-thread-bindings-aware-lambda - (lambda (string) - (send-to-remote-channel remote-id `(:write-string ,string))))))) - -(defun make-mrepl-input-stream (repl) - (slynk-backend:make-input-stream - (lambda () (read-input repl)))) - -(defun open-dedicated-output-stream (remote-id) - "Establish a dedicated output connection to Emacs. - -Emacs's channel at REMOTE-ID is notified of a socket listening at an -ephemeral port. Upon connection, the listening socket is closed, and -the resulting connecion socket is used as optimized way for Lisp to -deliver output to Emacs." - (let ((socket (slynk-backend:create-socket slynk::*loopback-interface* - *dedicated-output-stream-port*)) - (ef (or (some #'slynk::find-external-format '("utf-8-unix" "utf-8")) - (error "no suitable coding system for dedicated stream")))) - (unwind-protect - (let ((port (slynk-backend:local-port socket))) - (send-to-remote-channel remote-id - `(:open-dedicated-output-stream ,port nil)) - (let ((dedicated (slynk-backend:accept-connection - socket - :external-format ef - :buffering *dedicated-output-stream-buffering* - :timeout 30))) - (slynk:authenticate-client dedicated) - (slynk-backend:close-socket socket) - (setf socket nil) - (let ((result - ;; See github issue #21: Only sbcl and cmucl apparently - ;; respect :LINE as a buffering type, hence this reader - ;; conditional. This could/should be a definterface, but - ;; looks harmless enough... - ;; - #+(or sbcl cmucl) - dedicated - ;; ...on other implementations we make a relaying gray - ;; stream that is guaranteed to use line buffering for - ;; WRITE-SEQUENCE. That stream writes to the dedicated - ;; socket whenever it sees fit. - ;; - #-(or sbcl cmucl) - (if (eq *dedicated-output-stream-buffering* :line) - (slynk-backend:make-output-stream - (lambda (string) - (write-sequence string dedicated) - (force-output dedicated))) - dedicated))) - (prog1 result - (format result - "~&; Dedicated output stream setup (port ~a)~%" - port) - (force-output result))))) - (when socket - (slynk-backend:close-socket socket))))) - - -;;;; Globally redirect IO to Emacs -;;; -;;; This code handles redirection of the standard I/O streams -;;; (`*standard-output*', etc) into Emacs. If any LISTENER objects -;;; exist in the CONNECTION structure, they will contain the -;;; appropriate streams, so all we have to do is make the right -;;; bindings. -;;; -;;; When the first ever MREPL is created we redirect the streams into -;;; it, and they keep going into that MREPL even if more are -;;; established, in the current connection or even other -;;; connections. If the MREPL is closed (interactively or by closing -;;; the connection), we choose some other MREPL (in some other default -;;; connection possibly), or, or if there are no MREPL's left, we -;;; revert to the original (real) streams. -;;; -;;; It is slightly tricky to assign the global values of standard -;;; streams because they are often shadowed by dynamic bindings. We -;;; solve this problem by introducing an extra indirection via synonym -;;; streams, so that *STANDARD-INPUT* is a synonym stream to -;;; *CURRENT-STANDARD-INPUT*, etc. We never shadow the "current" -;;; variables, so they can always be assigned to affect a global -;;; change. -(defvar *globally-redirect-io* :started-from-emacs - "If non-nil, attempt to globally redirect standard streams to Emacs. -If the value is :STARTED-FROM-EMACS, do it only if the Slynk server -was started from SLYNK:START-SERVER, which is called from Emacs by M-x -sly.") - -(defvar *saved-global-streams* '() - "A plist to save and restore redirected stream objects. -E.g. the value for '*standard-output* holds the stream object -for *standard-output* before we install our redirection.") - -(defvar *standard-output-streams* - '(*standard-output* *error-output* *trace-output*) - "The symbols naming standard output streams.") - -(defvar *standard-input-streams* - '(*standard-input*) - "The symbols naming standard input streams.") - -(defvar *standard-io-streams* - '(*debug-io* *query-io* *terminal-io*) - "The symbols naming standard io streams.") - -(defvar *target-listener-for-redirection* nil - "The listener to which standard I/O streams are globally redirected. -NIL if streams are not globally redirected.") - -(defun setup-stream-indirection (stream-var &optional stream) - "Setup redirection scaffolding for a global stream variable. -Supposing (for example) STREAM-VAR is *STANDARD-INPUT*, this macro: - -1. Saves the value of *STANDARD-INPUT* in `*SAVED-GLOBAL-STREAMS*'. - -2. Creates *CURRENT-STANDARD-INPUT*, initially with the same value as -*STANDARD-INPUT*. - -3. Assigns *STANDARD-INPUT* to a synonym stream pointing to -*CURRENT-STANDARD-INPUT*. - -This has the effect of making *CURRENT-STANDARD-INPUT* contain the -effective global value for *STANDARD-INPUT*. This way we can assign -the effective global value even when *STANDARD-INPUT* is shadowed by a -dynamic binding." - (let ((current-stream-var (prefixed-var '#:current stream-var)) - (stream (or stream (symbol-value stream-var)))) - ;; Save the real stream value for the future. - (setf (getf *saved-global-streams* stream-var) stream) - ;; Define a new variable for the effective stream. - ;; This can be reassigned. - (proclaim `(special ,current-stream-var)) - (set current-stream-var stream) - ;; Assign the real binding as a synonym for the current one. - (let ((stream (make-synonym-stream current-stream-var))) - (set stream-var stream) - (slynk::set-default-initial-binding stream-var `(quote ,stream))))) - -(defun prefixed-var (prefix variable-symbol) - "(PREFIXED-VAR \"FOO\" '*BAR*) => SLYNK::*FOO-BAR*" - (let ((basename (subseq (symbol-name variable-symbol) 1))) - (intern (format nil "*~A-~A" (string prefix) basename) :slynk))) - -(defun init-global-stream-redirection () - (cond (*saved-global-streams* - (warn "Streams already redirected.")) - (t - (mapc #'setup-stream-indirection - (append *standard-output-streams* - *standard-input-streams* - *standard-io-streams*))))) - -(defun globally-redirect-to-listener (listener) - "Set the standard I/O streams to redirect to LISTENER. -Assigns *CURRENT-<STREAM>* for all standard streams." - (saving-listener-bindings listener - (dolist (o *standard-output-streams*) - (set (prefixed-var '#:current o) - *standard-output*)) - - ;; FIXME: If we redirect standard input to Emacs then we get the - ;; regular Lisp top-level trying to read from our REPL. - ;; - ;; Perhaps the ideal would be for the real top-level to run in a - ;; thread with local bindings for all the standard streams. Failing - ;; that we probably would like to inhibit it from reading while - ;; Emacs is connected. - ;; - ;; Meanwhile we just leave *standard-input* alone. - #+NIL - (dolist (i *standard-input-streams*) - (set (prefixed-var '#:current i) - (connection.user-input connection))) - (dolist (io *standard-io-streams*) - (set (prefixed-var '#:current io) - *terminal-io*)))) - -(defun revert-global-io-redirection () - "Set *CURRENT-<STREAM>* to *REAL-<STREAM>* for all standard streams." - ;; Log to SLYNK:*LOG-OUTPUT* since the standard streams whose - ;; redirection are about to be reverted might be in an unconsistent - ;; state after, for instance, restarting an image. - ;; - (format slynk:*log-output* "~&; About to revert global IO direction~%") - (when *target-listener-for-redirection* - (flush-listener-streams *target-listener-for-redirection*)) - (dolist (stream-var (append *standard-output-streams* - *standard-input-streams* - *standard-io-streams*)) - (set (prefixed-var '#:current stream-var) - (getf *saved-global-streams* stream-var)))) - -(defun globally-redirect-io-p () - (case *globally-redirect-io* - (:started-from-emacs slynk-api:*m-x-sly-from-emacs*) - (t *globally-redirect-io*))) - -(defun maybe-redirect-global-io (connection) - "Consider globally redirecting output to CONNECTION's listener. - -Return the current redirection target, or nil" - (let ((l (default-listener connection))) - (when (and (globally-redirect-io-p) - (null *target-listener-for-redirection*) - l) - (unless *saved-global-streams* - (init-global-stream-redirection)) - (setq *target-listener-for-redirection* l) - (globally-redirect-to-listener l) - (with-listener-bindings l - (format *standard-output* "~&; Redirecting all output to this MREPL~%") - (flush-listener-streams l))) - *target-listener-for-redirection*)) - -(defmethod close-channel :before ((r mrepl) &key force) - (with-slots (mode remote-id) r - (unless (or force (eq mode :teardown)) - (send-to-remote-channel remote-id `(:server-side-repl-close))) - ;; If this channel was the redirection target. - (close-listener r) - (when (eq r *target-listener-for-redirection*) - (setq *target-listener-for-redirection* nil) - (maybe-redirect-global-io (default-connection)) - (unless *target-listener-for-redirection* - (revert-global-io-redirection) - (format slynk:*log-output* "~&; Reverted global IO direction~%"))))) - -(provide :slynk/mrepl) diff --git a/elpa/sly-1.0.43/doc/Makefile b/elpa/sly-1.0.43/doc/Makefile @@ -1,123 +0,0 @@ -# This file has been placed in the public domain. -# -# Where to put the info file(s). NB: the GNU Coding Standards (GCS) -# and the Filesystem Hierarchy Standard (FHS) differ on where info -# files belong. The GCS says /usr/local/info; the FHS says -# /usr/local/share/info. Many distros obey the FHS, but people who -# installed their emacs from source probably have a GCS-ish file -# hierarchy. -infodir=/usr/local/info - -# What command to use to install info file(s) -INSTALL_CMD=install -m 644 - -# Info files generated here. -infofiles=sly.info - -TEXI = sly.texi contributors.texi - -help: - @echo -e "\ -Most important targets:\n\ -all generate info, pdf, and html documents\n\ -sly.info generate the sly.info file\n\ -sly.html generate a single html file\n\ -html/index.html generate on html file per node in html/ directory\n\ -html.tgz create a tarball of all html files\n\ -clean remove generated files" - -all: sly.info sly.pdf html/index.html - -sly.dvi: $(TEXI) - texi2dvi sly.texi - -sly.ps: sly.dvi - dvips -o $@ $< - -sly.info: $(TEXI) - makeinfo $< - -sly.html: $(TEXI) - texi2html --css-include=sly.css $< - -html/index.html: $(TEXI) - makeinfo -o html --css-include=sly.css --html $< - -html.tgz: html/index.html - tar -czf $@ html - -DOCDIR=/project/sly/public_html/doc - -publish: sly.html - rm -rf gh-pages - git clone git@github.com:joaotavora/sly.git --branch gh-pages --single-branch gh-pages - cp sly.html gh-pages/index.html - cp -Rf images/*.png gh-pages/images - cd gh-pages \ - && git add index.html images/*\ - && git commit -a -m "Automatic documentation update" \ - && git push origin gh-pages - -sly.pdf: $(TEXI) - texi2pdf $< - -sly-refcard.pdf: sly-refcard.tex - texi2pdf $< - -install: install-info - -uninstall: uninstall-info - -# Create contributors.texi, a texinfo table listing all known -# contributors of code. -# -# Explicitly includes Eric Marsden (pre-ChangeLog hacker) -# -# The gist of this horror show is that the contributor list is piped -# into texinfo-tabulate.awk with one name per line, sorted -# alphabetically. -contributors.texi: Makefile texinfo-tabulate.awk - git log --format='%aN' | \ - sort | \ - uniq -c | \ - LC_ALL=C sort -nr | \ - sed -e 's/^[^A-Z]*//; /^$$/d' | \ - LC_ALL=C awk -f texinfo-tabulate.awk \ - > $@ - -#.INTERMEDIATE: contributors.texi - -optimize-png: - optipng -clobber -o 7 images/*.png - -# Debian's install-info wants a --section argument. -install-info: section=$(shell grep INFO-DIR-SECTION $(infofiles) | sed 's/INFO-DIR-SECTION //') -install-info: sly.info - mkdir -p $(infodir) - $(INSTALL_CMD) $(infofiles) $(infodir)/$(infofiles) - @if (install-info --version && \ - install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ - echo "install-info --info-dir=$(infodir) $(infodir)/$(infofiles)";\ - install-info --info-dir="$(infodir)" "$(infodir)/$(infofiles)" || :;\ - else \ - echo "install-info --infodir=$(infodir) --section $(section) $(section) $(infodir)/$(infofiles)" && \ - install-info --infodir="$(infodir)" --section $(section) ${section} "$(infodir)/$(infofiles)" || :; fi - -uninstall-info: - @if (install-info --version && \ - install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ - echo "install-info --info-dir=$(infodir) --remove $(infodir)/$(infofiles)";\ - install-info --info-dir="$(infodir)" --remove "$(infodir)/$(infofiles)" || :;\ - else \ - echo "install-info --infodir=$(infodir) --remove $(infodir)/$(infofiles)";\ - install-info --infodir="$(infodir)" --remove "$(infodir)/$(infofiles)" || :; fi - rm -f $(infodir)/$(infofiles) - -clean: - rm -f contributors.texi - rm -f sly.aux sly.cp sly.cps sly.fn sly.fns sly.ky - rm -f sly.kys sly.log sly.pg sly.tmp sly.toc sly.tp - rm -f sly.vr sly.vrs - rm -f sly.info sly.pdf sly.dvi sly.ps sly.html - rm -f sly-refcard.pdf sly-refcard.log sly-refcard.aux - rm -rf html html.tgz diff --git a/elpa/sly-1.0.43/doc/animations/backreferences.gif b/elpa/sly-1.0.43/doc/animations/backreferences.gif Binary files differ. diff --git a/elpa/sly-1.0.43/doc/animations/company-flex-completion.gif b/elpa/sly-1.0.43/doc/animations/company-flex-completion.gif Binary files differ. diff --git a/elpa/sly-1.0.43/doc/animations/reverse-isearch.gif b/elpa/sly-1.0.43/doc/animations/reverse-isearch.gif Binary files differ. diff --git a/elpa/sly-1.0.43/doc/animations/stickers-example.gif b/elpa/sly-1.0.43/doc/animations/stickers-example.gif Binary files differ. diff --git a/elpa/sly-1.0.43/doc/contributors.texi b/elpa/sly-1.0.43/doc/contributors.texi @@ -1,31 +0,0 @@ -@multitable @columnfractions 0.333333 0.333333 0.333333 - -@item Helmut Eller @tab JoĂŁo Távora @tab Luke Gorrie -@item Tobias C. Rittweiler @tab Stas Boukarev @tab Marco Baringer -@item Matthias Koeppe @tab Nikodemus Siivola @tab Alan Ruttenberg -@item Attila Lendvai @tab LuĂ­s Borges de Oliveira @tab Dan Barlow -@item Andras Simon @tab Martin Simmons @tab Geo Carncross -@item Christophe Rhodes @tab Peter Seibel @tab Mark Evenson -@item Juho Snellman @tab Douglas Crosher @tab Wolfgang Jenkner -@item R Primus @tab Javier Olaechea @tab Edi Weitz -@item Zach Shaftel @tab James Bielman @tab Daniel Kochmanski -@item Terje Norderhaug @tab Vladimir Sedach @tab Juan Jose Garcia Ripoll -@item Alexander Artemenko @tab Spenser Truex @tab Nathan Trapuzzano -@item Brian Downing @tab Mark @tab Jeffrey Cunningham -@item Espen Wiborg @tab Paul M. Rodriguez @tab Masataro Asai -@item Jan Moringen @tab SĂ©bastien Villemot @tab Samuel Freilich -@item Raymond Toy @tab Pierre Neidhardt @tab Phil Hargett -@item Paulo Madeira @tab Kris Katterjohn @tab Jonas Bernoulli -@item Ivan Shvedunov @tab Gábor Melis @tab Francois-Rene Rideau -@item Christophe Junke @tab Bozhidar Batsov @tab Bart Botta -@item Wilfredo Velázquez-RodrĂ­guez @tab Tianxiang Xiong @tab Syohei YOSHIDA -@item Stefan Monnier @tab Rommel MARTINEZ @tab Pavel Kulyov -@item Paul A. Patience @tab Olof-Joachim Frahm @tab Mike Clarke -@item MichaĹ‚ Herda @tab Mark H. David @tab Mario Lang -@item Manfred Bergmann @tab Leo Liu @tab Koga Kazuo -@item Jon Oddie @tab John Stracke @tab Joe Robertson -@item Grant Shangreaux @tab Graham Dobbins @tab Eric Timmons -@item Douglas Katzman @tab Dmitry Igrishin @tab Dmitrii Korobeinikov -@item Deokhwan Kim @tab Denis Budyak @tab Chunyang Xu -@item Cayman @tab Angelo Rossi @tab Andrew Kirkpatrick -@end multitable diff --git a/elpa/sly-1.0.43/doc/sly-refcard.tex b/elpa/sly-1.0.43/doc/sly-refcard.tex @@ -1,121 +0,0 @@ -\documentclass[a4paper,10pt]{article} - -\usepackage{textcomp} -\usepackage{fullpage} -\pagestyle{empty} - - -\newcommand{\group}[1]{\bigskip\par\noindent\textbf{\large#1}\medskip} -\newcommand{\subgroup}[1]{\medskip\par\noindent\textbf{#1}\smallskip} -\newcommand{\key}[2]{\par\noindent\textbf{#1}\hfill{#2}} -\newcommand{\meta}[1]{\textlangle{#1}\textrangle} - -\begin{document} - -\twocolumn[\LARGE\centering{SLY Quick Reference Card}\vskip1cm] - -\group{Getting help in Emacs} - -\key{C-h \meta{key}}{describe function bound to \meta{key}} -\key{C-h b}{list the current key-bindings for the focus buffer} -\key{C-h m}{describe mode} -\key{C-h l}{shows the keys you have pressed} -\key{\meta{key} l}{what starts with \meta{key}} - -\group{Programming} - -\subgroup{Completion} - -\key{C-c tab}{complete symbol} - -\subgroup{Closure} - -\key{C-c C-q}{close parens at point} -\key{C-]}{close all sexp} - -\subgroup{Indentation} - -\key{C-c M-q}{reindent defun} -\key{C-M-q}{indent sexp} - -\subgroup{Documentation} - -\key{spc}{insert a space, display argument list} -\key{C-c C-d C-d}{describe symbol} -\key{C-c C-d C-f}{describe function} -\key{C-c C-d C-a}{apropos search for regexp} -\key{C-c C-d C-z}{apropos with internal symbols} -\key{C-c C-d C-p}{apropos in package} -\key{C-c C-d C-h}{hyperspec lookup} -\key{C-c C-d C-\~}{format character hyperspec lookup} - - -\subgroup{Cross reference} - -\key{C-c C-w C-c}{show function callers} -\key{C-c C-w C-r}{show references to global variable} -\key{C-c C-w C-b}{show bindings of a global variable} -\key{C-c C-w C-s}{show assignments to a global variable} -\key{C-c C-w C-m}{show expansions of a macro} -\key{C-c \textless}{list callers of a function} -\key{C-c \textgreater}{list callees of a function} - -\subgroup{Finding definitions} - -\key{M-.}{edit definition} -\key{M-, or M-*}{pop definition stack} -\key{C-x 4 .}{edit definition in other window} -\key{C-x 5 .}{edit definition in other frame} - -\newpage - -\subgroup{Macro expansion commands} - -\key{C-c C-m or C-c RET}{macroexpand-1} -\key{C-c M-m}{macroexpand-all} -\key{C-c C-t}{toggle tracing of the function at point} - -\subgroup{Disassembly} - -\key{C-c M-d}{disassemble function definition} - -\group{Compilation} - -\key{C-c C-c}{compile defun} -\key{C-c C-y}{call defun} -\key{C-c C-k}{compile and load file} -\key{C-c M-k}{compile file} -\key{C-c C-l}{load file} -\key{C-c C-z}{switch to output buffer} -\key{M-n}{next note} -\key{M-p}{previous note} -\key{C-c M-c}{remove notes} - -\group{Evaluation} - -\key{C-M-x}{eval defun} -\key{C-x C-e}{eval last expression} -\key{C-c C-p}{eval \& pretty print last expression} -\key{C-c C-r}{eval region} -\key{C-x M-e}{eval last expression, display output} -\key{C-c :}{interactive eval} -\key{C-c E}{edit value} -\key{C-c C-u}{undefine function} - -\group{Abort/Recovery} - -\key{C-c C-b}{interrupt (send SIGINT)} -\key{C-c \~}{sync the current package and working directory} -\key{C-c M-p}{set package in REPL} - -\group{Inspector} - -\key{C-c I}{inspect (from minibuffer)} -\key{ret}{operate on point} -\key{d}{describe} -\key{l}{pop} -\key{n}{next} -\key{q}{quit} -\key{M-ret}{copy down} - -\end{document} diff --git a/elpa/sly-1.0.43/doc/sly.css b/elpa/sly-1.0.43/doc/sly.css @@ -1,22 +0,0 @@ -body { font-family: Georgia, serif; - line-height: 1.3; - padding-left: 5em; padding-right: 1em; - padding-bottom: 1em; max-width: 60em; } -table { border-collapse: collapse } -span.roman { font-family: century schoolbook, serif; font-weight: normal; } -h1, h2, h3, h4, h5, h6 { font-family: Helvetica, sans-serif } -h4 { margin-top: 2.5em; } -dfn { font-family: inherit; font-variant: italic; font-weight: bolder } -var { font-variant: slanted; } -td { padding-right: 1em; padding-left: 1em } -sub { font-size: smaller } -.node { padding: 0; margin: 0 } -dd { padding-top: 1em; padding-bottom: 2em } -pre.example { - font-family: monospace; - background-color: #E9FFE9; border: 1px solid #9D9; - padding-top: 0.5em; padding-bottom: 0.5em; } -a:link { color: #383; text-decoration: none; padding: 1px 2px 1px 2px; } -a:visited { color: #161; text-decoration: none; padding: 1px 2px 1px 2px; } -a:hover { color: #161; text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #666; } -a:focus { color: #161; text-decoration: none; padding: 1px 2px 1px 2px; border: none; } diff --git a/elpa/sly-1.0.43/doc/sly.texi b/elpa/sly-1.0.43/doc/sly.texi @@ -1,3208 +0,0 @@ -\input texinfo -@c %**start of header -@setfilename sly.info - -@iftex -@documentencoding UTF-8 -@codequoteundirected on -@codequotebacktick on -@end iftex - -@dircategory Emacs -@direntry -* SLY: (sly). Common-Lisp IDE -@end direntry -@c %**end of header - -@set SLYVER 1.0.42 -@set UPDATED @today{} -@set TITLE SLY User Manual -@settitle @value{TITLE}, version @value{SLYVER} - -@copying -Written for SLIME Luke Gorrie and others, rewritten by JoĂŁo -Távora for SLY. - -This file has been placed in the public domain. -@end copying - -@c For screenshots use - -@c (make-frame '((height . 32) (width . 90) (font . "Andale Mono 13"))) -@c (make-frame '((height . 32) (width . 90) (font . "DejaVu Sans Mono 10"))) -@c M-x load-theme RET base16-bright-light RET - -@c preferably on Mac OSX, then Cmd-Shift-4 SPC and click the window - - -@titlepage -@title @value{TITLE} -@sp 10 -@example - _____ __ __ __ - / ___/ / / \ \/ / |\ _,,,---,,_ - \__ \ / / \ / /,`.-'`' -. ;-;;,_ - ___/ / / /___ / / |,4- ) )-,_..;\ ( `'-' - /____/ /_____/ /_/ '---''(_/--' `-'\_) - -@end example -@sp 10 -@subtitle version @value{SLYVER} -@page -@insertcopying - -@end titlepage - -@c Macros - -@macro SLY -@acronym{SLY} -@end macro - -@macro SLIME -@acronym{SLIME} -@end macro - -@macro SLY-DB -@acronym{SLY-DB} -@end macro - -@macro REPL -@acronym{REPL} -@end macro - -@macro Git -@acronym{Git} -@end macro - -@macro kbditem{key, command} -@item \key\ -@itemx M-x \command\ -@kindex \key\ -@findex \command\ -@c -@end macro - -@macro kbditempair{key1, key2, command1, command2} -@item \key1\, M-x \command1\ -@itemx \key2\, M-x \command2\ -@kindex \key1\ -@kindex \key2\ -@findex \command1\ -@findex \command2\ -@c -@end macro - -@macro cmditem{command} -@item M-x \command\ -@findex \command\ -@c -@end macro - -@macro kbdanchorc{key, command, comment} -@anchor{\command\} -@item \key\ -@code{\command\} -@i{\comment\}@* -@end macro - -@macro fcnindex{name} -@item \name\ -@xref{\name\}. -@end macro - -@c Merge the variable and concept indices because both are rather short -@synindex cp vr - - -@c @setchapternewpage off -@c @shortcontents -@contents - -@ifnottex -@node Top -@top SLY - -@SLY{} is a Common Lisp IDE for Emacs. This is the manual for version -@value{SLYVER}. (Last updated @value{UPDATED}) - -@insertcopying -@end ifnottex - -@menu -* Introduction:: -* Getting started:: -* A SLY tour for SLIME users:: -* Working with source files:: -* Common functionality:: -* SLY REPL and other special buffers:: -* Customization:: -* Tips and Tricks:: -* Extensions:: -* Credits:: -* Key Index:: -* Command Index:: -* Variable Index:: - -@detailmenu - --- The Detailed Node Listing --- - -Getting started - -* Platforms:: -* Downloading:: -* Basic setup:: -* Running:: -* Basic customization:: -* Multiple Lisps:: - -Working with source files - -* Evaluation:: -* Compilation:: -* Autodoc:: -* Semantic indentation:: -* Reader conditionals:: -* Macro-expansion:: - -Common functionality - -* Finding definitions:: -* Cross-referencing:: -* Completion:: -* Interactive objects:: -* Documentation:: -* Multiple connections:: -* Disassembly:: -* Recovery:: -* Temporary buffers:: -* Multi-threading:: - -SLY REPL and other special buffers - -* REPL:: -* Inspector:: -* Debugger:: -* Trace Dialog:: -* Stickers:: - -SLY REPL: the ``top level'' - -* REPL commands:: -* REPL output:: -* REPL backreferences:: - -The SLY-DB Debugger - -* Examining frames:: -* Restarts:: -* Frame Navigation:: -* Miscellaneous:: - -Customization - -* Emacs-side:: -* Lisp-side customization:: - -Emacs-side - -* Keybindings:: -* Keymaps:: -* Defcustom variables:: -* Hooks:: - -Lisp-side (Slynk) - -* Communication style:: -* Other configurables:: - -Tips and Tricks - -* Connecting to a remote Lisp:: -* Loading Slynk faster:: -* Auto-SLY:: -* REPLs and game loops:: -* Controlling SLY from outside Emacs:: - -Connecting to a remote Lisp - -* Setting up the Lisp image:: -* Setting up Emacs:: -* Setting up pathname translations:: - -Extensions - -* Loading and unloading:: -* More contribs:: - -More contribs - -* TRAMP Support:: -* Scratch Buffer:: - -@end detailmenu -@end menu - -@node Introduction -@chapter Introduction - -@SLY{} is Sylvester the Cat's Common Lisp IDE. It extends Emacs with -support for interactive programming in Common Lisp. - -The features are centered around an Emacs minor-mode called -@code{sly-mode}, which complements the standard major-mode -@code{lisp-mode} for editing Lisp source files. @code{sly-mode} -adds support for interacting with a running Common Lisp process for -compilation, debugging, documentation lookup, and so on. - -@SLY{} attempts to follow the example of Emacs's own native Emacs-Lisp -environment. Many of the keybindings and interface concepts used to -interact with Emacs's Elisp machine are reused in @SLY{} to interact -with the underlying Common Lisp run-times. Emacs makes requests to -these processes, asking them to compile files or code snippets; deliver -introspection information various objects; or invoke commands or -debugging restarts. - -Internally, @SLY{}'s user-interface, written in Emacs Lisp, is connected -via sockets to one or more instances of a server program called -``Slynk'' that is running in the Lisp processes. - -The two sides communicate using a Remote Procedure Call (@acronym{RPC}) -protocol. The Lisp-side server is primarily written in portable Common -Lisp. However, because some non-standard functionality is provided -differently by each Lisp implementation (SBCL, CMUCL, Allegro, etc...) -the Lisp-side server is again split into two parts -- portable and -non-portable implementation -- which communicate using a well-defined -interface. Each Lisp implementation provides a separate implementation -of that interface, making @SLY{} as a whole readily portable. - -@SLY{} is a direct fork of @acronym{SLIME}, the ``Superior Lisp -Interaction Mode for Emacs'', which itself derived from previous Emacs -programs such as @acronym{SLIM} and @acronym{ILISP}. If you already -know @acronym{SLIME}, @SLY{}'s closeness to it is immediately apparent. -However, where @acronym{SLIME} has traditionally focused on the -stability of its core functionality, @SLY{} aims for a richer feature -set, a more consistent user interface, and an experience generally -closer to Emacs' own. - -To understand the differences between the two projects read -@SLY{}'s @uref{https://github.com/joaotavora/sly/blob/master/NEWS.md,,NEWS.md} -file. For a hand-on approach to these differences you might want to -@ref{A SLY tour for SLIME users}. - -@node Getting started -@chapter Getting started - -This chapter tells you how to get @SLY{} up and running. - -@menu -* Platforms:: -* Downloading:: -* Basic setup:: -* Running:: -* Basic customization:: -* Multiple Lisps:: -@end menu - -@node Platforms -@section Supported Platforms - -@SLY{} supports a wide range of operating systems and Lisp -implementations. @SLY{} runs on Unix systems, Mac OSX, and Microsoft -Windows. GNU Emacs versions 24.4 and above are supported. @emph{XEmacs -or Emacs 23 are notably not supported}. - -The supported Lisp implementations, roughly ordered from the -best-supported, are: - -@itemize @bullet -@item -CMU Common Lisp (@acronym{CMUCL}), 19d or newer -@item -Steel Bank Common Lisp (@acronym{SBCL}), 1.0 or newer -@item -Clozure Common Lisp (@acronym{CCL}), version 1.3 or newer -@item -LispWorks, version 4.3 or newer -@item -Allegro Common Lisp (@acronym{ACL}), version 6 or newer -@item -@acronym{CLISP}, version 2.35 or newer -@item -Armed Bear Common Lisp (@acronym{ABCL}) -@item -Scieneer Common Lisp (@acronym{SCL}), version 1.2.7 or newer -@item -Embedded Common Lisp (@acronym{ECL}) -@item -ManKai Common Lisp (@acronym{MKCL}) -@item -Clasp -@end itemize - -Most features work uniformly across implementations, but some are -prone to variation. These include the precision of placing -compiler-note annotations, @acronym{XREF} support, and fancy debugger -commands (like ``restart frame''). - -@node Downloading -@section Downloading SLY - -By far the easiest method for getting @SLY{} up and running is using -Emacs’ package system configured to the popular MELPA repository. This -snippet of code should already be in your configuration: - -@example -(add-to-list 'package-archives - '("melpa" . "https://melpa.org/packages/")) -(package-initialize) -@end example - -You should now be able to issue the command @kbd{M-x package-install}, -choose @kbd{sly} and have it be downloaded and installed -automatically. If you don’t find it in the list, ensure you -run @kbd{M-x package-refresh-contents} first. - -In other situations, such as when developing @SLY{} itself, you can -access the @Git{} repository directly: - -@example -git clone https://github.com/joaotavora/sly.git -@end example - -If you want to hack on @SLY{}, use Github's @emph{fork} functionality -and submit a @emph{pull request}. Be sure to first read the -@uref{https://github.com/joaotavora/sly/blob/master/CONTRIBUTING.md,,CONTRIBUTING.md} file first. - -@node Basic setup -@section Basic setup - -If you installed @SLY{} from MELPA, it is quite possible that you -don’t need any more configuration, provided that @SLY{} can find a -suitable Lisp executable in your @code{PATH} environment variable. - -Otherwise, you need to tell it where a Lisp program can be found, so -customize the variable @code{inferior-lisp-program} (@pxref{Defcustom -variables}) or add a line like this one to your @file{~/.emacs} -or @file{~/.emacs.d/init.el} (@pxref{Emacs Init File}). - -@example -(setq inferior-lisp-program "/opt/sbcl/bin/sbcl") -@end example - -After evaluating this, you should be able to execute @kbd{M-x sly} and -be greeted with a @REPL{}. - -If you cloned from the @Git{} repository, you’ll have to add a couple -of more lines to your initialization file configuration: - -@example -(add-to-list 'load-path "~/dir/to/cloned/sly") -(require 'sly-autoloads) -@end example - -@node Running -@section Running SLY - -@SLY{} can either ask Emacs to start its own Lisp subprocesss or -connect to a running process on a local or remote machine. - -The first alternative is more common for local development and is -started via @kbd{M-x sly}. The ``inferior'' Lisp process thus started -is told to load the Lisp-side server known as ``Slynk'' and then a -socket connection is established between Emacs and Lisp. Finally -a @REPL{} buffer is created where you can enter Lisp expressions for -evaluation. - -The second alternative uses @kbd{M-x sly-connect}. This assumes that -that a Slynk server is running on some local or remote host, and -listening on a given port. @kbd{M-x sly-connect} prompts the user for -these values, and upon connection the @REPL{} is established. - -@node Basic customization -@section Basic customization - -A big part of Emacs, and Emacs’s extensions, are its near-infinite -customization possibilities. @SLY{} is no exception, because it runs -on both Emacs and the Lisp process, there are layers of Emacs-side -customization and Lisp-side customization. But don’t be put off by -this! @SLY{} tries hard to provide sensible defaults that don’t -``hide'' any fanciness beneath layers of complicated code, so that -even a setup with no customization at all exposes @SLY{}’s most -important functionality. - -Emacs-side customization is usually done via Emacs-lisp code snippets -added to the user’s initialization file, usually @file{$HOME/.emacs} or -@file{$HOME/.emacs.d/init.el} (@pxref{Emacs Init File}). - -90% of Emacs-lisp customization happens in either ``keymaps'' or -``hooks'' (@pxref{Emacs-side}). Still on the Emacs side, there is also a -separate interface, appropriately called @code{customize} (or sometimes -just @code{custom}), that uses a nicer UI with mouse-clickable buttons -to set some special variables. See @xref{Defcustom variables}. - -Lisp-side customization is done exclusively via Common Lisp code -snippets added to the user’s @file{$HOME/.slynkrc} -file. See @xref{Lisp-side customization}. - -As a preview, take this simple example of a frequently customized part of -@SLY{}: its keyboard shortcuts, known as ``keybindings''. In the -following snippet @kbd{M-h} is added to @code{sly-prefix-map} thus -yielding @kbd{C-c M-h} as a shortcut to -the @code{sly-documentation-lookup} command. - -@example -(eval-after-load 'sly - `(define-key sly-prefix-map (kbd "M-h") 'sly-documentation-lookup)) -@end example - -@node Multiple Lisps -@section Multiple Lisps - -By default, the command @kbd{M-x sly} starts the program specified -with @code{inferior-lisp-program}, a variable that you can customize -(@pxref{Defcustom variables}). However, if you invoke @kbd{M-x sly} -with a @emph{prefix argument}, meaning you type @kbd{C-u M-x sly} then -Emacs prompts for the program which should be started instead. - -If you need to do this frequently or if the command involves long -filenames it's more convenient to set -the @code{sly-lisp-implementations} variable in your initialization file -(@pxref{Emacs Init File}). For example here we define two programs: - -@lisp -(setq sly-lisp-implementations - '((cmucl ("cmucl" "-quiet")) - (sbcl ("/opt/sbcl/bin/sbcl") :coding-system utf-8-unix))) -@end lisp - -Now, if you invoke @SLY{} with a @emph{negative} prefix argument, -@kbd{M-- M-x sly}, you can select a program from that list. When -called without a prefix, either the name specified in -@code{sly-default-lisp}, or the first item of the list will be used. -The elements of the list should look like - -@lisp -(NAME (PROGRAM PROGRAM-ARGS...) &key CODING-SYSTEM INIT INIT-FUNCTION ENV) -@end lisp - -@table @code -@item NAME -is a symbol and is used to identify the program. -@item PROGRAM -is the filename of the program. Note that the filename can contain -spaces. -@item PROGRAM-ARGS -is a list of command line arguments. -@item CODING-SYSTEM -the coding system for the connection. (@pxref{sly-net-coding-system})x -@item INIT -should be a function which takes two arguments: a filename and a -character encoding. The function should return a Lisp expression as a -string which instructs Lisp to start the Slynk server and to write the -port number to the file. At startup, @SLY{} starts the Lisp process -and sends the result of this function to Lisp's standard input. As -default, @code{sly-init-command} is used. An example is shown in -@ref{init-example,,Loading Slynk faster}. -@item INIT-FUNCTION -should be a function which takes no arguments. It is called after -the connection is established. (See also @ref{sly-connected-hook}.) -@item ENV -specifies a list of environment variables for the subprocess. E.g. -@lisp -(sbcl-cvs ("/home/me/sbcl-cvs/src/runtime/sbcl" - "--core" "/home/me/sbcl-cvs/output/sbcl.core") - :env ("SBCL_HOME=/home/me/sbcl-cvs/contrib/")) -@end lisp -initializes @code{SBCL_HOME} in the subprocess. -@end table - -@node A SLY tour for SLIME users -@chapter A SLY tour for SLIME users - -The chances are that if you’re into Common Lisp, you already know about -@SLIME{}, the project that originated @SLY{}. Itself originating in -older Emacs extensions @acronym{SLIM} and @acronym{ILISP}, @SLIME{} has -been around for at least a decade longer than @SLY{} and is quite an -amazing IDE. It seems reasonable to assume that most Lispers have some -experience with it, and perhaps it is an even more reasonable idea to -provide, in the form of a quick tutorial, a hands-on overview of some of -the improvements of @SLY{} over @SLIME{}. - -When you start @SLY{} with @kbd{M-x sly} (@pxref{Basic setup}) you are -greeted with its @REPL{}, a common starting point of Lisp hacking -sessions. This has been completely redesigned in @SLY{}: you can spawn -multiple REPL sessions with @code{sly-mrepl-new}; copy objects from most -places directly into it (with @kbd{M-RET} and @kbd{M-S-RET}); and use a -much more powerful incremental history search engine (with @kbd{C-r}). - -@*@image{images/tutorial-1,350pt}@* - -Starting from the new @REPL{}, let's showcase some of @SLY{}’s features. -Let’s pretend we want to hack an existing Lisp project, say @SLY{} -itself, or rather a part of its Lisp server which is called Slynk. -Let's pretend we're intrigued by the way its ``flex''-style completion -works. What is ``flex''-style completion, you ask? Well, if you're at -the @REPL{} you can try it now: it's a way of @kbd{TAB}-completing -(@pxref{Completion}) symbol names based on educated guesses of a few -letters. Thus if we type @code{mvbind}, @SLY{} guesses that we probably -meant @code{multiple-value-bind}, and if we type @code{domat} it might -possibly guess @code{cl-ppcre:do-matches}. Let's dig into the code that -makes this happen. - -Where should we start though, if we know very little about this project? -Well, a good point to start is always the @emph{apropos} functionality, -which is a @code{grep} of sorts, but symbolic rather than purely -textual. In @SLY{}, @code{sly-apropos} will use the @code{CL-PPCRE} -library if it finds is it loaded, else it falls back to a regex-less -mode of searching. If you -have @uref{https://www.quicklisp.org/beta/,Quicklisp} you need only -type @code{(ql:quickload :cl-ppcre)} from the @REPL{}. - -So if we want to hack on @SLY{}'s ``flex-completion'' functionality, but -we don't any of its symbol's names. We type -@kbd{C-c C-d C-z} (the shortcut for @kbd{M-x sly-apropos-all}) and -then type in ``sly.*flex'' at the prompt, followed by @kbd{enter} -or @kbd{return} (abbreviated @code{RET} or @kbd{C-m}). @SLY{} should -now present all Lisp symbols matching your search pattern. - -@*@image{images/tutorial-2,350pt}@* - -In the @code{apropos} buffer, let’s examine, by right-clicking it, the -symbol @code{SLY-COMPLETIONS:FLEX-COMPLETIONS}. We’ll be presented with -a context menu with options for describing the symbol, inspecting it, or -navigating to its source definition. In general, the Lisp-side objects -that SLY presents --- symbols, CLOS objects, function calls, etc... --- -are right-clickable buttons with such a context menu -(@pxref{Interactive objects}). For now, let’s navigate to the source -definition of the symbol by choosing ``Go To source'' from the menu. We -could also have just pressed @kbd{M-.} on the symbol, of course. - -From the Lisp source buffer that we landed on (probably -@file{slynk-completion.lisp}), let’s @emph{trace} the newly found function -@code{SLY-COMPLETIONS:FLEX-COMPLETIONS}. However, instead of using the -regular @code{CL:TRACE}, we’ll use @SLY{}’s Trace Dialog functionality. -This is how we set it up: - -@enumerate - -@item -first type @kbd{C-c C-t} on the function’s name, or enter that in the -minibuffer prompt; - -@item -now, open the Trace Dialog in a new window by typing @kbd{C-c T} (that’s -a capital @code{T}). We should already see our traced function under -the heading ``Traced specs''; - -@item -thirdly, for good measure, let’s also trace the nearby -function @code{SLY-COMPLETIONS::FLEX-SCORE} by also typing @kbd{C-c C-t} -on its name, or just entering it in the minibuffer prompt. - -@end enumerate - -Now let’s return to the REPL by switching to its -@code{*sly-mrepl ...} buffer or typing @kbd{C-c C-z}. In the REPL, let’s try to -complete some typical Lisp string by typing just @code{desbind} and then -typing @kbd{TAB}. We should see a window popup including the desired -completion @code{destructuring-bind} (it should also be the top -match). Of course, we could now select some completion from the list, -but instead let's just type @kbd{C-g} to dismiss -the @code{*sly-completions*} window, since we wanted to test -completion, not write any actual @code{destructuring-bind} expression. - -Remember the traced functions in the Trace Dialog? Let’s see if we got -any traces: let's type @kbd{C-c T} to switch to that buffer, and then -type capital @kbd{G}. This should produce a fair number of traces -organized in a call graph. - -@*@image{images/tutorial-3,350pt}@* - -We can later learn more about this mode (@pxref{Trace Dialog}), but for -now let’s again pretend we expected the function @code{FLEX-SCORE} to -return a wildly different score for -@code{COMMON-LISP:DESTRUCTURING-BIND}. In that case we should like to -witness said @code{FLEX-SCORE} function respond to any implementation -improvements we perform. To do so, it's useful to be able to surgically -re-run that function with those very same arguments. Let's do this by -finding the function call in the Trace Dialog window, right-clicking it -with the mouse and selecting ``Copy call to REPL''. As an alternative, -pressing @kbd{M-S-RET} on it also works. Whichever way we do this, we -are automatically transported to the REPL again, where the desired -function call has already been typed out for us at the command prompt, -awaiting a confirmation @kbd{RET}, which will run the function call. -The call may look strange, though: - -@example -; The actual arguments passed to trace 15 -"desbind" -"COMMON-LISP:DESTRUCTURING-BIND" -(12 13 14 26 27 28 29) -SLYNK-COMPLETION> (slynk-completion::flex-score #v1:0 #v1:1 #v1:2) -0.003030303 (0.30303028%) -SLYNK-COMPLETION> -@end example - -@*@image{images/tutorial-4,350pt}@* - -So here’s what’s going on: to copy the call to the REPL, @SLY{} first -copied over its actual arguments, and then wrote the function using -special @emph{backreferences} to those arguments in the correct place. -These are the @code{#v4:0} and @code{#v4:1} bits seen at the command -prompt. Let’s go ahead and put the cursor on them (or hover the mouse). -See how this makes them highlight the corresponding object a few lines -above in the buffer? Later, you can also try typing ``#v'' at the REPL -to incrementally write your own backreferences. - -For one final demonstration, let’s now suppose say we are still -intrigued by how that function (@code{FLEX-SCORE}) works internally. So let's navigate to -its definition using @kbd{M-.} again (or just open -the @file{slynk-completion.lisp} buffer that you probably still have -open). The function’s code might look like this: - -@example -(defun flex-score (pattern string indexes) - "Score the match of PATTERN on STRING. -INDEXES as calculated by FLEX-MATCHES" - ;; FIXME: hideously naive scoring - (declare (ignore pattern)) - (float - (/ 1 - (* (length string) - (max 1 - (reduce #'+ - (loop for (a b) on indexes - while b - collect (- b a 1)))))))) -@end example - -Can this function be working correctly? What do all those expressions -return? Should we reach for good old C-style @code{printf}? Let's try -``stickers'' instead. SLY's stickers are a form of non-intrusive -function instrumentation that work like carefully crafted @code{print} -or @code{(format t ...)}), but are much easier to work with. You can -later read more about them (@pxref{Stickers}), but for now you can just -think of them as colorful labels placed on s-exp’s. Let’s place a bunch -here, like this: - -@enumerate - -@item -on the last line of @code{flex-score}, place your cursor on the first -open parenthesis of that line (the opening parenthesis of the expression -@code{(- b a 1)}) and press @kbd{C-c C-s C-s}; - -@item -now do the same for the symbol @code{indexes} a couple of lines above; - -@item -again, the same for the expressions @code{(loop...)}, -@code{(reduce...)}, @code{(max...)}, @code{(length...)}, -@code{(*...)}, @code{(/... )} and @code{(float...)}. You could have -done this in any order, by the way; - -@end enumerate - -Now let’s recompile this definition with @kbd{C-c C-c}. Beside the -minibuffer note something about stickers being ``armed'' our function -should now look like a rainbow in blue. - -@*@image{images/tutorial-5,350pt}@* - -Now we return to the @SLY{} REPL, but this time let’s use @kbd{C-c ~} -(that’s @kbd{C-c} followed by ``tilde'') to do so. This syncs the -REPL’s local package and local directory to the Lisp file that we’re -visiting. This is something not strictly necessary here but generally -convenient when hacking on a system, because you can now call functions -from the file you came from without package-qualification. - -Now, to re-run the newly instrumented function, by calling it with the -same arguments. No need to type all that again, because this REPL -supports reverse history i-search, remember? So just type the -binding @kbd{C-r} and then type something like @kbd{scor} to search -history backwards and arrive at the function call copied to the REPL -earlier. Type @kbd{RET} once to confirm that's the call your after, -and @kbd{RET} again to evaluate it. Because those @code{#v...} -backreferences are still trained specifically on those very same -function arguments, you can be sure that the function call is -equivalent. - -We can now use the @kbd{C-c C-s C-r} to @emph{replay} the sticker -recordings of this last function call. This is a kind of slow -walk-through conducted in separate navigation window -called @code{*sly-stickers-replay*} which pops up. There we can see the -Lisp value(s) that each sticker @code{eval}’ed to each time (or a note -if it exited non-locally). We can navigate recordings with @kbd{n} and -@kbd{p}, and do the usual things allowed by interactive objects like -inspecting them and returning them to the REPL. If you need help, toggle -help by typing @kbd{h}. There are lots of options here for navigating -stickers, ignoring some stickers, etc. When we’re done in this window, -we press @kbd{q} to quit. - -@*@image{images/tutorial-6,350pt}@* - -Finally, we declare that we’re finished debugging @code{FLEX-MATCHES}. -Even though stickers don’t get saved to the file in any way, we decide -we’re not interested in them anymore. So let’s open the ``SLY'' menu in -the menu bar, find the ``Delete stickers from top-level form'' option -under the ``Stickers'' sub-menu, and click it. Alternatively, we could -have typed @kbd{C-u C-c C-s C-s}. - -@node Working with source files -@chapter Working with source files - -@SLY{}'s commands when editing a Lisp file are provided via -@code{sly-editing-mode}, a minor-mode used in conjunction with Emacs's -@code{lisp-mode}. - -This chapter describes @SLY{}’s commands for editing and working in -Lisp source buffers. There are, of course, more @SLY{}’s commands that -also apply to these buffers (@pxref{Common functionality}), but -with very few exceptions these commands will always be run from -a @code{.lisp} file. - -@menu -* Evaluation:: -* Compilation:: -* Autodoc:: -* Semantic indentation:: -* Reader conditionals:: -* Macro-expansion:: -@end menu - -@node Evaluation -@section Evaluating code - -These commands each evaluate a Common Lisp expression in a different -way. Usually they mimic commands for evaluating Emacs Lisp code. By -default they show their results in the echo area, but a prefix -argument @kbd{C-u} inserts the results into the current buffer, while -a negative prefix argument @kbd{M--} sends them to the kill ring. - -@table @kbd - -@kbditem{C-x C-e, sly-eval-last-expression} - -Evaluate the expression before point and show the result in the echo -area. - -@kbditem{C-M-x, sly-eval-defun} -Evaluate the current toplevel form and show the result in the echo -area. `C-M-x' treats `defvar' expressions specially. Normally, -evaluating a `defvar' expression does nothing if the variable it -defines already has a value. But `C-M-x' unconditionally resets the -variable to the initial value specified in the `defvar' expression. -This special feature is convenient for debugging Lisp programs. - -@end table - -If @kbd{C-M-x} or @kbd{C-x C-e} is given a numeric argument, it -inserts the value into the current buffer, rather than displaying it -in the echo area. - -@table @kbd -@kbditem{C-c :, sly-interactive-eval} -Evaluate an expression read from the minibuffer. - -@kbditem{C-c C-r, sly-eval-region} -Evaluate the region. - -@kbditem{C-c C-p, sly-pprint-eval-last-expression} -Evaluate the expression before point and pretty-print the result in a -fresh buffer. - -@kbditem{C-c E, sly-edit-value} -Edit the value of a setf-able form in a new buffer @file{*Edit <form>*}. -The value is inserted into a temporary buffer for editing and then set -in Lisp when committed with @kbd{C-c C-c}. - -@kbditem{C-c C-u, sly-undefine-function} -Undefine the function, with @code{fmakunbound}, for the symbol at -point. - -@end table - -@node Compilation -@section Compiling functions and files - -@cindex Compilation - -@SLY{} has fancy commands for compiling functions, files, and -packages. The fancy part is that notes and warnings offered by the -Lisp compiler are intercepted and annotated directly onto the -corresponding expressions in the Lisp source buffer. (Give it a try to -see what this means.) - -@table @kbd -@cindex Compiling Functions -@kbditem{C-c C-c, sly-compile-defun} -Compile the top-level form at point. The region blinks shortly to -give some feedback which part was chosen. - -With (positive) prefix argument the form is compiled with maximal -debug settings (@kbd{C-u C-c C-c}). With negative prefix argument it is compiled for -speed (@kbd{M-- C-c C-c}). If a numeric argument is passed set debug or speed settings -to it depending on its sign. - -The code for the region is executed after compilation. In principle, -the command writes the region to a file, compiles that file, and loads -the resulting code. - -This compilation may arm stickers (@pxref{Stickers}). - -@kbditem{C-c C-k, sly-compile-and-load-file} -Compile and load the current buffer's source file. If the compilation -step fails, the file is not loaded. It's not always easy to tell -whether the compilation failed: occasionally you may end up in the -debugger during the load step. - -With (positive) prefix argument the file is compiled with maximal -debug settings (@kbd{C-u C-c C-k}). With negative prefix argument it is compiled for -speed (@kbd{M-- C-c C-k}). If a numeric argument is passed set debug or speed settings -to it depending on its sign. - -This compilation may arm stickers (@pxref{Stickers}). - -@kbditem{C-c M-k, sly-compile-file} -Compile (but don't load) the current buffer's source file. - -@kbditem{C-c C-l, sly-load-file} -Load a Lisp file. This command uses the Common Lisp LOAD function. - -@cmditem{sly-compile-region} -Compile the selected region. - -This compilation may arm stickers (@pxref{Stickers}). - -@end table - -The annotations are indicated as underlining on source forms. The -compiler message associated with an annotation can be read either by -placing the mouse over the text or with the selection commands below. - -@table @kbd -@kbditem{M-n, sly-next-note} -Move the point to the next compiler note and displays the note. - -@kbditem{M-p, sly-previous-note} -Move the point to the previous compiler note and displays the note. - -@kbditem{C-c M-c, sly-remove-notes} -Remove all annotations from the buffer. - -@kbditem{C-x `, next-error} -Visit the next-error message. This is not actually a @SLY{} command -but @SLY{} creates a hidden buffer so that most of the Compilation -mode commands (@inforef{Compilation Mode,, emacs}) work similarly for -Lisp as for batch compilers. - -@end table - -@node Autodoc -@section Autodoc - -SLY automatically shows information about symbols near the point. For -function names the argument list is displayed, and for global -variables, the value. Autodoc is implemented by means -of @code{eldoc-mode} of Emacs. - -@table @kbd -@cmditem{sly-arglist NAME} -Show the argument list of the function NAME. - -@cmditem{sly-autodoc-mode} -Toggles autodoc-mode on or off according to the argument, and -toggles the mode when invoked without argument. -@cmditem{sly-autodoc-manually} -Like sly-autodoc, but when called twice, -or after sly-autodoc was already automatically called, -display multiline arglist. -@end table - -If @code{sly-autodoc-use-multiline-p} is set to non-nil, -allow long autodoc messages to resize echo area display. - -@code{autodoc-mode} is a SLY extension and can be turned off if you -so wish (@pxref{Extensions}) - -@node Semantic indentation -@section Semantic indentation - -@SLY{} automatically discovers how to indent the macros in your Lisp -system. To do this the Lisp side scans all the macros in the system and -reports to Emacs all the ones with @code{&body} arguments. Emacs then -indents these specially, putting the first arguments four spaces in and -the ``body'' arguments just two spaces, as usual. - -This should ``just work.'' If you are a lucky sort of person you needn't -read the rest of this section. - -To simplify the implementation, @SLY{} doesn't distinguish between -macros with the same symbol-name but different packages. This makes it -fit nicely with Emacs's indentation code. However, if you do have -several macros with the same symbol-name then they will all be indented -the same way, arbitrarily using the style from one of their -arglists. You can find out which symbols are involved in collisions -with: - -@example -(slynk:print-indentation-lossage) -@end example - -If a collision causes you irritation, don't have a nervous breakdown, -just override the Elisp symbol's @code{sly-common-lisp-indent-function} -property to your taste. @SLY{} won't override your custom settings, it -just tries to give you good defaults. - -A more subtle issue is that imperfect caching is used for the sake of -performance. @footnote{@emph{Of course} we made sure it was actually too -slow before making the ugly optimization.} - -In an ideal world, Lisp would automatically scan every symbol for -indentation changes after each command from Emacs. However, this is too -expensive to do every time. Instead Lisp usually just scans the symbols -whose home package matches the one used by the Emacs buffer where the -request comes from. That is sufficient to pick up the indentation of -most interactively-defined macros. To catch the rest we make a full scan -of every symbol each time a new Lisp package is created between commands --- that takes care of things like new systems being loaded. - -You can use @kbd{M-x sly-update-indentation} to force all symbols to -be scanned for indentation information. - -@node Reader conditionals -@section Reader conditional fontification - -@SLY{} automatically evaluates reader-conditional expressions, like -@code{#+linux}, in source buffers and ``grays out'' code that will be -skipped for the current Lisp connection. - -@node Macro-expansion -@section Macro-expansion commands - -@cindex Macros - -@table @kbd -@kbditem{C-c C-m, sly-expand-1} -Macroexpand (or compiler-macroexpand) the expression at point -once. If invoked with a prefix argument use macroexpand instead or -macroexpand-1 (or compiler-macroexpand instead of -compiler-macroexpand-1). - -@cmditem{sly-macroexpand-1} -Macroexpand the expression at point once. If invoked with a prefix -argument, use macroexpand instead of macroexpand-1. - -@kbditem{C-c M-m, sly-macroexpand-all} -Fully macroexpand the expression at point. - -@cmditem{sly-compiler-macroexpand-1} -Display the compiler-macro expansion of sexp at point. - -@cmditem{sly-compiler-macroexpand} -Repeatedly expand compiler macros of sexp at point. - -@cmditem{sly-format-string-expand} -Expand the format-string at point and display it. -With prefix arg, or if no string at point, prompt the user for a -string to expand. - -@end table - -Within a sly macroexpansion buffer some extra commands are provided -(these commands are always available but are only bound to keys in a -macroexpansion buffer). - -@table @kbd -@kbditem{C-c C-m, sly-macroexpand-1-inplace} -Just like sly-macroexpand-1 but the original form is replaced with -the expansion. - -@c @anchor{sly-macroexpand-1-inplace} -@kbditem{g, sly-macroexpand-1-inplace} -The last macroexpansion is performed again, the current contents of -the macroexpansion buffer are replaced with the new expansion. - -@kbditem{q, sly-temp-buffer-quit} -Close the expansion buffer. - -@kbditem{C-_, sly-macroexpand-undo} -Undo last macroexpansion operation. - -@end table - -@node Common functionality -@chapter Common functionality - -This chapter describes the commands available throughout -@SLY{}-enabled buffers, which are not only Lisp source buffers, but -every auxiliary buffer created by @SLY{}, such as the @REPL{}, -Inspector, etc (@pxref{SLY REPL and other special buffers}) In -general, it’s a good bet that if the buffer’s name starts with -@code{*sly-...*}, these commands and functionality will be available -there. - -@menu -* Finding definitions:: -* Cross-referencing:: -* Completion:: -* Interactive objects:: -* Documentation:: -* Multiple connections:: -* Disassembly:: -* Recovery:: -* Temporary buffers:: -* Multi-threading:: -@end menu - -@node Finding definitions -@section Finding definitions - -One of the most used keybindings across all of @SLY{} is the -familiar @kbd{M-.} binding for @kbd{sly-edit-definition}. - -Here's the gist of it: when pressed with the cursor over a symbol -name, that symbol's name definition is looked up by the Lisp process, -thus producing a Lisp source location, which might be a file, or a -file-less buffer. For convenience, a type of ``breadcrumb'' is left -behind at the original location where @kbd{M-.} was pressed, so that -another keybinding @kbd{M-,} takes the user back to the original -location. Thus multiple @kbd{M-.} trace a path through lisp sources -that can be traced back with an equal number of @kbd{M-,}. - -@table @kbd -@kbditem{M-., sly-edit-definition} -Go to the definition of the symbol at point. - -@item M-, -@itemx M-* -@itemx M-x sly-pop-find-definition-stack -@kindex M-, -@findex sly-pop-find-definition-stack -Go back to the point where @kbd{M-.} was invoked. This gives multi-level -backtracking when @kbd{M-.} has been used several times. - -@kbditem{C-x 4 ., sly-edit-definition-other-window} -Like @code{sly-edit-definition} but switches to the other window to -edit the definition in. - -@kbditem{C-x 5 ., sly-edit-definition-other-frame} -Like @code{sly-edit-definition} but opens another frame to edit the -definition in. -@end table - -The behaviour of the @kbd{M-.} binding is sometimes affected by the -type of symbol you are giving it. - -@itemize @bullet -@item -For single functions or variables, @kbd{M-.} immediately switches -the current window's buffer and position to the target @code{defun} or -@code{defvar}. - -@item -For symbols with more than one associated definition, say, generic -functions, the same @kbd{M-.} finds all methods and presents these -results in separate window displaying a special @code{*sly-xref*} -buffer (@pxref{Cross-referencing}). -@end itemize - -@node Cross-referencing -@section Cross-referencing - -Finding and presenting the definition of a function is actually the -most elementary aspect of broader @emph{cross-referencing} facilities -framework in @SLY{}. There are other types of questions about the -source code relations that you can ask the Lisp process.@footnote{This -depends on the underlying implementation of some of these facilities: -for systems with no built-in @acronym{XREF} support @SLY{} queries a -portable -@acronym{XREF} package, which is taken from the @cite{CMU AI -Repository} and bundled with @SLY{}.} - -The following keybindings behave much like the @kbd{M-.} keybinding -(@pxref{Finding definitions}): when pressed as is they make a query -about the symbol at point, but with a @kbd{C-u} prefix argument they -prompt the user for a symbol. Importantly, they always popup a -transient @code{*sly-xref*} buffer in a different window. - -@table @kbd -@kbditem{M-?, sly-edit-uses} -Find all the references to this symbol, whatever the type of that -reference. - -@kbditem{C-c C-w C-c, sly-who-calls} -Show function callers. - -@kbditem{C-c C-w C-w, sly-calls-who} -Show all known callees. - -@kbditem{C-c C-w C-r, sly-who-references} -Show references to global variable. - -@kbditem{C-c C-w C-b, sly-who-binds} -Show bindings of a global variable. - -@kbditem{C-c C-w C-s, sly-who-sets} -Show assignments to a global variable. - -@kbditem{C-c C-w C-m, sly-who-macroexpands} -Show expansions of a macro. - -@cmditem{sly-who-specializes} -Show all known methods specialized on a class. - -@end table - -There are two further ``List callers/callees'' commands that operate -by rummaging through function objects on the heap at a low-level to -discover the call graph. They are only available with some Lisp -systems, and are most useful as a fallback when precise @acronym{XREF} -information is unavailable. - -@table @kbd -@kbditem{C-c <, sly-list-callers} -List callers of a function. - -@kbditem{C-c >, sly-list-callees} -List callees of a function. - -@end table - -In the resulting @code{*sly-xref*} buffer, these commands are -available: - -@table @kbd -@kbditem{RET, sly-show-xref} -Show definition at point in the other window. Do not leave -the @code{*sly-xref} buffer. - -@kbditem{Space, sly-goto-xref} -Show definition at point in the other window and close -the @code{*sly-xref} buffer. - -@kbditem{C-c C-c, sly-recompile-xref} -Recompile definition at point. Uses prefix arguments like -@code{sly-compile-defun}. - -@kbditem{C-c C-k, sly-recompile-all-xrefs} -Recompile all definitions. Uses prefix arguments like -@code{sly-compile-defun}. - -@end table - -@node Completion -@section Auto-completion - -@cindex Completion -@cindex Symbol Completion - -Completion commands are used to complete a symbol or form based on -what is already present at point. Emacs has many completion mechanisms -that @SLY{} tries to mimic as much as possible. - -SLY provides two styles of completion. The choice between them happens -in the Emacs customization variable -@pxref{sly-complete-symbol-function}, which can be set to two values, -or methods: - -@enumerate -@item @code{sly-flex-completions} -This method is speculative. It assumes that the letters you've already -typed aren't necessarily an exact prefix of the symbol you're thinking -of. Therefore, any possible completion that contains these letters, in -the order that you have typed them, is potentially a match. Completion -matches are then sorted according to a score that should reflect the -probability that you really meant that them. - -Flex completion implies that the package-qualification needed to -access some symbols is automatically discovered for you. However, to -avoid searching too many symbols unnecessarily, this method makes some -minimal assumptions that you can override: it assumes, for example, -that you don't normally want to complete to fully qualified internal -symbols, but will do so if it finds two consecutive colons (@code{::}) -in your initial pattern. Similarly, it assumes that if you start a -completion on a word starting @code{:}, you must mean a keyword (a -symbol from the keyword package.) - -Here are the top results for some typical searches. - -@example -CL-USER> (quiloa<TAB>) -> (ql:quickload) -CL-USER> (mvbind<TAB>) -> (multiple-value-bind) -CL-USER> (scan<TAB>) -> (ppcre:scan) -CL-USER> (p::scan<TAB>) -> (ppcre::scanner) -CL-USER> (setf locadirs<TAB>) -> (setf ql:*local-project-directories*) -CL-USER> foobar -> asdf:monolithic-binary-op -@end example - -@item @code{sly-simple-completions} -This method uses ``classical'' completion on an exact prefix. Although -poorer, this is simpler, more predictable and closer to the default -Emacs completion method. You type a prefix for a symbol reference and -@SLY{} let's you choose from symbols whose beginnings match it -exactly. -@end enumerate - -As an enhancement in @SLY{} over Emacs' built-in completion styles, -when the @code{*sly-completions*} buffer pops up, some keybindings are -momentarily diverted to it: - -@table @kbd -@item C-n -@itemx <down> -@itemx M-x sly-next-completion -@kindex C-n -@findex sly-next-completion -Select the next completion. - -@item C-p -@itemx <up> -@itemx M-x sly-prev-completion -@kindex C-p -@findex sly-prev-completion -Select the previous completion. - -@item tab -@itemx RET -@itemx M-x sly-choose-completion -@kindex tab -@findex sly-choose-completion -Choose the currently selected completion and enter it at point. -@end table - -As soon as the user selects a completion or gives up by -pressing @kbd{C-g} or moves out of the symbol being completed, the -@code{*sly-completions*} buffer is closed. - -@node Interactive objects -@section Interactive objects - -In many buffers and modes in @SLY{}, there are snippets of text that -represent objects ``living'' in the Lisp process connected to @SLY{}. -These regions are known in @SLY{} as interactive values or objects. -You can tell these objects from regular text by their distinct -``face'', is Emacs parlance for text colour, or decoration. Another -way to check if bit of text is an interactive object is to hover above -it with the mouse and right-click (@kbd{<mouse-3>}) it: a context menu -will appear listing actions that you can take on that object. - -@c Yet another way to discover these objects is -@c with @kbd{sly-button-forward} and @kbd{sly-button-backward} - -Depending on the mode, different actions may be active for different -types of objects. Actions can also be invoked using keybindings active -only when the cursor is on the button. - -@table @kbd - -@item @kbd{M-RET}, ``Copy to REPL'' - -Copy the object to the main @REPL{} (@pxref{REPL output} and @pxref{REPL -backreferences}). - -@item @kbd{M-S-RET}, ``Copy call to REPL'' - -An experimental feature. On some backtrace frames in the Debugger -(@pxref{Debugger}) and Trace Dialog (@pxref{Trace Dialog}), copy -the object to the main @REPL{}. That’s @emph{meta-shift-return}, by -the way, there’s no capital ``S''. - -@item @kbd{.},''Go To Source'' - -For function symbols, debugger frames, or traced function calls, go to -the Lisp source, much like with @kbd{M-.}. - -@item @kbd{v},''Show Source'' - -For function symbols, debugger frames, or traced function calls, show -the Lisp source in another window, but don’t switch to it. - -@item @kbd{p},''Pretty Print'' - -Pretty print the object in a separate buffer, much -like @code{sly-pprint-eval-last-expression}. - -@item @kbd{i},''Inspect'' - -Inspect the object in a separate inspector buffer (@pxref{Inspector}). - -@item @kbd{d},''Describe'' - -Describe the object in a separate buffer using Lisp’s -@code{CL:DESCRIBE}. - -@end table - -@node Documentation -@section Documentation commands - -@SLY{}'s online documentation commands follow the example of Emacs -Lisp. The commands all share the common prefix @kbd{C-c C-d} and allow -the final key to be modified or unmodified (@pxref{Keybindings}.) - -@table @kbd - -@cmditem{sly-info} -This command should land you in an electronic version of this very -manual that you can read inside Emacs. - -@kbditem{C-c C-d C-d, sly-describe-symbol} -Describe the symbol at point. - -@kbditem{C-c C-d C-f, sly-describe-function} -Describe the function at point. - -@kbditem{C-c C-d C-a, sly-apropos} -Perform an apropos search on Lisp symbol names for a regular expression -match and display their documentation strings. By default the external -symbols of all packages are searched. With a prefix argument you can choose a -specific package and whether to include unexported symbols. - -@kbditem{C-c C-d C-z, sly-apropos-all} -Like @code{sly-apropos} but also includes internal symbols by default. - -@kbditem{C-c C-d C-p, sly-apropos-package} -Show apropos results of all symbols in a package. This command is for -browsing a package at a high-level. With package-name completion it -also serves as a rudimentary Smalltalk-ish image-browser. - -@kbditem{C-c C-d C-h, sly-hyperspec-lookup} -Lookup the symbol at point in the @cite{Common Lisp Hyperspec}. This -uses the familiar @file{hyperspec.el} to show the appropriate section -in a web browser. The Hyperspec is found either on the Web or in -@code{common-lisp-hyperspec-root}, and the browser is selected by -@code{browse-url-browser-function}. - -Note: this is one case where @kbd{C-c C-d h} is @emph{not} the same as -@kbd{C-c C-d C-h}. - -@kbditem{C-c C-d ~, hyperspec-lookup-format} -Lookup a @emph{format character} in the @cite{Common Lisp Hyperspec}. - -@kbditem{C-c C-d #, hyperspec-lookup-reader-macro} -Lookup a @emph{reader macro} in the @cite{Common Lisp Hyperspec}. -@end table - -@node Multiple connections -@section Multiple connections - -@SLY{} is able to connect to multiple Lisp processes at the same -time. The @kbd{M-x sly} command, when invoked with a prefix -argument, will offer to create an additional Lisp process if one is -already running. This is often convenient, but it requires some -understanding to make sure that your @SLY{} commands execute in the -Lisp that you expect them to. - -Some @SLY{} buffers are tied to specific Lisp processes. It’s easy -read that from the buffer’s name which will usually be -@code{*sly-<something> for <connection>*}, where @code{connection} is -the name of the connection. - -Each Lisp connection has its own main @acronym{REPL} buffer -(@pxref{REPL}), and all expressions entered or @SLY{} commands invoked -in that buffer are sent to the associated connection. Other buffers -created by @SLY{} are similarly tied to the connections they originate -from, including @SLY-DB{} buffers (@pxref{Debugger}), apropos result -listings, and so on. These buffers are the result of some interaction -with a Lisp process, so commands in them always go back to that same -process. - -Commands executed in other places, such as @code{sly-mode} source -buffers, always use the ``default'' connection. Usually this is the -most recently established connection, but this can be reassigned via -the ``connection list'' buffer: - -@table @kbd -@kbditem{C-c C-x c, sly-list-connections} -Pop up a buffer listing the established connections. - -@kbditem{C-c C-x n, sly-next-connection} -Switch to the next Lisp connection by cycling through all connections. - -@kbditem{C-c C-x p, sly-prev-connection} -Switch to the previous Lisp connection by cycling through all connections. - -@end table - -The buffer displayed by @code{sly-list-connections} gives a one-line -summary of each connection. The summary shows the connection's serial -number, the name of the Lisp implementation, and other details of the -Lisp process. The current ``default'' connection is indicated with an -asterisk. - -The commands available in the connection-list buffer are: - -@table @kbd -@kbditem{RET, sly-goto-connection} -Pop to the @acronym{REPL} buffer of the connection at point. - -@kbditem{d, sly-connection-list-make-default} -Make the connection at point the ``default'' connection. It will then -be used for commands in @code{sly-mode} source buffers. - -@kbditem{g, sly-update-connection-list} -Update the connection list in the buffer. - -@kbditem{q, sly-temp-buffer-quit} -Quit the connection list (kill buffer, restore window configuration). - -@kbditem{R, sly-restart-connection-at-point} -Restart the Lisp process for the connection at point. - -@cmditem{sly-connect} -Connect to a running Slynk server. With prefix argument, asks if all -connections should be closed first. - -@cmditem{sly-disconnect} -Disconnect all connections. - -@cmditem{sly-abort-connection} -Abort the current attempt to connect. - -@end table - -@node Disassembly -@section Disassembly commands - -@table @kbd - -@kbditem{C-c M-d, sly-disassemble-symbol} -Disassemble the function definition of the symbol at point. - -@kbditem{C-c C-t, sly-toggle-trace-fdefinition} -Toggle tracing of the function at point. If invoked with a prefix -argument, read additional information, like which particular method -should be traced. - -@cmditem{sly-untrace-all} -Untrace all functions. - -@end table - -@node Recovery -@section Abort/Recovery commands - -@table @kbd -@kbditem{C-c C-b, sly-interrupt} -Interrupt Lisp (send @code{SIGINT}). - -@cmditem{sly-restart-inferior-lisp} -Restart the @code{inferior-lisp} process. - -@kbditem{C-c ~, sly-mrepl-sync} -Synchronize the current package and working directory from Emacs to -Lisp. - -@cmditem{sly-cd} -Set the current directory of the Lisp process. This also -changes the current directory of the REPL buffer. - -@cmditem{sly-pwd} -Print the current directory of the Lisp process. - -@end table - -@node Temporary buffers -@section Temporary buffers - -Some @SLY{} commands create temporary buffers to display their -results. Although these buffers usually have their own special-purpose -major-modes, certain conventions are observed throughout. - -Temporary buffers can be dismissed by pressing @kbd{q}. This kills the -buffer and restores the window configuration as it was before the -buffer was displayed. Temporary buffers can also be killed with the -usual commands like @code{kill-buffer}, in which case the previous -window configuration won't be restored. - -Pressing @kbd{RET} is supposed to ``do the most obvious useful -thing.'' For instance, in an apropos buffer this prints a full -description of the symbol at point, and in an @acronym{XREF} buffer it -displays the source code for the reference at point. This convention -is inherited from Emacs's own buffers for apropos listings, -compilation results, etc. - -Temporary buffers containing Lisp symbols use @code{sly-mode} in -addition to any special mode of their own. This makes the usual -@SLY{} commands available for describing symbols, looking up -function definitions, and so on. - -Initial focus of those ``description'' buffers depends on the variable -@code{sly-description-autofocus}. If @code{nil} (the default), -description buffers do not receive focus automatically, and vice -versa. - -@node Multi-threading -@section Multi-threading - -If the Lisp system supports multi-threading, SLY spawns a new thread -for each request, e.g., @kbd{C-x C-e} creates a new thread to evaluate -the expression. An exception to this rule are requests from the -@REPL{}: all commands entered in the @REPL{} buffer are evaluated in a -dedicated @REPL{} thread. - -You can see a listing of the threads for the current connection with -the command @code{M-x sly-list-threads}, or @code{C-c C-x t}. This -pops open a @code{*sly-threads*} buffer, where some keybindings to -control threads are active, if you know what you are doing. The most -useful is probably @kbd{k} to kill a thread, but type @kbd{C-h m} in -that buffer to get a full listing. - -Some complications arise with multi-threading and special variables. -Non-global special bindings are thread-local, e.g., changing the value -of a let bound special variable in one thread has no effect on the -binding of the variables with the same name in other threads. This -makes it sometimes difficult to change the printer or reader behaviour -for new threads. The variable -@code{slynk:*default-worker-thread-bindings*} was introduced for such -situations: instead of modifying the global value of a variable, add a -binding the @code{slynk:*default-worker-thread-bindings*}. E.g., with -the following code, new threads will read floating point values as -doubles by default: - -@example -(push '(*read-default-float-format* . double-float) - slynk:*default-worker-thread-bindings*). -@end example - -@node SLY REPL and other special buffers -@chapter SLY REPL and other special buffers - -@menu -* REPL:: -* Inspector:: -* Debugger:: -* Trace Dialog:: -* Stickers:: -@end menu - -@node REPL -@section SLY REPL: the ``top level'' - -@cindex Listener - -@SLY{} uses a custom Read-Eval-Print Loop (@REPL{}, also known as a -``top level'', or listener): - -@itemize @bullet -@item -Conditions signalled in @REPL{} expressions are debugged with the -integrated SLY debugger. -@item -Return values are interactive values (@pxref{Interactive objects}) -distinguished from printed output by separate Emacs faces (colors). -@item -Output from the Lisp process is inserted in the right place, and -doesn't get mixed up with user input. -@item -Multiple @REPL{}s are possible in the same Lisp connection. This is -useful for performing quick one-off experiments in different packages -or directories without disturbing the state of an existing REPL. -@item -The REPL is a central hub for much of SLY's functionality, since -objects examined in the inspector (@pxref{Inspector}), debugger -(@pxref{Debugger}), and other extensions can be returned there. -@end itemize - -Switching to the @REPL{} from anywhere in a @SLY{} buffer is a very -common task. One way to do it is to find the @code{*sly-mrepl...*} -buffer in Emacs’s buffer list, but there are other ways to reach a -@REPL{}. - -@table @kbd -@kbditem{C-c C-z, sly-mrepl} -Start or select an existing main @REPL{} buffer. - -@cmditem{sly-mrepl-new} -Start a new secondary @REPL session, prompting for a nickname. - -@kbditem{C-c ~, sly-mrepl-sync} -Go to the REPL, switching package and default directory as -applicable. More precisely the Lisp variables @code{*package*} and -@code{*default-pathname-defaults*} are affected by the location -where the command was issued. In a specific position of a @code{.lisp} -file, for instance the current package and that file’s directory are -chosen. -@end table - -@menu -* REPL commands:: -* REPL output:: -* REPL backreferences:: -@end menu - -@node REPL commands -@subsection REPL commands - -@table @kbd - -@kbditem{RET, sly-mrepl-return} - -Evaluate the expression at prompt and return the result. - -@kbditem{TAB, sly-mrepl-indent-and-complete-symbol} - -Indent the current line. If line already indented complete the symbol -at point (@pxref{Completion}). If there is not symbol at point show -the argument list of the most recently enclosed function or macro in -the minibuffer. - -@kbditem{M-p, sly-mrepl-previous-input-or-button} - -When at the current prompt, fetches previous input from the history, -otherwise jumps to the previous interactive value (@pxref{Interactive -objects}) representing a Lisp object. - -@kbditem{M-n, sly-mrepl-next-input-or-button} - -When at the current prompt, fetches next input from the history, -otherwise jumps to the previous interactive value representing a -Lisp object. - -@kbditem{C-r, isearch-backward} - -This regular Emacs keybinding, when invoked at the current @REPL{} -prompt, starts a special transient mode turning the prompt into the -string ``History-isearch backward''. While in this mode, the user can -compose a string used to search backwards through history, and reverse -the direction of search by pressing @kbd{C-s}. When invoked outside -the current @REPL{} prompt, does a normal text search through the -buffer contents. - -@kbditem{C-c C-b, sly-interrupt} - -Interrupts the current thread of the inferior-lisp process. - -For convenience this function is also bound to @kbd{C-c C-c}. - -@kbditem{C-M-p, sly-button-backward} - -Jump to the previous interactive value representing a Lisp object. - -@kbditem{C-M-n, sly-button-forward} - -Jump to the next interactive value representing a Lisp object. - -@kbditem{C-c C-o, sly-mrepl-clear-recent-output} - -Clear output between current and last REPL prompts, keeping results. - -@kbditem{C-c M-o, sly-mrepl-clear-repl} - -Clear the whole REPL of output and results. - -@end table - -@node REPL output -@subsection REPL output - -REPLs wouldn’t be much use if they just took user input and didn’t -print anything back. In @SLY{} the output printed to the REPL can -come from four different places: - -@itemize @bullet - -@item -A function’s return values. One line per return value is printed. Each -line of printed text, called a @REPL{} result, persists after more -expressions are evaluated, and is actually a button -(@pxref{Interactive objects}) presenting the Lisp-side object. You -can, for instance, inspect it (@pxref{Inspector}) or re-return it to -right before the current command prompt so that you may conjure it up -again, as usual in Lisp @REPL{}s, with the special variable @code{*}. - -In the @SLY{} @REPL{}, in addition to the @code{*}, @code{**} -and @code{***} special variables, return values can also be accessed -through a special backreference (@pxref{REPL backreferences}). - -@item -An object may be copied to the REPL from some other part in @SLY{}, -such as the Inspector (@pxref{Inspector}), Debugger -(@pxref{Debugger}), etc. using the familiar @kbd{M-RET} binding, or by -selecting ``Copy to REPL'' from the context menu of an interactive -object. Aside from not having been produced by the evaluation of a -Lisp form in the @REPL{}, these objects behaves exactly like a @REPL{} -result. - -@item -The characters printed to the standard Lisp streams -@code{*standard-output*}, @code{*error-output*} -and @code{*trace-output*} as a @emph{synchronous} and direct result of -the evaluation of an expression in the @REPL{}. - -@item -The characters printed to the standard Lisp streams -@code{*standard-output*}, @code{*error-output*} and -@code{*trace-output*} printed, perhaps @emph{asynchronously}, -from others threads, for instance. This feature is optional and -controlled by the variable @code{SLYNK:*GLOBALLY-REDIRECT-IO*}. -@end itemize - -@noindent -For advanced users, there are some Lisp-side Slynk variables affecting -the way Slynk transmits @REPL{} output to @SLY{}. - -@table @code -@item @code{SLYNK:*GLOBALLY-REDIRECT-IO*} - -This variable controls the global redirection of the the standard -streams (@code{*standard-output*}, etc) to the @REPL{} in Emacs. The -default value is @code{:started-from-emacs}, which means that -redirection should only take place upon @code{M-x sly} invocations. -When @code{t}, global redirection happens even for sessions started with -@code{M-x sly-connect}, meaning output may be diverted from wherever -you started the Lisp server originally. - -When @code{NIL} these streams are only temporarily redirected to Emacs -using dynamic bindings while handling requests, meaning you only see -output caused by the commands you issued to the REPL. - -Note that @code{*standard-input*} is currently never globally redirected -into Emacs, because it can interact badly with the Lisp's native @REPL{} -by having it try to read from the Emacs one. - -Also note that secondary @REPL{}s (those started with -@kbd{sly-mrepl-new}) don’t receive any redirected output. - -@item @code{SLYNK:*USE-DEDICATED-OUTPUT-STREAM*} - -This variable controls whether to use a separate socket solely for Lisp -to send printed output to Emacs through, which is more efficient than -sending the output in protocol messages to Emacs. - -The default value is @code{:started-from-emacs}, which means that the -socket should only be established upon @code{M-x sly} invocations. When -@code{t}, it's established even for sessions started with @code{M-x -sly-connect}. When @code{NIL} usual protocol messages are used for -sending input to the @REPL{}. - -Notice that using a dedicated output stream makes it more difficult to -communicate to a Lisp running on a remote host via SSH -(@pxref{Connecting to a remote Lisp}). If you connect via @code{M-x -sly-connect}, the default @code{:started-from-emacs} value should ensure -this isn't a problem. - -@item @code{SLYNK:*DEDICATED-OUTPUT-STREAM-PORT*} - -When @code{*USE-DEDICATED-OUTPUT-STREAM*} is @code{t} the stream will -be opened on this port. The default value, @code{0}, means that the -stream will be opened on some random port. - -@item @code{SLYNK:*DEDICATED-OUTPUT-STREAM-BUFFERING*} - -For efficiency, some Lisps backends wait until a certain conditions -are met in a Lisp character stream before flushing that stream’s -contents, thus sending it to the @SLY{} @REPL{}. Be advised that this -sometimes works poorly on some implementations, so it’s probably best -to leave alone. Possible values are @code{nil} (no -buffering), @code{t} (enable buffering) or @code{:line} (enable -buffering on EOL) -@end table - -@node REPL backreferences -@subsection REPL backreferences - -In a regular Lisp REPL, the objects produced by evaluating expressions -at the command prompt can usually be referenced in future commands -using the special variables @code{*}, @code{**} and @code{***}. This -is also true of the @SLY{} @REPL{}, but it also provides a different -way to re-conjure these objects through a special Lisp reader macro -character available only in the REPL. The macro character, which -is @code{#v} by default takes, in a terse syntax, two indexes -specifying the precise objects in all of the @SLY{} @REPL{}’s recorded -history. - -Consider this fragment of a REPL session: - -@example -; Cleared REPL history -CL-USER> (values 'a 'b 'c) -A -B -C -CL-USER> (list #v0) -(A) -CL-USER> (list #v0:1 #v0:2) -(B C) -CL-USER> (append #v1:0 #v2:0) -(A B C) -CL-USER> -@end example - -@noindent -Admittedly, while useful, this doesn’t seem terribly easy to use at -first sight. There are a couple of reasons, however, that should make -it worth considering: - -@itemize @bullet -@item -Backreference annotation and highlighting - -As soon as the @SLY{} @REPL{} detects that you have pressed @code{#v}, -all the @REPL{} results that can possibly be referenced are -temporarily annotated on their left with two special numbers. These -numbers are in the syntax accepted by the @code{#v} macro-character, -namely @code{#vENTRY-IDX:VALUE-IDX}. - -Furthermore, as soon as you type a number for @code{ENTRY-IDX}, only -that entries values remain highlighted. Then, as you finish the entry -with @code{VALUE-IDX}, only that exact object remains highlighted. If -you make a mistake (say, by typing a letter or an invalid number) -while composing @code{#v} syntax, @SLY{} lets you know by painting the -backreference red. - -Highlighting also happens when you place the cursor over existing -valid @code{#v} expressions. - -@item -Returning functions calls - -An experimental feature in @SLY{} allows copying @emph{function calls} -to the @REPL{} from the Debugger (@pxref{Debugger}) and the Trace -Dialog (@pxref{Trace Dialog}). In those buffers, pressing -keybinding @kbd{M-S-RET} over objects that represent function calls -will copy the @emph{call}, and not the object, to the @REPL{}. This -works by first copying over the argument objects in order to the -@REPL{} results, and then composing an input line that includes the -called function's name and backreferences to those arguments -(@pxref{REPL backreferences}). - -Naturally, this call isn't @emph{exactly} the same because it doesn’t -evaluate in the same dynamic environment as the original one. But it's -a useful debug technique because backreferences are stable -@footnote{until you clear the @REPL{}’s output, that is}, so repeating -that very same function call with the very same arguments is just a -matter of textually copying the previous expression into the command -prompt, no matter how far ago it happened. And that, in turn, -is as easy as using @kbd{C-r} and some characters (@pxref{REPL -commands}) to arrive and repeat the desired @REPL{} history entry. -@end itemize - -@node Inspector -@section The Inspector - -The @SLY{} inspector is a Emacs-based alternative to the -standard @code{INSPECT} function. The inspector presents objects in -Emacs buffers using a combination of plain text, hyperlinks to related -objects. - -The inspector can easily be specialized for the objects in your own -programs. For details see the @code{inspect-for-emacs} generic -function in @file{slynk-backend.lisp}. - -@table @kbd - -@kbditem{C-c I, sly-inspect} -Inspect the value of an expression entered in the minibuffer. - -@end table - -The standard commands available in the inspector are: - -@table @kbd - -@kbditem{RET, sly-inspector-operate-on-point} -If point is on a value then recursively call the inspector on that -value. If point is on an action then call that action. - -@kbditem{D, sly-inspector-describe-inspectee} -Describe the slot at point. - -@kbditem{e, sly-inspector-eval} -Evaluate an expression in the context of the inspected object. The -variable @code{*} will be bound to the inspected object. - -@kbditem{v, sly-inspector-toggle-verbose} -Toggle between verbose and terse mode. Default is determined by -`slynk:*inspector-verbose*'. - -@kbditem{l, sly-inspector-pop} -Go back to the previous object (return from @kbd{RET}). - -@kbditem{n, sly-inspector-next} -The inverse of @kbd{l}. Also bound to @kbd{SPC}. - -@kbditem{g, sly-inspector-reinspect} -Reinspect. - -@kbditem{h, sly-inspector-history} -Show the previously inspected objects. - -@kbditem{q, sly-inspector-quit} -Dismiss the inspector buffer. - -@kbditem{>, sly-inspector-fetch-all} -Fetch all inspector contents and go to the end. - -@kbditem{M-RET, sly-mrepl-copy-part-to-repl} -Store the value under point in the variable `*'. This can -then be used to access the object in the REPL. - -@kbditempair{TAB, S-TAB, forward-button, backward-button} - -Jump to the next and previous inspectable object respectively. - -@end table - -@node Debugger -@section The SLY-DB Debugger - -@cindex Debugger - -@SLY{} has a custom Emacs-based debugger called @SLY-DB{}. Conditions -signalled in the Lisp system invoke @SLY-DB{} in Emacs by way of the -Lisp @code{*DEBUGGER-HOOK*}. - -@SLY-DB{} pops up a buffer when a condition is signalled. The buffer -displays a description of the condition, a list of restarts, and a -backtrace. Commands are offered for invoking restarts, examining the -backtrace, and poking around in stack frames. - -@menu -* Examining frames:: -* Restarts:: -* Frame Navigation:: -* Miscellaneous:: -@end menu - -@node Examining frames -@subsection Examining frames - -Commands for examining the stack frame at point. - -@table @kbd -@kbditem{t, sly-db-toggle-details} -Toggle display of local variables and @code{CATCH} tags. - -@kbditem{v, sly-db-show-frame-source} -View the frame's current source expression. The expression is -presented in the Lisp source file's buffer. - -@kbditem{e, sly-db-eval-in-frame} -Evaluate an expression in the frame. The expression can refer to the -available local variables in the frame. - -@kbditem{d, sly-db-pprint-eval-in-frame} -Evaluate an expression in the frame and pretty-print the result in a -temporary buffer. - -@kbditem{D, sly-db-disassemble} -Disassemble the frame's function. Includes information such as the -instruction pointer within the frame. - -@kbditem{i, sly-db-inspect-in-frame} -Inspect the result of evaluating an expression in the frame. - -@kbditem{C-c C-c, sly-db-recompile-frame-source} -Recompile frame. @kbd{C-u C-c C-c} for recompiling with maximum debug settings. - -@end table - -@node Restarts -@subsection Invoking restarts - -@table @kbd -@kbditem{a, sly-db-abort} -Invoke the @code{ABORT} restart. - -@anchor{sly-db-quit} -@kbditem{q, sly-db-quit} -``Quit'' -- For @SLY{} evaluation requests, invoke a restart which -restores to a known program state. For errors in other threads, -@xref{*SLY-DB-QUIT-RESTART*}. - -@kbditem{c, sly-db-continue} -Invoke the @code{CONTINUE} restart. - -@kbditem{0 ... 9, sly-db-invoke-restart-n} -Invoke a restart by number. -@end table - -Restarts can also be invoked by pressing @kbd{RET} or @kbd{Mouse-2} on -them in the buffer. - -@node Frame Navigation -@subsection Navigating between frames - -@table @kbd -@kbditempair{n,p,sly-db-down,sly-db-up} -Move between frames. - -@kbditempair{M-n, M-p, sly-db-details-down, sly-db-details-up} -Move between frames ``with sugar'': hide the details of the original -frame and display the details and source code of the next. Sugared -motion makes you see the details and source code for the current frame -only. - -@kbditem{>, sly-db-end-of-backtrace} -Fetch the entire backtrace and go to the last frame. - -@kbditem{<, sly-db-beginning-of-backtrace} -Go to the first frame. - -@end table - -@node Miscellaneous -@subsection Miscellaneous Commands - -@table @kbd -@kbditem{r, sly-db-restart-frame} -Restart execution of the frame with the same arguments it was -originally called with. (This command is not available in all -implementations.) - -@kbditem{R, sly-db-return-from-frame} -Return from the frame with a value entered in the minibuffer. (This -command is not available in all implementations.) - - -@kbditem{B, sly-db-break-with-default-debugger} -Exit @SLY-DB{} and debug the condition using the Lisp system's default -debugger. - -@kbditem{C, sly-db-inspect-condition} -Inspect the condition currently being debugged. - -@kbditem{:, sly-interactive-eval} -Evaluate an expression entered in the minibuffer. -@kbditem{A, sly-db-break-with-system-debugger} -Attach debugger (e.g. gdb) to the current lisp process. - -@end table - -@node Trace Dialog -@section Trace Dialog - -The @SLY{} Trace Dialog, in package @code{sly-trace-dialog}, is a -tracing facility, similar to Common Lisp's @code{trace}, but -interactive rather than purely textual. - -You use it just like you would regular @code{trace}: after tracing a -function, calling it causes interesting information about that -particular call to be reported. - -However, instead of printing the trace results to the -the @code{*trace-output*} stream (usually the REPL), the @SLY{} Trace -Dialog collects and stores them in your Lisp environment until, on -user's request, they are fetched into Emacs and displayed in a -dialog-like interactive view. - -After starting up @SLY{}, @SLY{}'s Trace Dialog installs -a @emph{Trace} menu in the menu-bar of any @code{sly-mode} buffer and -adds two new commands, with respective key-bindings: - -@table @kbd -@kbditem{C-c C-t, sly-trace-dialog-toggle-trace} -If point is on a symbol name, toggle tracing of its function -definition. If point is not on a symbol, prompt user for a function. - -With a @kbd{C-u} prefix argument, and if your lisp implementation -allows it, attempt to decipher lambdas, methods and other complicated -function signatures. - -The function is traced for the @SLY{} Trace Dialog only, i.e. it is -not found in the list returned by Common Lisp's @code{trace}. - -@kbditem{C-c T, sly-trace-dialog} -Pop to the interactive Trace Dialog buffer associated with the -current connection (@pxref{Multiple connections}). -@end table - -@page -Consider the (useless) program: - -@example -(defun foo (n) (if (plusp n) (* n (bar (1- n))) 1)) -(defun bar (n) (if (plusp n) (* n (foo (1- n))) 1)) -@end example - -After tracing both @code{foo} and @code{bar} with @kbd{C-c M-t}, -calling call @code{(foo 2)} and moving to the trace dialog with -@kbd{C-c T}, we are presented with this buffer. - -@example -Traced specs (2) [refresh] - [untrace all] - [untrace] common-lisp-user::bar - [untrace] common-lisp-user::foo - -Trace collection status (3/3) [refresh] - [clear] - - 0 - common-lisp-user::foo - | > 2 - | < 2 - 1 `--- common-lisp-user::bar - | > 1 - | < 1 - 2 `-- common-lisp-user::foo - > 0 - < 1 -@end example - -The dialog is divided into sections displaying the functions already -traced, the trace collection progress and the actual trace tree that -follow your program's logic. The most important key-bindings in this -buffer are: - -@table @kbd -@kbditem{g, sly-trace-dialog-fetch-status} -Update information on the trace collection and traced specs. -@kbditem{G, sly-trace-dialog-fetch-traces} -Fetch the next batch of outstanding (not fetched yet) traces. With a -@kbd{C-u} prefix argument, repeat until no more outstanding traces. -@kbditem{C-k, sly-trace-dialog-clear-fetched-traces} -Prompt for confirmation, then clear all traces, both fetched and -outstanding. -@end table - -The arguments and return values below each entry are interactive -buttons. Clicking them opens the inspector -(@pxref{Inspector}). Invoking @kbd{M-RET} -(@code{sly-trace-dialog-copy-down-to-repl}) returns them to the REPL -for manipulation (@pxref{REPL}). The number left of each entry -indicates its absolute position in the calling order, which might -differ from display order in case multiple threads call the same -traced function. - -@code{sly-trace-dialog-hide-details-mode} hides arguments and return -values so you can concentrate on the calling logic. Additionally, -@code{sly-trace-dialog-autofollow-mode} will automatically -display additional detail about an entry when the cursor moves over -it. - -@node Stickers -@section Stickers - -@SLY{} Stickers, implemented as the @code{sly-stickers} contrib -(@pxref{Extensions}), is a tool for ``live'' code annotations. It's an -alternative to the @code{print} or @code{break} statements you add to -your code when debugging. - -Contrary to these techniques, ``stickers'' are non-intrusive, meaning -that saving your file doesn't save your debug code along with it. - -Here's the general workflow: - -@itemize @bullet -@item -In Lisp source files, using @kbd{C-c C-s C-s} or @code{M-x -sly-stickers-dwim} places a sticker on any Lisp form. Stickers can -exist inside other stickers. - -@*@image{images/stickers-1-placed-stickers,350pt}@* - -@item -Stickers are ``armed'' when a definition or a file is compiled with the -familiar @kbd{C-c C-c} (@code{M-x sly-compile-defun}) or @kbd{C-c C-k} -(@code{M-x sly-compile-file}) commands. An armed sticker changes color from -the default grey background to a blue background. - -@*@image{images/stickers-2-armed-stickers,350pt}@* - -@end itemize - -From this point on, when the Lisp code is executed, the results of -evaluating the underlying forms are captured in the Lisp -side. Stickers help you examine your program's behaviour in three -ways: -@enumerate -@item -@kbd{C-c C-s C-r} -(or @code{M-x sly-stickers-replay}) interactively walks the user -through recordings in the order that they occurred. In the -created @code{*sly-stickers-replay*} buffer, type @kbd{h} for a list -of keybindings active in that buffer. - -@*@image{images/stickers-3-replay-stickers,350pt}@* - -@item -To step through stickers as your code is executed, ensure that -``breaking stickers'' are enabled via @code{M-x -sly-stickers-toggle-break-on-stickers}. Whenever a sticker-covered -expression is reached, the debugger comes up with useful restarts and -interactive for the values produced. You can tweak this behaviour by -setting the Lisp-side variable -@code{SLYNK-STICKERS:*BREAK-ON-STICKERS*} to a list with the -elements @code{:before} and @code{:after}, making @SLY{} break before a -sticker, after it, or both. - -@*@image{images/stickers-4-breaking-stickers,350pt}@* - -@item -@kbd{C-c C-s S} (@code{M-x sly-stickers-fetch}) populates the sticker -overlay with the latest captured results, called ``recordings''. If a sticker -has captured any recordings, it will turn green, otherwise it will turn -red. A sticker whose Lisp expression has caused a non-local exit, will -be also be marked with a special face. - -@*@image{images/stickers-5-fetch-recordings,350pt}@* - -@end enumerate - -At any point, stickers can be removed with the -same @code{sly-stickers-dwim} keybinding, by placing the cursor at the -beginning of a sticker. Additionally adding prefix arguments -to @code{sly-stickers-dwim} increase its scope, so @kbd{C-u C-c C-s -C-s} will remove all stickers from the current function and @kbd{C-u -C-u C-c C-s C-s} will remove all stickers from the current file. - -Stickers can be nested inside other stickers, so it is possible to -record the value of an expression inside another expression which is -also annotated. - -Stickers are interactive parts just like any other part in @SLY{} that -represents Lisp-side objects, so they can be inspected and returned to -the REPL, for example. To move through the stickers with the keyboard -use the existing keybindings to move through compilation -notes (@kbd{M-p} and @kbd{M-n}) or use @kbd{C-c C-s p} and @kbd{C-c C-s -n} (@code{sly-stickers-prev-sticker} and -@code{sly-stickers-next-sticker}). - -There are some caveats when using @SLY{} Stickers: - -@itemize -@item -Stickers on unevaluated forms (such as @code{let} variable bindings, or -other constructs) are rejected, though the function is still compiled as -usual. To let the user know about this, these stickers remain grey, and are -marked as ``disarmed''. A message also appears in the echo area. -@item -Stickers placed on expressions inside backquoted expressions in macros -are always armed, even though they may come to provoke a runtime error -when the macro's expansion is run. Think of this when setting a sticker -inside a macro definition. -@end itemize - -@node Customization -@chapter Customization - -@menu -* Emacs-side:: -* Lisp-side customization:: -@end menu - -@node Emacs-side -@section Emacs-side - -@menu -* Keybindings:: -* Keymaps:: -* Defcustom variables:: -* Hooks:: -@end menu - -@node Keybindings -@subsection Keybindings - -In general we try to make our key bindings fit with the overall Emacs -style. - -We never bind @kbd{C-h} anywhere in a key sequence. This is because -Emacs has a built-in default so that typing a prefix followed -by @kbd{C-h} will display all bindings starting with that prefix, -so @kbd{C-c C-d C-h} will actually list the bindings for all -documentation commands. This feature is just a bit too useful to -clobber! - -@quotation -@i{``Are you deliberately spiting Emacs's brilliant online help facilities? The gods will be angry!''} -@end quotation - -@noindent This is a brilliant piece of advice. The Emacs online help facilities -are your most immediate, up-to-date and complete resource for keybinding -information. They are your friends: - -@table @kbd -@kbdanchorc{C-h k <key>, describe-key, ``What does this key do?''} -Describes current function bound to @kbd{<key>} for focus buffer. - -@kbdanchorc{C-h b, describe-bindings, ``Exactly what bindings are available?''} -Lists the current key-bindings for the focus buffer. - -@kbdanchorc{C-h m, describe-mode, ``Tell me all about this mode''} -Shows all the available major mode keys, then the minor mode keys, for -the modes of the focus buffer. - -@kbdanchorc{C-h l, view-lossage, ``Woah@comma{} what key chord did I just do?''} -Shows you the literal sequence of keys you've pressed in order. - -@c <key> is breaks links PDF, despite that it's not l it's C-h -@c @kbdanchorc{ <key> l, , ``What starts with?''} -@c Lists all keybindings that begin with @code{<key>} for the focus buffer mode. - - -@end table - -@anchor{Emacs Init File} -For example, you can add one of the following to your Emacs init file -(usually @file{~/.emacs} or @file{~/.emacs.d/init.el}, but @pxref{Init -File,Init File, Emacs Init File, emacs, The Emacs Manual}). - -@example -(eval-after-load 'sly - `(define-key sly-prefix-map (kbd "M-h") 'sly-documentation-lookup)) -@end example - -@SLY{} comes bundled with many extensions (called ``contribs'' for -historical reasons, @pxref{Extensions}) which you can customize just -like @SLY{}'s code. To make @kbd{C-c C-c} clear the last REPL prompt's -output, for example, use - -@example -(eval-after-load 'sly-mrepl - `(define-key sly-mrepl-mode-map (kbd "C-c C-k") - 'sly-mrepl-clear-recent-output)) -@end example - -@node Keymaps -@subsection Keymaps - -Emacs’s keybindings ``live'' in keymap variables. To customize a -particular binding and keep it from trampling on other important keys -you should do it in one of @SLY{}'s keymaps. The following -non-exhaustive list of @SLY{}-related keymaps is just a reference: the -manual will go over each associated functionality in detail. - -@table @code - -@item sly-doc-map - -Keymap for documentation commands (@pxref{Documentation}) in -@SLY{}-related buffers, accessible by the @kbd{C-c C-d} prefix. - -@item sly-who-map - -Keymap for cross-referencing (``who-calls'') commands -(@pxref{Cross-referencing}) in @SLY{}-related buffers, accessible by -the @kbd{C-c C-w} prefix. - -@item sly-selector-map - -A keymap for @SLY{}-related functionality that should be available in -globally in all Emacs buffers (not just @SLY{}-related buffers). - -@item sly-mode-map - -A keymap for functionality available in all @SLY{}-related buffers. - -@item sly-editing-mode-map - -A keymap for @SLY{} functionality available in Lisp source files. - -@item sly-popup-buffer-mode-map - -A keymap for functionality available in the temporary ``popup'' -buffers that @SLY{} displays (@pxref{Temporary buffers}) - -@item sly-apropos-mode-map - -A keymap for functionality available in the temporary @SLY{} -``apropos'' buffers (@pxref{Documentation}). - -@item sly-xref-mode-map - -A keymap for functionality available in the temporary @code{xref} -buffers used by cross-referencing commands -(@pxref{Cross-referencing}). - -@item sly-macroexpansion-minor-mode-map - -A keymap for functionality available in the temporary buffers used for -macroexpansion presentation (@pxref{Macro-expansion}). - -@item sly-db-mode-map - -A keymap for functionality available in the debugger buffers used to -debug errors in the Lisp process (@pxref{Debugger}). - -@item sly-thread-control-mode-map - -A keymap for functionality available in the @SLY{} buffers dedicated -to controlling Lisp threads (@pxref{Multi-threading}). - -@item sly-connection-list-mode-map - -A keymap for functionality available in the @SLY{} buffers dedicated -to managing multiple Lisp connections (@pxref{Multiple connections}). - -@item sly-inspector-mode-map - -A keymap for functionality available in the @SLY{} buffers dedicated -to inspecting Lisp objects (@pxref{Inspector}). - -@item sly-mrepl-mode-map - -A keymap for functionality available in @SLY{}’s REPL buffers -(@pxref{REPL}). - -@item sly-trace-dialog-mode-map - -A keymap for functionality available in @SLY{}’s ``Trace Dialog'' -buffers (@pxref{Trace Dialog}). - -@end table - - -@node Defcustom variables -@subsection Defcustom variables - -The Emacs part of @SLY{} can be configured with the Emacs -@code{customize} system, just use @kbd{M-x customize-group sly -RET}. Because the customize system is self-describing, we only cover a -few important or obscure configuration options here in the manual. - -@table @code - -@item sly-truncate-lines -The value to use for @code{truncate-lines} in line-by-line summary -buffers popped up by @SLY{}. This is @code{t} by default, which -ensures that lines do not wrap in backtraces, apropos listings, and so -on. It can however cause information to spill off the screen. - -@anchor{sly-complete-symbol-function} -@item sly-complete-symbol-function -The function to use for completion of Lisp symbols. Two completion -styles are available: @code{sly-simple-completions} -and @code{sly-flex-completions} (@pxref{Completion}). - -@item sly-filename-translations -This variable controls filename translation between Emacs and the Lisp -system. It is useful if you run Emacs and Lisp on separate machines -which don't share a common file system or if they share the filesystem -but have different layouts, as is the case with @acronym{SMB}-based -file sharing. - -@anchor{sly-net-coding-system} -@cindex Unicode -@cindex UTF-8 -@cindex ASCII -@cindex LATIN-1 -@cindex Character Encoding -@item sly-net-coding-system -If you want to transmit Unicode characters between Emacs and the Lisp -system, you should customize this variable. E.g., if you use SBCL, you -can set: -@example -(setq sly-net-coding-system 'utf-8-unix) -@end example -To actually display Unicode characters you also need appropriate -fonts, otherwise the characters will be rendered as hollow boxes. If -you are using Allegro CL and GNU Emacs, you can also -use @code{emacs-mule-unix} as coding system. GNU Emacs has often -nicer fonts for the latter encoding. (Different encodings can be used -for different Lisps, see @ref{Multiple Lisps}.) - -@item sly-keep-buffers-on-connection-close -This variable holds a list of keywords indicating @SLY{} buffer types -that should be kept around when a connection closes. For example, if -the variable's value includes @code{:mrepl} (which is the default), -@REPL{} buffer is kept around while all other stale buffers (debugger, -inspector, etc..) are automatically killed. - -@end table - -@node Hooks -@subsection Hooks - -@table @code - -@item sly-mode-hook -This hook is run each time a buffer enters @code{sly-mode}. It is -most useful for setting buffer-local configuration in your Lisp source -buffers. An example use is to enable @code{sly-autodoc-mode} -(@pxref{Autodoc}). - -@anchor{sly-connected-hook} -@item sly-connected-hook -This hook is run when @SLY{} establishes a connection to a Lisp -server. An example use is to pop to a new @REPL{}. - -@item sly-db-hook -This hook is run after @SLY-DB{} is invoked. The hook functions are -called from the @SLY-DB{} buffer after it is initialized. An example use -is to add @code{sly-db-print-condition} to this hook, which makes all -conditions debugged with @SLY-DB{} be recorded in the @REPL{} buffer. - -@end table - -@node Lisp-side customization -@section Lisp-side (Slynk) - -The Lisp server side of @SLY{} (known as ``Slynk'') offers several -variables to configure. The initialization file @file{~/.slynk.lisp} -is automatically evaluated at startup and can be used to set these -variables. - -@menu -* Communication style:: -* Other configurables:: -@end menu - -@node Communication style -@subsection Communication style - -The most important configurable is @code{SLYNK:*COMMUNICATION-STYLE*}, -which specifies the mechanism by which Lisp reads and processes -protocol messages from Emacs. The choice of communication style has a -global influence on @SLY{}'s operation. - -The available communication styles are: - -@table @code -@item NIL -This style simply loops reading input from the communication socket -and serves @SLY{} protocol events as they arise. The simplicity -means that the Lisp cannot do any other processing while under -@SLY{}'s control. - -@item :FD-HANDLER -This style uses the classical Unix-style ``@code{select()}-loop.'' -Slynk registers the communication socket with an event-dispatching -framework (such as @code{SERVE-EVENT} in @acronym{CMUCL} and -@acronym{SBCL}) and receives a callback when data is available. In -this style requests from Emacs are only detected and processed when -Lisp enters the event-loop. This style is simple and predictable. - -@item :SIGIO -This style uses @dfn{signal-driven I/O} with a @code{SIGIO} signal -handler. Lisp receives requests from Emacs along with a signal, -causing it to interrupt whatever it is doing to serve the -request. This style has the advantage of responsiveness, since Emacs -can perform operations in Lisp even while it is busy doing other -things. It also allows Emacs to issue requests concurrently, e.g. to -send one long-running request (like compilation) and then interrupt -that with several short requests before it completes. The -disadvantages are that it may conflict with other uses of @code{SIGIO} -by Lisp code, and it may cause untold havoc by interrupting Lisp at an -awkward moment. - -@item :SPAWN -This style uses multiprocessing support in the Lisp system to execute -each request in a separate thread. This style has similar properties -to @code{:SIGIO}, but it does not use signals and all requests issued -by Emacs can be executed in parallel. - -@end table - -The default request handling style is chosen according to the -capabilities of your Lisp system. The general order of preference is -@code{:SPAWN}, then @code{:SIGIO}, then @code{:FD-HANDLER}, with -@code{NIL} as a last resort. You can check the default style by -calling @code{SLYNK-BACKEND::PREFERRED-COMMUNICATION-STYLE}. You can -also override the default by setting -@code{SLYNK:*COMMUNICATION-STYLE*} in your Slynk init file (@pxref{Lisp-side customization}). - -@node Other configurables -@subsection Other configurables - -These Lisp variables can be configured via your @file{~/.slynk.lisp} -file: - -@table @code - -@item SLYNK:*CONFIGURE-EMACS-INDENTATION* -This variable controls whether indentation styles for -@code{&body}-arguments in macros are discovered and sent to Emacs. It -is enabled by default. - -@item SLYNK:*GLOBAL-DEBUGGER* -When true (the default) this causes @code{*DEBUGGER-HOOK*} to be -globally set to @code{SLYNK:SLYNK-DEBUGGER-HOOK} and thus for @SLY{} -to handle all debugging in the Lisp image. This is for debugging -multithreaded and callback-driven applications. - -@anchor{*SLY-DB-QUIT-RESTART*} -@item SLYNK:*SLY-DB-QUIT-RESTART* -This variable names the restart that is invoked when pressing @kbd{q} -(@pxref{sly-db-quit}) in @SLY-DB{}. For @SLY{} evaluation requests this -is @emph{unconditionally} bound to a restart that returns to a safe -point. This variable is supposed to customize what @kbd{q} does if an -application's thread lands into the debugger (see -@code{SLYNK:*GLOBAL-DEBUGGER*}). -@example -(setf slynk:*sly-db-quit-restart* 'sb-thread:terminate-thread) -@end example - -@item SLYNK:*BACKTRACE-PRINTER-BINDINGS* -@itemx SLYNK:*MACROEXPAND-PRINTER-BINDINGS* -@itemx SLYNK:*SLY-DB-PRINTER-BINDINGS* -@itemx SLYNK:*SLYNK-PPRINT-BINDINGS* -These variables can be used to customize the printer in various -situations. The values of the variables are association lists of -printer variable names with the corresponding value. E.g., to enable -the pretty printer for formatting backtraces in @SLY-DB{}, you can use: - -@example -(push '(*print-pretty* . t) slynk:*sly-db-printer-bindings*). -@end example - -The fact that most @SLY{} output (in the @REPL{} for instance, -@pxref{REPL}) uses @code{SLYNK:*SLYNK-PPRINT-BINDINGS*} may surprise you -if you expected it to use a global setting for, say, -@code{*PRINT-LENGTH*}. The rationale for this decision is that output -is a very basic feature of @SLY{}, and it should keep operating normally -even if you (mistakenly) set absurd values for some @code{*PRINT-...*} -variable. You, of course, override this protection: - -@example -(setq slynk:*slynk-pprint-bindings* - (delete '*print-length* - slynk:*slynk-pprint-bindings* :key #'car)) -@end example - -@item SLYNK:*STRING-ELISION-LENGTH* -@itemx SLYNK:*STRING-ELISION-LENGTH* - -This variable controls the maximum length of strings before their pretty -printed representation in the Inspector, Debugger, @REPL, etc is elided. -Don't set this variable directly, create a binding for this variable -in @code{SLYNK:*SLYNK-PPRINT-BINDINGS*} instead. - -@item SLYNK:*ECHO-NUMBER-ALIST* -@itemx SLYNK:*PRESENT-NUMBER-ALIST* -These variables hold function designators used for displaying numbers -when SLY presents them in its interface. - -The difference between the two functions is that -@code{*PRESENT-NUMBER-ALIST*}, if non-nil, -overrides @code{*ECHO-NUMBER-ALIST*} in the context of the @REPL{}, Trace -Dialog and Stickers (see @ref{REPL}, @ref{Trace Dialog} and -@ref{Stickers}), while the latter is used for commands like @kbd{C-x -C-e} or the inspector (see @ref{Evaluation}, @ref{Inspector}). - -If in doubt, use @code{*ECHO-NUMBER-ALIST*}. - -Both variables have the same structure: each element in the alist takes -the form @code{(TYPE . FUNCTIONS)}, where @code{TYPE} is a type designator -and @code{FUNCTIONS} is a list of function designators for displaying -that number in SLY. Each function takes the number as a single argument -and returns a string, or nil, if that particular representation is to be -disregarded. - -Additionally if a given function chooses to return @code{t} as its -optional second value, then all the remaining functions following it -in the list are disregarded. - -For integer numbers, the default value of this variable holds function -designators that echo an integer number in its binary, hexadecimal and -octal representation. However, if your application is using integers -to represent -@uref{https://en.wikipedia.org/wiki/Unix_time,,Unix Epoch Times} you -can use this function to display a human-readable time -whenever you evaluate an integer. - -@example -(defparameter *day-names* '("Monday" "Tuesday" "Wednesday" - "Thursday" "Friday" "Saturday" - "Sunday")) - -(defun fancy-unix-epoch-time (integer) - "Format INTEGER as a Unix Epoch Time if within 10 years from now." - (let ((now (get-universal-time)) - (tenyears (encode-universal-time 0 0 0 1 1 1910 0)) - (unix-to-universal - (+ integer - (encode-universal-time 0 0 0 1 1 1970 0)))) - (when (< (- now tenyears) unix-to-universal (+ now tenyears)) - (multiple-value-bind - (second minute hour date month year day-of-week dst-p tz) - (decode-universal-time unix-to-universal) - (declare (ignore dst-p)) - (format nil "~2,'0d:~2,'0d:~2,'0d on ~a, ~d/~2,'0d/~d (GMT~@@d)" - hour minute second (nth day-of-week *day-names*) - month date year (- tz)))))) - -(pushnew 'fancy-unix-epoch-time - (cdr (assoc 'integer slynk:*echo-number-alist*))) - -42 ; => 42 (6 bits, #x2A, #o52, #b101010) -1451404675 ; => 1451404675 (15:57:55 on Tuesday, 12/29/2015 (GMT+0), 31 bits, #x5682AD83) -@end example - -@item SLYNK-APROPOS:*PREFERRED-APROPOS-MATCHER* -This variable holds a function used for performing apropos searches. It -defaults to @code{SLYNK-APROPOS:MAKE-FLEX-MATCHER}, but can also be set -to @code{SLYNK-APROPOS:MAKE-CL-PPCRE-MATCHER} (to use a regex-able -matcher) or @code{SLYNK-APROPOS:MAKE-PLAIN-MATCHER}, for example. - -@item SLYNK:*LOG-EVENTS* -Setting this variable to @code{t} causes all protocol messages -exchanged with Emacs to be printed to @code{*TERMINAL-IO*}. This is -useful for low-level debugging and for observing how @SLY{} works -``on the wire.'' The output of @code{*TERMINAL-IO*} can be found in -your Lisp system's own listener, usually in the buffer -@code{*inferior-lisp*}. - -@end table - -@node Tips and Tricks -@chapter Tips and Tricks - -@menu -* Connecting to a remote Lisp:: -* Loading Slynk faster:: -* Auto-SLY:: -* REPLs and game loops:: -* Controlling SLY from outside Emacs:: -@end menu - -@node Connecting to a remote Lisp -@section Connecting to a remote Lisp - -One of the advantages of the way @SLY{} is implemented is that we can -easily run the Emacs side (@code{sly.el} and friends) on one machine -and the Lisp backend (Slynk) on another. The basic idea is to start up -Lisp on the remote machine, load Slynk and wait for incoming @SLY{} -connections. On the local machine we start up Emacs and tell @SLY{} to -connect to the remote machine. The details are a bit messier but the -underlying idea is that simple. - -@menu -* Setting up the Lisp image:: -* Setting up Emacs:: -* Setting up pathname translations:: -@end menu - -@node Setting up the Lisp image -@subsection Setting up the Lisp image - -The easiest way to load Slynk ``standalone'' (i.e. without -having @code{M-x sly} start a Lisp that is subsidiary to a particular -Emacs), is to load the ASDF system definition for Slynk. - -Make sure the path to the directory containing Slynk's @code{.asd} -file is in @code{ASDF:*CENTRAL-REGISTRY*}. This file lives in -the @code{slynk} subdirectory of @SLY{}. Type: - -@example -(push #p"/path/to/sly/slynk/" ASDF:*CENTRAL-REGISTRY*) -(asdf:require-system :slynk) -@end example - -inside a running Lisp image@footnote{@SLY{} also SLIME's old-style -@code{slynk-loader.lisp} loader which does the same thing, but ASDF is preferred}. - -Now all we need to do is startup our Slynk server. A working example -uses the default settings: - -@example -(slynk:create-server) -@end example - -This creates a ``one-connection-only'' server on port 4005 using the -preferred communication style for your Lisp system. The following -parameters to @code{slynk:create-server} can be used to change that -behaviour: - -@table @code -@item :PORT -Port number for the server to listen on (default: 4005). -@item :DONT-CLOSE -Boolean indicating if the server will continue to accept connections -after the first one (default: @code{NIL}). For ``long-running'' Lisp processes -to which you want to be able to connect from time to time, -specify @code{:dont-close t} -@item :STYLE -See @xref{Communication style}. -@end table - -So a more complete example will be -@example -(slynk:create-server :port 4006 :dont-close t) -@end example - -Finally, since section we're going to be tunneling our connection via -@acronym{SSH}@footnote{there is a way to connect without an SSH -tunnel, but it has the side-effect of giving the entire world access -to your Lisp image, so we're not going to talk about it} we'll only -have one port open we must tell Slynk's REPL contrib (see @REPL{}) to -not use an extra connection for output, which it will do by default. - -@example -(setf slynk:*use-dedicated-output-stream* nil) -@end example - -@footnote{Alternatively, a separate tunnel for the port set in -@code{slynk:*dedicated-output-stream-port*} can also be used if a dedicated output -is essential.} - -@node Setting up Emacs -@subsection Setting up Emacs - -Now we need to create the tunnel between the local machine and the -remote machine. Assuming a @acronym{UNIX} command-line, this can be -done with: - -@example -ssh -L4005:localhost:4005 youruser@@remote.example.com -@end example - -This incantation creates a SSH tunnel between the port 4005 on our -local machine and the port 4005 on the remote machine, -where @code{youruser} is expected to have an account. @footnote{By -default Slynk listens for incoming connections on port 4005, had we -passed a @code{:port} parameter to @code{slynk:create-server} we'd be -using that port number instead}. - -Finally we start @SLY{} with @code{sly-connect} instead of the usual -@code{sly}: - -@example -M-x sly-connect RET RET -@end example - -The @kbd{RET RET} sequence just means that we want to use the default -host (@code{localhost}) and the default port (@code{4005}). Even -though we're connecting to a remote machine the SSH tunnel fools Emacs -into thinking it's actually @code{localhost}. - -@node Setting up pathname translations -@subsection Setting up pathname translations - -One of the main problems with running slynk remotely is that Emacs -assumes the files can be found using normal filenames. if we want -things like @code{sly-compile-and-load-file} (@kbd{C-c C-k}) and -@code{sly-edit-definition} (@kbd{M-.}) to work correctly we need to -find a way to let our local Emacs refer to remote files. - -There are, mainly, two ways to do this. The first is to mount, using -NFS or similar, the remote machine's hard disk on the local machine's -file system in such a fashion that a filename like -@file{/opt/project/source.lisp} refers to the same file on both -machines. Unfortunately NFS is usually slow, often buggy, and not -always feasible. Fortunately we have an ssh connection and Emacs' -@code{tramp-mode} can do the rest. -(See @inforef{Top, TRAMP User Manual,tramp}.) - -What we do is teach Emacs how to take a filename on the remote machine -and translate it into something that tramp can understand and access -(and vice versa). Assuming the remote machine's host name is -@code{remote.example.com}, @code{cl:machine-instance} returns -``remote'' and we login as the user ``user'' we can use @code{sly-tramp} -contrib to setup the proper translations by simply doing: - -@example -(add-to-list 'sly-filename-translations - (sly-create-filename-translator - :machine-instance "remote" - :remote-host "remote.example.com" - :username "user")) -@end example - -@node Loading Slynk faster -@section Loading Slynk faster - -In this section, a technique to load Slynk faster on South Bank Common -Lisp (SBCL) is presented. Similar setups should also work for other -Lisp implementations. - -A pre-canned solution that automates this technique was developed by -@uref{https://gitlab.com/ambrevar/lisp-repl-core-dumper,Pierre -Neidhardt}. - -For SBCL, we recommend that you create a custom core file with socket -support and @acronym{POSIX} bindings included because those modules -take the most time to load. To create such a core, execute the -following steps: - -@example -shell$ sbcl -* (mapc 'require '(sb-bsd-sockets sb-posix sb-introspect sb-cltl2 asdf)) -* (save-lisp-and-die "sbcl.core-for-sly") -@end example - -After that, add something like this to your @file{~/.emacs} or -@file{~/.emacs.d/init.el} (@pxref{Emacs Init File}): - -@lisp -(setq sly-lisp-implementations '((sbcl ("sbcl" "--core" - "sbcl.core-for-sly")))) -@end lisp - -For maximum startup speed you can include the Slynk server directly in -a core file. The disadvantage of this approach is that the setup is a -bit more involved and that you need to create a new core file when you -want to update @SLY{} or @acronym{SBCL}. The steps to execute are: - -@example -shell$ sbcl -* (load ".../sly/slynk-loader.lisp") -* (slynk-loader:dump-image "sbcl.core-with-slynk") -@end example - -@noindent -Then add this to the Emacs initializion file: - -@anchor{init-example} -@lisp -(setq sly-lisp-implementations - '((sbcl ("sbcl" "--core" "sbcl.core-with-slynk") - :init (lambda (port-file _) - (format "(slynk:start-server %S)\n" port-file))))) -@end lisp - -@node Auto-SLY -@section Connecting to SLY automatically - -To make @SLY{} connect to your lisp whenever you open a lisp file -just add this to your @file{~/.emacs} or -@file{~/.emacs.d/init.el} (@pxref{Emacs Init File}): - -@example -(add-hook 'sly-mode-hook - (lambda () - (unless (sly-connected-p) - (save-excursion (sly))))) -@end example - -@node REPLs and game loops -@section REPLs and ``Game Loops'' - -When developing Common Lisp video games or graphical applications, a -REPL (@pxref{REPL}) is just as useful as anywhere else. But it is often -the case that one needs to control exactly the timing of REPL requests -and ensure they do not interfere with the ``game loop''. In other -situations, the choice of communication style (@pxref{Communication -style}) to the Slynk server may invalidate simultaneous multi-threaded -operation of REPL and game loop. - -Instead of giving up on the REPL or using a complicated solution, SLY's -REPL can be built into your game loop by using a couple of Slynk Common -Lisp functions, @code{SLYNK-MREPL:SEND-PROMPT} and -@code{SLYNK:PROCESS-REQUESTS}. - -@example -(defun my-repl-aware-game-loop () - (loop initially - (princ "Starting our game") - (slynk-mrepl:send-prompt) - for i from 0 - do (with-simple-restart (abort "Skip rest of this game loop iteration") - (when (zerop (mod i 10)) - (fresh-line) - (princ "doing high-priority 3D game loop stuff")) - (sleep 0.1) - ;; When you're ready to serve a potential waiting - ;; REPL request, just do this non-blocking thing: - (with-simple-restart (abort "Abort this game REPL evaluation") - (slynk:process-requests t))))) -@end example - -Note that this function is to be called @emph{from the REPL}, and will -enter kind of ``sub-REPL'' inside it. It'll likely ``just work'' in -this situation. However, if you need you need to call this from -anywhere else (like, say, another thread), you must additionally arrange -for the variable @code{SLYNK-API:*CHANNEL*} to be bound to the value it -is bound to in whatever SLY REPL you wish to interact with your game. - -@node Controlling SLY from outside Emacs -@section Controlling SLY from outside Emacs - -If your application has a non-SLY, non-Emacs user interface (graphical -or otherwise), you can use it to exert some control over SLY -functionality, such as its REPL (@pxref{REPL}) and inspector -(@pxref{Inspector}). This requires that you first set, in Emacs, -variable @code{sly-enable-evaluate-in-emacs} to non-nil. As the name -suggests, it lets outside Slynk servers evaluate code in your Elisp -runtime. It is set to @code{nil} by default for security purposes. - -Once you've done that, you can -call @code{SLYNK-MREPL:COPY-TO-REPL-IN-EMACS} from your CL code with -some objects you'd like to manipulate in the REPL. Then you can have -this code run from some UI event handler: - -@example -(lambda () - (slynk-mrepl:copy-to-repl-in-emacs - (list 42 'foo) - :blurb "Just a forty-two and a foo")) -@end example - -And see those objects pop up in your REPL for inspection and -manipulation. - -You can also use the functions @code{SLYNK:INSPECT-IN-EMACS}, -@code{SLYNK:ED-IN-EMACS}, and in general, any exported function ending -in @code{IN-EMACS}. See their docstrings for details. - -@node Extensions -@chapter Extensions - -@menu -* Loading and unloading:: More contribs:: -* More contribs:: -@end menu - -@cindex Contribs -@cindex Contributions -@cindex Plugins -@cindex Extensions - -Extensions, also known as ``contribs'' are Emacs packages that extend -@SLY{}’s functionality. Contrasting with its ancestor @SLIME{} -(@pxref{Introduction}), most contribs bundled with @SLY{} are active -by default, since they are a decent way to split @SLY{} into pluggable -modules. The auto-documentation (@pxref{Autodoc}), trace (@pxref{Trace -Dialog}) and Stickers (@pxref{Stickers}) are contribs enabled by -default, for example. - -Usually, contribs differ from regular Emacs plugins in that they are -partly written in Emacs-lisp and partly in Common Lisp. The former is -usually the UI that queries the latter for information and then -presents it to the user. @SLIME{} used to load all the contribs’ -Common Lisp code upfront, but @SLY{} takes care to loading these two -parts at the correct time. In this way, developers can write -third-party contribs that live independently of @SLY{} perhaps even in -different code repositories. The @code{sly-macrostep} contrib -(@uref{https://github.com/joaotavora/sly-macrostep}) is one such -example. - -A special @code{sly-fancy} contrib package is the only one loaded by -default. You might never want to fiddle with it (it is the one that -contains the default extensions), but if you find that you don't like -some package or you are having trouble with a package, you can modify -your setup a bit. Generally, you set the variable @code{sly-contribs} -with the list of package-names that you want to use. For example, a -setup to load only the @code{sly-scratch} and @code{sly-mrepl} -packages looks like: - -@example -;; @emph{Setup load-path and autoloads} -(add-to-list 'load-path "~/dir/to/cloned/sly") -(require 'sly-autoloads) - -;; @emph{Set your lisp system and some contribs} -(setq inferior-lisp-program "/opt/sbcl/bin/sbcl") -(setq sly-contribs '(sly-scratch sly-mrepl)) -@end example - -After starting @SLY{}, the commands of both packages should be -available. - -@node Loading and unloading -@section Loading and unloading ``on the fly'' - -We recommend that you setup the @code{sly-contribs} -variable @emph{before} starting @SLY{} via @kbd{M-x sly}, but if you -want to enable more contribs @emph{after} you that, you can set -new @code{sly-contribs} variable to another value and call @code{M-x -sly-setup} or @code{M-x sly-enable-contrib}. Note this though: - -@itemize @bullet -@item -If you've removed contribs from the list they won't be unloaded -automatically. -@item -If you have more than one @SLY{} connection currently active, you must -manually repeat the @code{sly-setup} step for each of them. -@end itemize - -Short of restarting Emacs, a reasonable way of unloading contribs is -by calling an Emacs Lisp function whose name is obtained by -adding @code{-unload} to the contrib's name, for every contrib you -wish to unload. So, to remove @code{sly-mrepl}, you must call -@code{sly-mrepl-unload}. Because the unload function will only, if -ever, unload the Emacs Lisp side of the contrib, you may also need to -restart your lisps. - -@node More contribs -@section More contribs - -@menu -* TRAMP Support:: -* Scratch Buffer:: -@end menu - -@node TRAMP Support -@subsection TRAMP - -@cindex TRAMP - -The package @code{sly-tramp} provides some functions to set up -filename translations for TRAMP. (@pxref{Setting up pathname -translations}) - -@node Scratch Buffer -@subsection Scratch Buffer - -@anchor{sly-scratch} -The @SLY{} scratch buffer, in contrib package @code{sly-scratch}, -imitates Emacs' usual @code{*scratch*} buffer. -If @code{sly-scratch-file} is set, it is used to back the scratch -buffer, making it persistent. The buffer is like any other Lisp -buffer, except for the command bound to @kbd{C-j}. - -@table @kbd - -@kbditem{C-j, sly-eval-print-last-expression} -Evaluate the expression sexp before point and insert a printed -representation of the return values into the current buffer. - -@cmditem{sly-scratch} -Create a @file{*sly-scratch*} buffer. In this -buffer you can enter Lisp expressions and evaluate them with -@kbd{C-j}, like in Emacs's @file{*scratch*} buffer. - -@end table - - -@node Credits -@chapter Credits - -@emph{The soppy ending...} - -@unnumberedsec Hackers of the good hack - -@SLY{} is a fork of SLIME which is itself an Extension -of @acronym{SLIM} by Eric Marsden. At the time of writing, the authors -and code-contributors of @SLY{} are: - -@include contributors.texi - -... not counting the bundled code from @file{hyperspec.el}, -@cite{CLOCC}, and the @cite{CMU AI Repository}. - -Many people on the @code{sly-devel} mailing list have made non-code -contributions to @SLY{}. Life is hard though: you gotta send code to -get your name in the manual. @code{:-)} - -@unnumberedsec Thanks! - -We're indebted to the good people of @code{common-lisp.net} for their -hosting and help, and for rescuing us from ``Sourceforge hell.'' - -Implementors of the Lisps that we support have been a great help. We'd -like to thank the @acronym{CMUCL} maintainers for their helpful -answers, Craig Norvell and Kevin Layer at Franz providing Allegro CL -licenses for @SLY{} development, and Peter Graves for his help to -get @SLY{} running with @acronym{ABCL}. - -Most of all we're happy to be working with the Lisp implementors -who've joined in the @SLY{} development: Dan Barlow and Christophe -Rhodes of @acronym{SBCL}, Gary Byers of OpenMCL, and Martin Simmons of -LispWorks. Thanks also to Alain Picard and Memetrics for funding -Martin's initial work on the LispWorks backend! - -@node Key Index -@unnumbered Key (Character) Index -@printindex ky - -@node Command Index -@unnumbered Command and Function Index -@printindex fn - -@node Variable Index -@unnumbered Variable and Concept Index -@printindex vr - -@bye -Local Variables: -paragraph-start: "@[a-zA-Z]+\\({[^}]+}\\)?[ \n]\\|[ ]*$" -paragraph-separate: "@[a-zA-Z]+\\({[^}]+}\\)?[ \n]\\|[ ]*$" -End: - -@c LocalWords: RET backreferences defun Autodoc autodoc minibuffer -@c LocalWords: backreference diff --git a/elpa/sly-1.0.43/doc/texinfo-tabulate.awk b/elpa/sly-1.0.43/doc/texinfo-tabulate.awk @@ -1,21 +0,0 @@ -#!/usr/bin/env awk -f -# -# Format input lines into a multi-column texinfo table. -# Note: does not do texinfo-escaping of the input. - -# This code has been placed in the Public Domain. All warranties -# are disclaimed. - -BEGIN { - columns = 3; - printf("@multitable @columnfractions"); - for (i = 0; i < columns; i++) - printf(" %f", 1.0/columns); - print -} - -{ if (NR % columns == 1) printf("\n@item %s", $0); - else printf(" @tab %s", $0); } - -END { printf("\n@end multitable\n"); } - diff --git a/elpa/sly-1.0.43/lib/sly-buttons.el b/elpa/sly-1.0.43/lib/sly-buttons.el @@ -1,324 +0,0 @@ -;;; sly-buttons.el --- Button-related utils for SLY -*- lexical-binding: t; -*- -;;; -(require 'cl-lib) -(require 'sly-messages "lib/sly-messages") - -(defvar sly-part-button-keymap - (let ((map (make-sparse-keymap))) - (set-keymap-parent map button-map) - (define-key map [down-mouse-3] 'sly-button-popup-part-menu) - (define-key map [mouse-3] 'sly-button-popup-part-menu) - (define-key map [mouse-1] 'push-button) - (define-key map [return] 'push-button) - map)) - -(defvar sly-button-popup-part-menu-keymap - (let ((map (make-sparse-keymap))) - map)) - -(defun sly-button-popup-part-menu (event) - "Popup a menu for a `sly-part' button" - (interactive "@e") - (let* ((button (button-at (posn-point (event-end event)))) - (label (button-get button 'part-label)) - (items (cdr (button-get button 'part-menu-keymap)))) - (popup-menu - `(keymap - ,@(when label - `(,(truncate-string-to-width label 30 nil nil t))) - ,@items)))) - -(defun sly-button-at (&optional pos type no-error) - (let ((button (button-at (or pos - (if (mouse-event-p last-input-event) - (posn-point (event-end last-input-event)) - (point)))))) - (cond ((and button type - (button-type-subtype-p (button-type button) type)) - button) - ((and button type) - (unless no-error - (error "[sly] Button at point is not of expected type %s" type))) - (button - button) - (t - (unless no-error - (error "[sly] No button at point")))))) - -(defun sly-button-buttons-in (beg end) - (save-excursion - (goto-char (point-min)) - (cl-loop for count-current = t then nil - for button = (next-button (point) count-current) - while button - do (goto-char (button-start button)) - collect button))) - -(defmacro sly-button-define-part-action (action label key) - `(progn - (defun ,action (button) - ,(format "%s the object under BUTTON." - label) - (interactive (list (sly-button-at))) - (let ((fn (button-get button ',action)) - (args (button-get button 'part-args))) - (if (and - (sly-current-connection) - (eq (button-get button 'sly-connection) - (sly-current-connection))) - (cond ((and fn args) - (apply fn args)) - (args - (sly-error "button of type `%s' doesn't implement `%s'" - (button-type button) ',action)) - (fn - (sly-error "button %s doesn't have the `part-args' property" - button))) - (sly-error (format "button is from an older connection"))))) - ,@(when key - `((define-key sly-part-button-keymap ,key - '(menu-item "" ,action - :filter (lambda (cmd) - (let ((button (sly-button-at))) - (and button - (button-get button ',action) - cmd))))))) - (define-key sly-button-popup-part-menu-keymap - [,action] '(menu-item ,label ,action - :visible (let ((button (sly-button-at))) - (and button - (button-get button ',action))))))) - -(sly-button-define-part-action sly-button-inspect "Inspect" (kbd "i")) -(sly-button-define-part-action sly-button-describe "Describe" (kbd "d")) -(sly-button-define-part-action sly-button-pretty-print "Pretty Print" (kbd "p")) -(sly-button-define-part-action sly-button-show-source "Show Source" (kbd "v")) -(sly-button-define-part-action sly-button-goto-source "Go To Source" (kbd ".")) - -(defun sly--make-text-button (beg end &rest properties) - "Just like `make-text-button', but add sly-specifics." - (apply #'make-text-button beg end - 'sly-connection (sly-current-connection) - properties)) - -(defun sly-make-action-button (label action &rest props) - (apply #'sly--make-text-button - label nil :type 'sly-action - 'action action - 'mouse-action action - props)) - -(defface sly-action-face - `((t (:inherit warning))) - "Face for SLY buttons." - :group 'sly) - -(define-button-type 'sly-button - 'sly-button-search-id 'regular-button) - -(define-button-type 'sly-action :supertype 'sly-button - 'face 'sly-action-face - 'mouse-face 'highlight - 'sly-button-echo 'sly-button-echo-button) - -(defface sly-part-button-face - '((t (:inherit font-lock-constant-face))) - "Face for things which be interactively inspected, etc" - :group 'sly) - -(define-button-type 'sly-part :supertype 'sly-button - 'face 'sly-part-button-face - 'action 'sly-button-inspect - 'mouse-action 'sly-button-inspect - 'keymap sly-part-button-keymap - 'sly-button-echo 'sly-button-echo-part - 'part-menu-keymap sly-button-popup-part-menu-keymap - 'help-echo "RET, mouse-2: Inspect object; mouse-3: Context menu" - ;; these are ajust here for clarity - ;; - 'sly-button-inspect nil - 'sly-button-describe nil - 'sly-button-pretty-print nil - 'sly-button-show-source nil) - -(cl-defun sly-button-flash (button &key - (face 'highlight) - (pattern '(0.07 0.07 0.07 0.07)) - times - timeout) - (sly-flash-region (button-start button) (button-end button) - :timeout timeout - :pattern pattern - :times times - :face face)) - - -(defun sly-button-echo-button (button) (sly-message "A sly button")) - -(defun sly-button-echo-part (button) - (sly-button-flash button) - (sly-message (button-get button 'part-label))) - - -;;; Overlay-button specifics -;;; -(defun sly-button--overlays-in (beg end &optional filter) - "Return overlays overlapping positions BEG and END" - (cl-remove-if-not #'(lambda (button) - (and - ;; Workaround fragility in Emacs' buttons: - ;; `button-type-subtype-p' errors when - ;; `button' is not actually a button. A - ;; straightforward predicate for this doesn't - ;; seem to exist yet. - (ignore-errors - (button-type-subtype-p (button-type button) 'sly-button)) - (or (not filter) - (funcall filter button)))) - (overlays-in beg end))) - -(defun sly-button--overlays-between (beg end &optional filter) - "Return overlays contained entirely between BEG and END" - (cl-remove-if-not #'(lambda (button) - (and (>= (button-start button) beg) - (<= (button-end button) end))) - (sly-button--overlays-in beg end filter))) - -(defun sly-button--overlays-exactly-at (beg end &optional filter) - "Return overlays exactly between BEG and END" - (cl-remove-if-not #'(lambda (button) - (and (= (button-start button) beg) - (= (button-end button) end))) - (sly-button--overlays-in beg end filter))) - -(defun sly-button--overlays-at (&optional point filter) - "Return overlays near POINT" - (let ((point (or point (point)))) - (cl-sort (sly-button--overlays-in (1- point) (1+ point) filter) - #'> :key #'sly-button--level))) - -(gv-define-setter sly-button--level (level button) - `(overlay-put ,button 'sly-button-level ,level)) - -(defun sly-button--level (button) - (or (overlay-get button 'sly-button-level) 0)) - - - -;;; Button navigation -;;; -(defvar sly-button--next-search-id 0) - -(defun sly-button-next-search-id () - (cl-incf sly-button--next-search-id)) - -(defun sly-button--searchable-buttons-at (pos filter) - (let* ((probe (sly-button-at pos 'sly-button 'no-error)) - (non-overlay-button (and probe - (not (overlayp probe)) - probe))) - (cl-remove-duplicates - (append (sly-button--overlays-at pos filter) - (if (and non-overlay-button - (or (not filter) - (funcall filter non-overlay-button))) - (list non-overlay-button)))))) - -(defun sly-button--searchable-buttons-starting-at (&optional point filter) - (let ((point (or point (point)))) - (cl-remove-if-not #'(lambda (button) - (= (button-start button) point)) - (sly-button--searchable-buttons-at point filter)))) - -(defun sly-button--search-1 (n filter) - (cl-loop with off-by-one = (if (cl-plusp n) -1 +1) - for search-start = (point) then pos - for preval = (and (not (cond ((cl-plusp n) - (= search-start (point-min))) - (t - (= search-start (point-max))))) - (get-char-property (+ off-by-one - search-start) - 'sly-button-search-id)) - for pos = (funcall - (if (cl-plusp n) - #'next-single-char-property-change - #'previous-single-char-property-change) - search-start - 'sly-button-search-id) - for newval = (get-char-property pos 'sly-button-search-id) - until (cond ((cl-plusp n) - (= pos (point-max))) - (t - (= pos (point-min)))) - for buttons = (sly-button--searchable-buttons-at - pos (or filter #'identity)) - when (and buttons - newval - (not (eq newval preval)) - (eq pos (button-start (car buttons)))) - return buttons)) - - -(put 'sly-button-forward 'sly-button-navigation-command t) -(put 'sly-button-backward 'sly-button-navigation-command t) - -(defun sly-button-search (n &optional filter) - "Go forward to Nth buttons verifying FILTER and echo it. - -With negative N, go backward. Visiting is done via the -`sly-button-echo' property. - -If more than one button overlap the same region, the button -starting before is visited first. If more than one button start -at exactly the same spot, they are both visited simultaneously, -`sly-button-echo' being passed a variable number of button arguments." - (cl-loop for i from 0 below (abs n) - for buttons = - (or (and (not (and - ;; (symbolp last-command) - (get last-command 'sly-button-navigation-command))) - (sly-button--searchable-buttons-starting-at (point) filter)) - (sly-button--search-1 n filter)) - for button = (car buttons) - while buttons - finally - (cond (buttons - (goto-char (button-start (car buttons))) - (apply (button-get button 'sly-button-echo) - button - (cl-remove-if-not - #'(lambda (b) - (= (button-start b) (button-start button))) - (cdr buttons)))) - (t - (sly-user-error "No more buttons!"))))) - -(defvar sly-button-filter-function #'identity - "Filter buttons considered by `sly-button-forward' -Set to `sly-note-button-p' to only navigate compilation notes, -or leave at `identity' to visit every `sly-button' in the buffer.'") - -(defun sly-button-forward (n) - "Go to and describe the next button in the buffer." - (interactive "p") - (sly-button-search n sly-button-filter-function)) - -(defun sly-button-backward (n) - "Go to and describe the previous button in the buffer." - (interactive "p") - (sly-button-forward (- n))) - -(define-minor-mode sly-interactive-buttons-mode - "Minor mode where text property SLY buttons exist" - nil nil nil - ;; Prevent strings copied from SLY buffers and yanked to source - ;; buffers to land with misleading `sly-' properties. - (when (fboundp 'add-function) - (add-function :filter-return (local 'filter-buffer-substring-function) - #'substring-no-properties - '((name . sly-remove-string-properties))))) - -(provide 'sly-buttons) - -;;; sly-buttons.el ends here diff --git a/elpa/sly-1.0.43/lib/sly-cl-indent.el b/elpa/sly-1.0.43/lib/sly-cl-indent.el @@ -1,1782 +0,0 @@ -;;; sly-cl-indent.el --- enhanced lisp-indent mode -*- lexical-binding: t; -*- - -;; Copyright (C) 1987, 2000-2011 Free Software Foundation, Inc. - -;; Author: Richard Mlynarik <mly@eddie.mit.edu> -;; Created: July 1987 -;; Maintainer: FSF -;; Keywords: lisp, tools -;; Package: emacs - -;; This file is forked from cl-indent.el, which is part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; This package supplies a single entry point, `sly-common-lisp-indent-function', -;; which performs indentation in the preferred style for Common Lisp code. -;; To enable it: -;; -;; (setq lisp-indent-function 'sly-common-lisp-indent-function) -;; -;; This file is substantially patched from original cl-indent.el, -;; which is in Emacs proper. Although it is named after the SLY -;; library, it DOES NOT require it. sly-cl-indent is instead required -;; by one of SLY's contribs, `sly-indentation'. -;; -;; Before making modifications to this file, consider adding them to -;; Emacs's own `cl-indent' and refactoring this file to be an -;; extension of Emacs's. -;; -;;; Code: -(require 'cl-lib) - -(defgroup sly-lisp-indent nil - "Indentation in Common Lisp." - :group 'sly - :group 'lisp-indent) - -(defcustom sly-lisp-indent-maximum-backtracking 6 - "Maximum depth to backtrack out from a sublist for structured indentation. -If this variable is 0, no backtracking will occur and forms such as `flet' -may not be correctly indented if this value is less than 4." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-tag-indentation 1 - "Indentation of tags relative to containing list. -This variable is used by the function `sly--lisp-indent-tagbody'." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-tag-body-indentation 3 - "Indentation of non-tagged lines relative to containing list. -This variable is used by the function `sly--lisp-indent-tagbody' to indent normal -lines (lines without tags). -The indentation is relative to the indentation of the parenthesis enclosing -the special form. If the value is t, the body of tags will be indented -as a block at the same indentation as the first s-expression following -the tag. In this case, any forms before the first tag are indented -by `lisp-body-indent'." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-backquote-indentation t - "Whether or not to indent backquoted lists as code. -If nil, indent backquoted lists as data, i.e., like quoted lists." - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-loop-indent-subclauses t - "Whether or not to indent loop subclauses." - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-simple-loop-indentation 2 - "Indentation of forms in simple loop forms." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-loop-clauses-indentation 2 - "Indentation of loop clauses if `loop' is immediately followed by a newline." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-loop-indent-body-forms-relative-to-loop-start nil - "When true, indent loop body clauses relative to the open paren of the loop -form, instead of the keyword position." - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-loop-body-forms-indentation 3 - "Indentation of loop body clauses." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-loop-indent-forms-like-keywords nil - "Whether or not to indent loop subforms just like -loop keywords. Only matters when `sly-lisp-loop-indent-subclauses' -is nil." - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-align-keywords-in-calls t - "Whether to align keyword arguments vertically or not. -If t (the default), keywords in contexts where no other -indentation rule takes precedence are aligned like this: - -\(make-instance 'foo :bar t - :quux 42) - -If nil, they are indented like any other function -call arguments: - -\(make-instance 'foo :bar t - :quux 42)" - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-lambda-list-indentation t - "Whether to indent lambda-lists specially. Defaults to t. Setting this to -nil makes `sly-lisp-lambda-list-keyword-alignment', -`sly-lisp-lambda-list-keyword-parameter-alignment', and -`sly-lisp-lambda-list-keyword-parameter-indentation' meaningless, causing -lambda-lists to be indented as if they were data: - -\(defun example (a b &optional o1 o2 - o3 o4 - &rest r - &key k1 k2 - k3 k4) - #|...|#)" - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-lambda-list-keyword-alignment nil - "Whether to vertically align lambda-list keywords together. -If nil (the default), keyworded lambda-list parts are aligned -with the initial mandatory arguments, like this: - -\(defun foo (arg1 arg2 &rest rest - &key key1 key2) - #|...|#) - -If non-nil, alignment is done with the first keyword -\(or falls back to the previous case), as in: - -\(defun foo (arg1 arg2 &rest rest - &key key1 key2) - #|...|#)" - :type 'boolean - :group 'sly-lisp-indent) - -(defcustom sly-lisp-lambda-list-keyword-parameter-indentation 2 - "Indentation of lambda list keyword parameters. -See `sly-lisp-lambda-list-keyword-parameter-alignment' -for more information." - :type 'integer - :group 'sly-lisp-indent) - -(defcustom sly-lisp-lambda-list-keyword-parameter-alignment nil - "Whether to vertically align lambda-list keyword parameters together. -If nil (the default), the parameters are aligned -with their corresponding keyword, plus the value of -`sly-lisp-lambda-list-keyword-parameter-indentation', like this: - -\(defun foo (arg1 arg2 &key key1 key2 - key3 key4) - #|...|#) - -If non-nil, alignment is done with the first parameter -\(or falls back to the previous case), as in: - -\(defun foo (arg1 arg2 &key key1 key2 - key3 key4) - #|...|#)" - :type 'boolean - :group 'sly-lisp-indent) - - -;; should this be a defcustom? -(defvar sly-lisp-indent-defun-method '(4 &lambda &body) - "Defun-like indentation method. -This applies when the value of the `sly-common-lisp-indent-function' property -is set to `defun'.") - - -;;;; Named styles. -;;;; -;;;; -*- common-lisp-style: foo -*- -;;;; -;;;; sets the style for the buffer. -;;;; -;;;; A Common Lisp style is a list of the form: -;;;; -;;;; (NAME INHERIT VARIABLES INDENTATION HOOK DOCSTRING) -;;;; -;;;; where NAME is a symbol naming the style, INHERIT is the name of the style -;;;; it inherits from, VARIABLES is an alist specifying buffer local variables -;;;; for the style, and INDENTATION is an alist specifying non-standard -;;;; indentations for Common Lisp symbols. HOOK is a function to call when -;;;; activating the style. DOCSTRING is the documentation for the style. -;;;; -;;;; Convenience accessors `sly--common-lisp-style-name', &co exist. -;;;; -;;;; `sly-common-lisp-style' stores the name of the current style. -;;;; -;;;; `sly-common-lisp-style-default' stores the name of the style to use when none -;;;; has been specified. -;;;; -;;;; `sly--lisp-indent-active-style' stores a cons of the list specifying the -;;;; current style, and a hash-table containing all indentation methods of that -;;;; style and any styles it inherits from. Whenever we're indenting, we check -;;;; that this is up to date, and recompute when necessary. -;;;; -;;;; Just setting the buffer local sly-common-lisp-style will be enough to have -;;;; the style take effect. `sly-common-lisp-set-style' can also be called -;;;; explicitly, however, and offers name completion, etc. - -(cl-defstruct (sly--common-lisp-style - (:type list) - (:copier nil) - (:predicate nil) - (:constructor nil) - (:constructor sly--common-lisp-make-style - (name inherits variables - indentation hook docstring))) - name inherits variables indentation hook docstring) - -;;; Convenience accessors -(defalias 'sly--lisp-indent-parse-state-start #'cl-second) -(defalias 'sly--lisp-indent-parse-state-prev #'cl-third) - -(defvar-local sly-common-lisp-style nil) - -;;; `sly-define-common-lisp-style' updates the docstring of -;;; `sly-common-lisp-style', using this as the base. -(put 'sly-common-lisp-style 'sly-common-lisp-style-base-doc - "Name of the Common Lisp indentation style used in the current buffer. -Set this by giving eg. - - ;; -*- common-lisp-style: sbcl -*- - -in the first line of the file, or by calling `sly-common-lisp-set-style'. If -buffer has no style specified, but `sly-common-lisp-style-default' is set, that -style is used instead. Use `sly-define-common-lisp-style' to define new styles.") - -;;; `lisp-mode' kills all buffer-local variables. Setting the -;;; `permanent-local' property allows us to retain the style. -(put 'sly-common-lisp-style 'permanent-local t) - -;;; Mark as safe when the style doesn't evaluate arbitrary code. -(put 'sly-common-lisp-style 'safe-local-variable 'sly--lisp-indent-safe-style-p) - -;;; Common Lisp indentation style specifications. -(defvar sly--common-lisp-styles (make-hash-table :test 'equal)) - -;; unused -(defsubst sly--lisp-indent-delete-style (stylename) - (remhash stylename sly--common-lisp-styles)) - -(defun sly--lisp-indent-find-style (stylename) - (let ((name (if (symbolp stylename) - (symbol-name stylename) - stylename))) - (or (gethash name sly--common-lisp-styles) - (error "Unknown Common Lisp style: %s" name)))) - -(defun sly--lisp-indent-safe-style-p (stylename) - "True for known Common Lisp style without an :EVAL option. -Ie. styles that will not evaluate arbitrary code on activation." - (let* ((style (ignore-errors (sly--lisp-indent-find-style stylename))) - (base (sly--common-lisp-style-inherits style))) - (and style - (not (sly--common-lisp-style-hook style)) - (or (not base) - (sly--lisp-indent-safe-style-p base))))) - -(defun sly--lisp-indent-add-style (stylename inherits variables - indentation hooks documentation) - ;; Invalidate indentation methods cached in common-lisp-active-style. - (maphash (lambda (k v) - (puthash k (cl-copy-list v) sly--common-lisp-styles)) - sly--common-lisp-styles) - ;; Add/Redefine the specified style. - (puthash stylename - (sly--common-lisp-make-style - stylename inherits - variables indentation - hooks documentation) - sly--common-lisp-styles) - ;; Frob `sly-common-lisp-style' docstring. - (let ((doc (get 'sly-common-lisp-style - 'sly-common-lisp-style-base-doc)) - (all nil)) - (setq doc (concat doc "\n\nAvailable styles are:\n")) - (maphash (lambda (name style) - (push (list name (sly--common-lisp-style-docstring style)) all)) - sly--common-lisp-styles) - (dolist (info (sort all (lambda (a b) (string< (car a) (car b))))) - (let ((style-name (cl-first info)) - (style-doc (cl-second info))) - (if style-doc - (setq doc (concat doc - "\n " style-name "\n" - " " style-doc "\n")) - (setq doc (concat doc "\n " style-name " (undocumented)\n"))))) - (put 'sly-common-lisp-style 'variable-documentation doc)) - stylename) - -;;; Activate STYLENAME, adding its indentation methods to METHODS -- and -;;; recurse on style inherited from. -(defun sly--lisp-indent-activate-style (stylename methods) - (let* ((style (sly--lisp-indent-find-style stylename)) - (basename (sly--common-lisp-style-inherits style))) - ;; Recurse on parent. - (when basename - (sly--lisp-indent-activate-style basename methods)) - ;; Copy methods - (dolist (spec (sly--common-lisp-style-indentation style)) - (puthash (cl-first spec) (cl-second spec) methods)) - ;; Bind variables. - (dolist (var (sly--common-lisp-style-variables style)) - (set (make-local-variable (cl-first var)) (cl-second var))) - ;; Run hook. - (let ((hook (sly--common-lisp-style-hook style))) - (when hook - (funcall hook))))) - -;;; When a style is being used, `sly--lisp-indent-active-style' holds a cons -;;; -;;; (STYLE . METHODS) -;;; -;;; where STYLE is the list specifying the currently active style, and -;;; METHODS is the table of indentation methods -- including inherited -;;; ones -- for it. `sly--lisp-indent-active-style-methods' is reponsible -;;; for keeping this up to date. -(defvar-local sly--lisp-indent-active-style nil) - -;;; Makes sure sly--lisp-indent-active-style corresponds to sly-common-lisp-style, and -;;; pick up redefinitions, etc. Returns the method table for the currently -;;; active style. -(defun sly--lisp-indent-active-style-methods () - (let* ((name (or sly-common-lisp-style (bound-and-true-p common-lisp-style))) - (style (when name (sly--lisp-indent-find-style name)))) - (if (eq style (car sly--lisp-indent-active-style)) - (cdr sly--lisp-indent-active-style) - (when style - (let ((methods (make-hash-table :test 'equal))) - (sly--lisp-indent-activate-style name methods) - (setq sly--lisp-indent-active-style (cons style methods)) - methods))))) - -(defvar sly--lisp-indent-set-style-history nil) - -(defun sly--lisp-indent-style-names () - (let (names) - (maphash (lambda (k v) - (push (cons k v) names)) - sly--common-lisp-styles) - names)) - -;;;###autoload -(defun sly-common-lisp-set-style (stylename) - "Set current buffer to use the Common Lisp style STYLENAME. -STYLENAME, a string, must be an existing Common Lisp style. Styles -are added (and updated) using `sly-define-common-lisp-style'. - -The buffer-local variable `sly-common-lisp-style' will get set to STYLENAME. - -A Common Lisp style is composed of local variables, indentation -specifications, and may also contain arbitrary elisp code to run upon -activation." - (interactive - (list (let ((completion-ignore-case t) - (prompt "Specify Common Lisp indentation style: ")) - (completing-read prompt - (sly--lisp-indent-style-names) nil t nil - 'sly--lisp-indent-set-style-history)))) - (setq sly-common-lisp-style (sly--common-lisp-style-name - (sly--lisp-indent-find-style stylename)) - sly--lisp-indent-active-style nil) - ;; Actually activates the style. - (sly--lisp-indent-active-style-methods) - stylename) - -;;;###autoload -(defmacro sly-define-common-lisp-style (name documentation &rest options) - "Define a Common Lisp indentation style. - -NAME is the name of the style. - -DOCUMENTATION is the docstring for the style, automatically added to the -docstring of `sly-common-lisp-style'. - -OPTIONS are: - - (:variables (name value) ...) - - Specifying the buffer local variables associated with the style. - - (:indentation (symbol spec) ...) - - Specifying custom indentations associated with the style. SPEC is - a normal `sly-common-lisp-indent-function' indentation specification. - - (:inherit style) - - Inherit variables and indentations from another Common Lisp style. - - (:eval form ...) - - Lisp code to evaluate when activating the style. This can be used to - eg. activate other modes. It is possible that over the lifetime of - a buffer same style gets activated multiple times, so code in :eval - option should cope with that. -" - (declare (indent 1)) - (when (consp documentation) - (setq options (cons documentation options) - documentation nil)) - `(sly--lisp-indent-add-style ,name - ,(cadr (assoc :inherit options)) - ',(cdr (assoc :variables options)) - ',(cdr (assoc :indentation options)) - ,(when (assoc :eval options) - `(lambda () - ,@(cdr (assoc :eval options)))) - ,documentation)) - -(sly-define-common-lisp-style "basic-common" - (:variables - (sly-lisp-indent-maximum-backtracking 6) - (sly-lisp-tag-indentation 1) - (sly-lisp-tag-body-indentation 3) - (sly-lisp-backquote-indentation t) - (sly-lisp-loop-indent-subclauses t) - (sly-lisp-loop-indent-forms-like-keywords nil) - (sly-lisp-simple-loop-indentation 2) - (sly-lisp-align-keywords-in-calls t) - (sly-lisp-lambda-list-indentation t) - (sly-lisp-lambda-list-keyword-alignment nil) - (sly-lisp-lambda-list-keyword-parameter-indentation 2) - (sly-lisp-lambda-list-keyword-parameter-alignment nil) - (sly-lisp-indent-defun-method (4 &lambda &body)) - (sly-lisp-loop-clauses-indentation 2) - (sly-lisp-loop-indent-body-forms-relative-to-loop-start nil) - (sly-lisp-loop-body-forms-indentation 3))) - -(sly-define-common-lisp-style "basic-emacs25" - "This style adds a workaround needed for Emacs 25" - (:inherit "basic-common") - (:variables - ;; Without these (;;foo would get a space inserted between - ;; ( and ; by indent-sexp. - (comment-indent-function (lambda () nil)))) - -(sly-define-common-lisp-style "basic-emacs26" - "This style is the same as basic-common. It doesn't need or - want the workaround used in Emacs 25. In Emacs 26, that - workaround introduces a weird behavior where a single - semicolon breaks the mode and causes the cursor to move to the - start of the line after every character inserted." - (:inherit "basic-common")) - -(sly-define-common-lisp-style "basic" - "This style merely gives all identation variables their default values, - making it easy to create new styles that are proof against user - customizations. It also adjusts comment indentation from default. - All other predefined modes inherit from basic." - (:inherit (if (>= emacs-major-version 26) - "basic-emacs26" - "basic-emacs25"))) - -(sly-define-common-lisp-style "classic" - "This style of indentation emulates the most striking features of 1995 - vintage cl-indent.el once included as part of Slime: IF indented by two - spaces, and CASE clause bodies indentented more deeply than the keys." - (:inherit "basic") - (:variables - (sly-lisp-lambda-list-keyword-parameter-indentation 0)) - (:indentation - (case (4 &rest (&whole 2 &rest 3))) - (if (4 2 2)))) - -(sly-define-common-lisp-style "modern" - "A good general purpose style. Turns on lambda-list keyword and keyword - parameter alignment, and turns subclause aware loop indentation off. - (Loop indentation so because simpler style is more prevalent in existing - sources, not because it is necessarily preferred.)" - (:inherit "basic") - (:variables - (sly-lisp-lambda-list-keyword-alignment t) - (sly-lisp-lambda-list-keyword-parameter-alignment t) - (sly-lisp-lambda-list-keyword-parameter-indentation 0) - (sly-lisp-loop-indent-subclauses nil))) - -(sly-define-common-lisp-style "sbcl" - "Style used in SBCL sources. A good if somewhat intrusive general purpose - style based on the \"modern\" style. Adds indentation for a few SBCL - specific constructs, sets indentation to use spaces instead of tabs, - fill-column to 78, and activates whitespace-mode to show tabs and trailing - whitespace." - (:inherit "modern") - (:eval - (whitespace-mode 1)) - (:variables - (whitespace-style (tabs trailing)) - (indent-tabs-mode nil) - (comment-fill-column nil) - (fill-column 78)) - (:indentation - (def!constant (as defconstant)) - (def!macro (as defmacro)) - (def!method (as defmethod)) - (def!struct (as defstruct)) - (def!type (as deftype)) - (defmacro-mundanely (as defmacro)) - (deftransform (as defmacro)) - (define-source-transform (as defun)) - (!def-type-translator (as defun)) - (!def-debug-command (as defun)))) - -(defcustom sly-common-lisp-style-default nil - "Name of the Common Lisp indentation style to use in lisp-mode buffers if -none has been specified." - :type `(choice (const :tag "None" nil) - ,@(mapcar (lambda (spec) - `(const :tag ,(car spec) ,(car spec))) - (sly--lisp-indent-style-names)) - (string :tag "Other")) - :group 'sly-lisp-indent) - -;;; If style is being used, that's a sufficient invitation to snag -;;; the indentation function. -(defun sly--lisp-indent-lisp-mode-hook () - (let ((style (or sly-common-lisp-style - (bound-and-true-p common-lisp-style) - sly-common-lisp-style-default))) - (when style - (setq-local lisp-indent-function #'sly-common-lisp-indent-function) - (sly-common-lisp-set-style style)))) -(add-hook 'lisp-mode-hook #'sly--lisp-indent-lisp-mode-hook) - - -;;;; The indentation specs are stored at three levels. In order of priority: -;;;; -;;;; 1. Indentation as set by current style, from the indentation table -;;;; in the current style. -;;;; -;;;; 2. Globally set indentation, from the `sly-common-lisp-indent-function' -;;;; property of the symbol. -;;;; -;;;; 3. Per-package indentation derived by the system. A live Common Lisp -;;;; system may (via Slime, eg.) add indentation specs to -;;;; sly-common-lisp-system-indentation, where they are associated with -;;;; the package of the symbol. Then we run some lossy heuristics and -;;;; find something that looks promising. -;;;; -;;;; FIXME: for non-system packages the derived indentation should probably -;;;; take precedence. - -;;; This maps symbols into lists of (INDENT . PACKAGES) where INDENT is -;;; an indentation spec, and PACKAGES are the names of packages where this -;;; applies. -;;; -;;; We never add stuff here by ourselves: this is for things like Slime to -;;; fill. -(defvar sly-common-lisp-system-indentation (make-hash-table :test 'equal)) - -(defun sly--lisp-indent-guess-current-package () - (save-excursion - (ignore-errors - (when (let ((case-fold-search t)) - (search-backward "(in-package ")) - (re-search-forward "[ :\"]+") - (let ((start (point))) - (re-search-forward "[\":)]") - (upcase (buffer-substring-no-properties - start (1- (point))))))))) - -(defvar sly--lisp-indent-current-package-function - 'sly--lisp-indent-guess-current-package - "Used to derive the package name to use for indentation at a -given point. Defaults to `sly--lisp-indent-guess-current-package'.") - -(defun sly--lisp-indent-symbol-package (string) - (if (and (stringp string) (string-match ":" string)) - (let ((p (match-beginning 0))) - (if (eq p 0) - "KEYWORD" - (upcase (substring string 0 p)))) - (funcall sly--lisp-indent-current-package-function))) - -(defun sly--lisp-indent-get-indentation (name &optional full) - "Retrieves the indentation information for NAME." - (let ((method - (or - ;; From style - (let ((methods (sly--lisp-indent-active-style-methods))) - (and methods (gethash name methods))) - ;; From global settings. - (get name 'sly-common-lisp-indent-function) - (get name 'common-lisp-indent-function) - ;; From system derived information. - (let ((system-info (gethash name sly-common-lisp-system-indentation))) - (if (not (cdr system-info)) - (caar system-info) - (let ((guess nil) - (guess-n 0) - (package (sly--lisp-indent-symbol-package full))) - (cl-dolist (info system-info guess) - (let* ((pkgs (cdr info)) - (n (length pkgs))) - (cond ((member package pkgs) - ;; This is it. - (cl-return (car info))) - ((> n guess-n) - ;; If we can't find the real thing, go with the one - ;; accessible in most packages. - (setf guess (car info) - guess-n n))))))))))) - (if (eq 'as (car-safe method)) - (sly--lisp-indent-get-indentation (cadr method)) - method))) - -;;;; LOOP indentation, the simple version - -(defun sly--lisp-indent-loop-type (loop-start) - "Returns the type of the loop form at LOOP-START. -Possible types are SIMPLE, SIMPLE/SPLIT, EXTENDED, and EXTENDED/SPLIT. */SPLIT -refers to extended loops whose body does not start on the same line as the -opening parenthesis of the loop." - (let (comment-split) - (condition-case () - (save-excursion - (goto-char loop-start) - (let ((line (line-number-at-pos)) - (maybe-split t)) - (forward-char 1) - (forward-sexp 1) - (save-excursion - (when (looking-at "\\s-*\\\n*;") - (search-forward ";") - (backward-char 1) - (if (= line (line-number-at-pos)) - (setq maybe-split nil) - (setq comment-split t)))) - (forward-sexp 1) - (backward-sexp 1) - (if (eq (char-after) ?\() - (if (or (not maybe-split) (= line (line-number-at-pos))) - 'simple - 'simple/split) - (if (or (not maybe-split) (= line (line-number-at-pos))) - 'extended - 'extended/split)))) - (error - (if comment-split - 'simple/split - 'simple))))) - -(defun sly--lisp-indent-trailing-comment () - (ignore-errors - ;; If we had a trailing comment just before this, find it. - (save-excursion - (backward-sexp) - (forward-sexp) - (when (looking-at "\\s-*;") - (search-forward ";") - (1- (current-column)))))) - -;;;###autoload -(defun sly-common-lisp-indent-function (indent-point state) - "Function to indent the arguments of a Lisp function call. -This is suitable for use as the value of the variable -`lisp-indent-function'. INDENT-POINT is the point at which the -indentation function is called, and STATE is the -`parse-partial-sexp' state at that position. Browse the -`sly-lisp-indent' customize group for options affecting the behavior -of this function. - -If the indentation point is in a call to a Lisp function, that -function's `sly-common-lisp-indent-function' property specifies how -this function should indent it. Possible values for this -property are: - -* defun, meaning indent according to - `sly-lisp-indent-defun-method'; i.e., like (4 &lambda &body), - as explained below. - -* any other symbol, meaning a function to call. The function - should take the arguments: PATH STATE INDENT-POINT SEXP-COLUMN - NORMAL-INDENT. PATH is a list of integers describing the - position of point in terms of list-structure with respect to - the containing lists. For example, in - ((a b c (d foo) f) g), foo has a path of (0 3 1). In other - words, to reach foo take the 0th element of the outermost list, - then the 3rd element of the next list, and finally the 1st - element. STATE and INDENT-POINT are as in the arguments to - `sly-common-lisp-indent-function'. SEXP-COLUMN is the column of - the open parenthesis of the innermost containing list. - NORMAL-INDENT is the column the indentation point was - originally in. This function should behave like - `sly--lisp-indent-259'. - -* an integer N, meaning indent the first N arguments like - function arguments, and any further arguments like a body. - This is equivalent to (4 4 ... &body). - -* a list starting with `as' specifies an indirection: indentation - is done as if the form being indented had started with the - second element of the list. - -* any other list. The list element in position M specifies how - to indent the Mth function argument. If there are fewer - elements than function arguments, the last list element applies - to all remaining arguments. The accepted list elements are: - - * nil, meaning the default indentation. - - * an integer, specifying an explicit indentation. - - * &lambda. Indent the argument (which may be a list) by 4. - - * &rest. When used, this must be the penultimate element. The - element after this one applies to all remaining arguments. - - * &body. This is equivalent to &rest lisp-body-indent, i.e., indent - all remaining elements by `lisp-body-indent'. - - * &whole. This must be followed by nil, an integer, or a - function symbol. This indentation is applied to the - associated argument, and as a base indent for all remaining - arguments. For example, an integer P means indent this - argument by P, and all remaining arguments by P, plus the - value specified by their associated list element. - - * a symbol. A function to call, with the 6 arguments specified above. - - * a list, with elements as described above. This applies when the - associated function argument is itself a list. Each element of the list - specifies how to indent the associated argument. - -For example, the function `case' has an indent property -\(4 &rest (&whole 2 &rest 1)), meaning: - * indent the first argument by 4. - * arguments after the first should be lists, and there may be any number - of them. The first list element has an offset of 2, all the rest - have an offset of 2+1=3." - (sly--lisp-indent-function-1 indent-point state)) - -(define-minor-mode sly-lisp-indent-compatibility-mode - "Replace the definition of `common-lisp-indent-function' with `sly-common-lisp-indent-function'. - -For backwards compatibility with the old sly-cl-indent.el, which -used to do this by default." - :group 'sly-lisp-indent - (if sly-lisp-indent-compatibility-mode - (advice-add 'common-lisp-indent-function - :override 'sly-common-lisp-indent-function) - (advice-remove 'common-lisp-indent-function - 'sly-common-lisp-indent-function))) - - -(defvar sly--lisp-indent-feature-expr-regexp "#!?\\(+\\|-\\)") - -;;; Semi-feature-expression aware keyword check. -(defun sly--lisp-indent-looking-at-keyword () - (or (looking-at ":") - (and (looking-at sly--lisp-indent-feature-expr-regexp) - (save-excursion - (forward-sexp) - (skip-chars-forward " \t\n") - (sly--lisp-indent-looking-at-keyword))))) - -;;; Semi-feature-expression aware backwards movement for keyword -;;; argument pairs. -(defun sly--lisp-indent-backward-keyword-argument () - (ignore-errors - (backward-sexp 2) - (when (looking-at sly--lisp-indent-feature-expr-regexp) - (cond ((ignore-errors - (save-excursion - (backward-sexp 2) - (looking-at sly--lisp-indent-feature-expr-regexp))) - (sly--lisp-indent-backward-keyword-argument)) - ((ignore-errors - (save-excursion - (backward-sexp 1) - (looking-at ":"))) - (backward-sexp)))) - t)) - -(defvar sly--lisp-indent-containing-sexp) - -(defun sly--lisp-indent-function-1 (indent-point state) - ;; If we're looking at a splice, move to the first comma. - (when (or (eq (char-before) ?,) - (and (eq (char-before) ?@) - (eq (char-before (1- (point))) ?,))) - (when (re-search-backward "[^,@'],") - (forward-char 1))) - (let ((normal-indent (current-column))) - ;; Walk up list levels until we see something - ;; which does special things with subforms. - (let ((depth 0) - ;; Path describes the position of point in terms of - ;; list-structure with respect to containing lists. - ;; `foo' has a path of (0 3 1) in `((a b c (d foo) f) g)'. - (path ()) - ;; set non-nil when somebody works out the indentation to use - calculated - ;; If non-nil, this is an indentation to use - ;; if nothing else specifies it more firmly. - tentative-calculated - ;; (last-point indent-point) - ;; the position of the open-paren of the innermost containing list - (containing-form-start (sly--lisp-indent-parse-state-start state)) - ;; the column of the above - sexp-column) - ;; Move to start of innermost containing list - (goto-char containing-form-start) - (setq sexp-column (current-column)) - - ;; Look over successively less-deep containing forms - (while (and (not calculated) - (< depth sly-lisp-indent-maximum-backtracking)) - (let ((sly--lisp-indent-containing-sexp (point))) - (forward-char 1) - (parse-partial-sexp (point) indent-point 1 t) - ;; Move to the car of the relevant containing form - (let (tem full function method tentative-defun) - (if (not (looking-at "\\sw\\|\\s_")) - ;; This form doesn't seem to start with a symbol - (setq function nil method nil full nil) - (setq tem (point)) - (forward-sexp 1) - (setq full (downcase (buffer-substring-no-properties tem (point))) - function full) - (goto-char tem) - (setq tem (intern-soft function) - method (sly--lisp-indent-get-indentation tem)) - (cond ((and (null method) - (string-match ":[^:]+" function)) - ;; The pleblisp package feature - (setq function (substring function (1+ (match-beginning 0))) - method (sly--lisp-indent-get-indentation - (intern-soft function) full))) - ((and (null method)) - ;; backwards compatibility - (setq method (sly--lisp-indent-get-indentation tem))))) - (let ((n 0)) - ;; How far into the containing form is the current form? - (if (< (point) indent-point) - (while (ignore-errors - (forward-sexp 1) - (if (>= (point) indent-point) - nil - (parse-partial-sexp (point) - indent-point 1 t) - (setq n (1+ n)) - t)))) - (setq path (cons n path))) - - ;; Guess. - (when (and (not method) function (null (cdr path))) - ;; (package prefix was stripped off above) - (cond ((and (string-match "\\`def" function) - (not (string-match "\\`default" function)) - (not (string-match "\\`definition" function)) - (not (string-match "\\`definer" function))) - (setq tentative-defun t)) - ((string-match - (eval-when-compile - (concat "\\`\\(" - (regexp-opt '("with" "without" "do")) - "\\)-")) - function) - (setq method '(&lambda &body))))) - - ;; #+ and #- cleverness. - (save-excursion - (goto-char indent-point) - (backward-sexp) - (let ((indent (current-column))) - (when - (or (looking-at sly--lisp-indent-feature-expr-regexp) - (ignore-errors - (backward-sexp) - (when (looking-at sly--lisp-indent-feature-expr-regexp) - (setq indent (current-column)) - (let ((line (line-number-at-pos))) - (while - (ignore-errors - (backward-sexp 2) - (and (= line (line-number-at-pos)) - (looking-at sly--lisp-indent-feature-expr-regexp))) - (setq indent (current-column)))) - t))) - (setq calculated (list indent containing-form-start))))) - - (cond ((and (or (eq (char-after (1- sly--lisp-indent-containing-sexp)) ?\') - (and (not sly-lisp-backquote-indentation) - (eq (char-after (1- sly--lisp-indent-containing-sexp)) ?\`))) - (not (eq (char-after (- sly--lisp-indent-containing-sexp 2)) ?\#))) - ;; No indentation for "'(...)" elements - (setq calculated (1+ sexp-column))) - ((eq (char-after (1- sly--lisp-indent-containing-sexp)) ?\#) - ;; "#(...)" - (setq calculated (1+ sexp-column))) - ((null method) - ;; If this looks like a call to a `def...' form, - ;; think about indenting it as one, but do it - ;; tentatively for cases like - ;; (flet ((defunp () - ;; nil))) - ;; Set both normal-indent and tentative-calculated. - ;; The latter ensures this value gets used - ;; if there are no relevant containing constructs. - ;; The former ensures this value gets used - ;; if there is a relevant containing construct - ;; but we are nested within the structure levels - ;; that it specifies indentation for. - (if tentative-defun - (setq tentative-calculated - (sly--lisp-indent-call-method - function sly-lisp-indent-defun-method - path state indent-point - sexp-column normal-indent) - normal-indent tentative-calculated) - (when sly-lisp-align-keywords-in-calls - ;; No method so far. If we're looking at a keyword, - ;; align with the first keyword in this expression. - ;; This gives a reasonable indentation to most things - ;; with keyword arguments. - (save-excursion - (goto-char indent-point) - (back-to-indentation) - (when (sly--lisp-indent-looking-at-keyword) - (while (sly--lisp-indent-backward-keyword-argument) - (when (sly--lisp-indent-looking-at-keyword) - (setq calculated - (list (current-column) - containing-form-start))))))))) - ((integerp method) - ;; convenient top-level hack. - ;; (also compatible with lisp-indent-function) - ;; The number specifies how many `distinguished' - ;; forms there are before the body starts - ;; Equivalent to (4 4 ... &body) - (setq calculated (cond ((cdr path) normal-indent) - ((<= (car path) method) - ;; `distinguished' form - (list (+ sexp-column 4) - containing-form-start)) - ((= (car path) (1+ method)) - ;; first body form. - (+ sexp-column lisp-body-indent)) - (t - ;; other body form - normal-indent)))) - (t - (setq calculated - (sly--lisp-indent-call-method - function method path state indent-point - sexp-column normal-indent))))) - (goto-char sly--lisp-indent-containing-sexp) - ;; (setq last-point sly--lisp-indent-containing-sexp) - (unless calculated - (condition-case () - (progn (backward-up-list 1) - (setq depth (1+ depth))) - (error - (setq depth sly-lisp-indent-maximum-backtracking)))))) - - (or calculated tentative-calculated - ;; Fallback. - ;; - ;; Instead of punting directly to calculate-lisp-indent we - ;; handle a few of cases it doesn't deal with: - ;; - ;; A: (foo ( - ;; bar zot - ;; quux)) - ;; - ;; would align QUUX with ZOT. - ;; - ;; B: - ;; (foo (or x - ;; y) t - ;; z) - ;; - ;; would align the Z with Y. - ;; - ;; C: - ;; (foo ;; Comment - ;; (bar) - ;; ;; Comment 2 - ;; (quux)) - ;; - ;; would indent BAR and QUUX by one. - (ignore-errors - (save-excursion - (goto-char indent-point) - (back-to-indentation) - (let ((p (point))) - (goto-char containing-form-start) - (down-list) - (let ((one (current-column))) - (skip-chars-forward " \t") - (if (or (eolp) (looking-at ";")) - ;; A. - (list one containing-form-start) - (forward-sexp 2) - (backward-sexp) - (if (/= p (point)) - ;; B. - (list (current-column) containing-form-start) - (backward-sexp) - (forward-sexp) - (let ((tmp (+ (current-column) 1))) - (skip-chars-forward " \t") - (if (looking-at ";") - ;; C. - (list tmp containing-form-start))))))))))))) - - - -;; Dynamically bound in `sly--lisp-indent-call-method'. -(defvar sly--lisp-indent-error-function) - -(defun sly--lisp-indent-call-method (function method path state indent-point - sexp-column normal-indent) - (let ((sly--lisp-indent-error-function function)) - (if (symbolp method) - (funcall method - path state indent-point - sexp-column normal-indent) - (sly--lisp-indent-259 method path state indent-point - sexp-column normal-indent)))) - -(defun sly--lisp-indent-report-bad-format (m) - (error "%s has a badly-formed %s property: %s" - ;; Love those free variable references!! - sly--lisp-indent-error-function - 'sly-common-lisp-indent-function m)) - - -;; Lambda-list indentation is now done in `sly--lisp-indent-lambda-list'. -;; See also `sly-lisp-lambda-list-keyword-alignment', -;; `sly-lisp-lambda-list-keyword-parameter-alignment' and -;; `sly-lisp-lambda-list-keyword-parameter-indentation' -- dvl - -(defvar sly--lisp-indent-lambda-list-keywords-regexp - "&\\(\ -optional\\|rest\\|key\\|allow-other-keys\\|aux\\|whole\\|body\\|\ -environment\\|more\ -\\)\\>" - "Regular expression matching lambda-list keywords.") - -(defun sly--lisp-indent-lambda-list - (indent-point sexp-column containing-form-start) - (if (not sly-lisp-lambda-list-indentation) - (1+ sexp-column) - (sly--lisp-indent-properly-indent-lambda-list - indent-point sexp-column containing-form-start))) - -(defun sly--lisp-indent-properly-indent-lambda-list - (indent-point sexp-column containing-form-start) - (cond - ((save-excursion - (goto-char indent-point) - (back-to-indentation) - (looking-at sly--lisp-indent-lambda-list-keywords-regexp)) - ;; We're facing a lambda-list keyword. - (if sly-lisp-lambda-list-keyword-alignment - ;; Align to the first keyword if any, or to the beginning of - ;; the lambda-list. - (save-excursion - (goto-char containing-form-start) - (down-list) - (let ((key-indent nil) - (next t)) - (while (and next (< (point) indent-point)) - (if (looking-at sly--lisp-indent-lambda-list-keywords-regexp) - (setq key-indent (current-column) - next nil) - (setq next (ignore-errors (forward-sexp) t)) - (if next - (ignore-errors - (forward-sexp) - (backward-sexp))))) - (or key-indent - (1+ sexp-column)))) - ;; Align to the beginning of the lambda-list. - (1+ sexp-column))) - (t - ;; Otherwise, align to the first argument of the last lambda-list - ;; keyword, the keyword itself, or the beginning of the - ;; lambda-list. - (save-excursion - (goto-char indent-point) - (let ((indent nil) - (next t)) - (while (and next (> (point) containing-form-start)) - (setq next (ignore-errors (backward-sexp) t)) - (let* ((col (current-column)) - (pos - (save-excursion - (ignore-errors (forward-sexp)) - (skip-chars-forward " \t") - (if (eolp) - (+ col sly-lisp-lambda-list-keyword-parameter-indentation) - col)))) - (if (looking-at sly--lisp-indent-lambda-list-keywords-regexp) - (setq indent - (if sly-lisp-lambda-list-keyword-parameter-alignment - (or indent pos) - (+ col sly-lisp-lambda-list-keyword-parameter-indentation)) - next nil) - (setq indent col)))) - (or indent (1+ sexp-column))))))) - -(defun sly--lisp-indent-lambda-list-initial-value-form-p (point) - (let ((state 'x) - (point (save-excursion - (goto-char point) - (back-to-indentation) - (point)))) - (save-excursion - (backward-sexp) - (ignore-errors (down-list 1)) - (while (and point (< (point) point)) - (cond ((looking-at "&\\(key\\|optional\\|aux\\)") - (setq state 'key)) - ((looking-at sly--lisp-indent-lambda-list-keywords-regexp) - (setq state 'x))) - (if (not (ignore-errors (forward-sexp) t)) - (setq point nil) - (ignore-errors - (forward-sexp) - (backward-sexp)) - (cond ((> (point) point) - (backward-sexp) - (when (eq state 'var) - (setq state 'x)) - (or (ignore-errors - (down-list 1) - (cond ((> (point) point) - (backward-up-list)) - ((eq 'key state) - (setq state 'var))) - t) - (setq point nil))) - ((eq state 'var) - (setq state 'form)))))) - (eq 'form state))) - -;; Blame the crufty control structure on dynamic scoping -;; -- not on me! -(defun sly--lisp-indent-259 - (method path state indent-point sexp-column normal-indent) - (catch 'exit - (let* ((p (cdr path)) - (containing-form-start (elt state 1)) - (n (1- (car path))) - tem tail) - (if (not (consp method)) - (sly--lisp-indent-report-bad-format method)) - (while n - ;; This while loop is for advancing along a method - ;; until the relevant (possibly &rest/&body) pattern - ;; is reached. - ;; n is set to (1- n) and method to (cdr method) - ;; each iteration. - (setq tem (car method)) - - (or (eq tem 'nil) ;default indentation - (eq tem '&lambda) ;lambda list - (and (eq tem '&body) (null (cdr method))) - (and (eq tem '&rest) - (consp (cdr method)) - (null (cddr method))) - (integerp tem) ;explicit indentation specified - (and (consp tem) ;destructuring - (or (consp (car tem)) - (and (eq (car tem) '&whole) - (or (symbolp (cadr tem)) - (integerp (cadr tem)))))) - (and (symbolp tem) ;a function to call to do the work. - (null (cdr method))) - (sly--lisp-indent-report-bad-format method)) - (cond ((eq tem '&body) - ;; &body means (&rest <lisp-body-indent>) - (throw 'exit - (if (null p) - (+ sexp-column lisp-body-indent) - normal-indent))) - ((eq tem '&rest) - ;; this pattern holds for all remaining forms - (setq tail (> n 0) - n 0 - method (cdr method))) - ((> n 0) - ;; try next element of pattern - (setq n (1- n) - method (cdr method)) - (if (< n 0) - ;; Too few elements in pattern. - (throw 'exit normal-indent))) - ((eq tem 'nil) - (throw 'exit (if (consp normal-indent) - normal-indent - (list normal-indent containing-form-start)))) - ((eq tem '&lambda) - (throw 'exit - (cond ((not (eq (char-before) ?\))) - ;; If it's not a list at all, indent it - ;; like body instead. - (if (null p) - (+ sexp-column lisp-body-indent) - normal-indent)) - ((sly--lisp-indent-lambda-list-initial-value-form-p indent-point) - (if (consp normal-indent) - normal-indent - (list normal-indent containing-form-start))) - ((null p) - (list (+ sexp-column 4) containing-form-start)) - (t - ;; Indentation within a lambda-list. -- dvl - (list (sly--lisp-indent-lambda-list - indent-point - sexp-column - containing-form-start) - containing-form-start))))) - ((integerp tem) - (throw 'exit - (if (null p) ;not in subforms - (list (+ sexp-column tem) containing-form-start) - normal-indent))) - ((symbolp tem) ;a function to call - (throw 'exit - (funcall tem path state indent-point - sexp-column normal-indent))) - (t - ;; must be a destructing frob - (if p - ;; descend - (setq method (cddr tem) - n (car p) - p (cdr p) - tail nil) - (let ((wholep (eq '&whole (car tem)))) - (setq tem (cadr tem)) - (throw 'exit - (cond (tail - (if (and wholep (integerp tem) - (save-excursion - (goto-char indent-point) - (back-to-indentation) - (looking-at "\\sw"))) - ;; There's a further level of - ;; destructuring, but we're looking at a - ;; word -- indent to sexp. - (+ sexp-column tem) - normal-indent)) - ((not tem) - (list normal-indent - containing-form-start)) - ((integerp tem) - (list (+ sexp-column tem) - containing-form-start)) - (t - (funcall tem path state indent-point - sexp-column normal-indent)))))))))))) - -(defun sly--lisp-indent-tagbody (path state indent-point sexp-column normal-indent) - (if (cdr path) - normal-indent - (save-excursion - (goto-char indent-point) - (back-to-indentation) - (list (cond ((looking-at "\\sw\\|\\s_") - ;; a tagbody tag - (+ sexp-column sly-lisp-tag-indentation)) - ((integerp sly-lisp-tag-body-indentation) - (+ sexp-column sly-lisp-tag-body-indentation)) - ((eq sly-lisp-tag-body-indentation 't) - (condition-case () - (progn (backward-sexp 1) (current-column)) - (error (1+ sexp-column)))) - (t (+ sexp-column lisp-body-indent))) - (nth 1 state))))) - -(defun sly--lisp-indent-do (path state indent-point sexp-column normal-indent) - (if (>= (car path) 3) - (let ((sly-lisp-tag-body-indentation lisp-body-indent)) - (sly--lisp-indent-tagbody - path state indent-point sexp-column normal-indent)) - (sly--lisp-indent-259 - '((&whole nil &rest - ;; the following causes weird indentation - ;;(&whole 1 1 2 nil) - ) - (&whole nil &rest 1)) - path state indent-point sexp-column normal-indent))) - -(defun sly--lisp-indent-defsetf - (path state indent-point sexp-column normal-indent) - (ignore normal-indent) - (let ((form-start (nth 1 state))) - (list - (cond - ;; Inside the lambda-list in a long-form defsetf. - ((and (eq 2 (car path)) (cdr path)) - (sly--lisp-indent-lambda-list indent-point sexp-column form-start)) - ;; Long form: has a lambda-list. - ((or (cdr path) - (save-excursion - (goto-char form-start) - (ignore-errors - (down-list) - (forward-sexp 3) - (backward-sexp) - (looking-at "nil\\|(")))) - (+ sexp-column (if (<= 1 (car path) 3) 4 2))) - ;; Short form. - (t (+ sexp-column (if (<= 1 (car path) 2) 4 2)))) - form-start))) - -(defun sly--lisp-indent-beginning-of-defmethod-qualifiers () - (let ((case-fold-search t) - (regexp "(\\(?:\\(def\\)\\|\\(:\\)\\)method")) - (ignore-errors - (while (not (looking-at regexp)) (backward-up-list)) - (cond ((match-string 1) - (forward-char) - ;; Skip name. - (forward-sexp 2) - 1) - ((match-string 2) - (forward-char) - (forward-sexp 1) - 0))))) - -;; LISP-INDENT-DEFMETHOD now supports the presence of more than one method -;; qualifier and indents the method's lambda list properly. -- dvl -(defun sly--lisp-indent-defmethod - (path state indent-point sexp-column normal-indent) - (sly--lisp-indent-259 - (let ((nskip nil)) - (if (save-excursion - (when (setq nskip (sly--lisp-indent-beginning-of-defmethod-qualifiers)) - (skip-chars-forward " \t\n") - (while (looking-at "\\sw\\|\\s_") - (cl-incf nskip) - (forward-sexp) - (skip-chars-forward " \t\n")) - t)) - (nconc (make-list nskip 4) '(&lambda &body)) - (sly--lisp-indent-get-indentation 'defun))) - path state indent-point sexp-column normal-indent)) - -(defun sly--lisp-indent-function-lambda-hack (path state indent-point - sexp-column normal-indent) - (ignore indent-point state) - ;; indent (function (lambda () <newline> <body-forms>)) kludgily. - (if (or (cdr path) ; wtf? - (> (car path) 3)) - ;; line up under previous body form - normal-indent - ;; line up under function rather than under lambda in order to - ;; conserve horizontal space. (Which is what #' is for.) - (condition-case () - (save-excursion - (backward-up-list 2) - (forward-char 1) - (if (looking-at "\\(\\(common-lisp\\|cl\\)::?\\)?function\\(\\Sw\\|\\S_\\)") - (+ lisp-body-indent -1 (current-column)) - (+ sexp-column lisp-body-indent))) - (error (+ sexp-column lisp-body-indent))))) - -(defun sly--lisp-indent-loop (path state indent-point sexp-column normal-indent) - (ignore sexp-column) - (if (cdr path) - normal-indent - (let* ((loop-start (elt state 1)) - (type (sly--lisp-indent-loop-type loop-start))) - (cond ((and sly-lisp-loop-indent-subclauses - (memq type '(extended extended/split))) - (list (sly--lisp-indent-loop-macro-1 state indent-point) - (sly--lisp-indent-parse-state-start state))) - (t - (sly--lisp-indent-loop-part-indentation indent-point state type)))))) - -;;;; LOOP indentation, the complex version -- handles subclause indentation - -;; Regexps matching various varieties of loop macro keyword ... -(defvar sly--common-lisp-body-introducing-loop-macro-keyword - "\\(#?:\\)?\\(do\\(ing\\)?\\|finally\\|initially\\)" - "Regexp matching loop macro keywords which introduce body forms.") - -;; Not currently used -(defvar sly--common-lisp-accumlation-loop-macro-keyword - "\\(#?:\\)?\\(collect\\(ing\\)?\\|append\\(ing\\)?\\|nconc\\(ing\\)?\\|\ -count\\(ing\\)?\\|sum\\(ming\\)?\\|maximiz\\(e\\|ing\\)\\|\ -minimiz\\(e\\|ing\\)\\)" - "Regexp matching loop macro keywords which introduce accumulation clauses.") - -;; This is so "and when" and "else when" get handled right -;; (not to mention "else do" !!!) -(defvar sly--common-lisp-prefix-loop-macro-keyword - "\\(#?:\\)?\\(and\\|else\\)" - "Regexp matching loop macro keywords which are prefixes.") - -(defvar sly--common-lisp-indent-clause-joining-loop-macro-keyword - "\\(#?:\\)?and" - "Regexp matching 'and', and anything else there ever comes to be like it.") - -(defvar sly--common-lisp-indent-indented-loop-macro-keyword - "\\(#?:\\)?\\(\\(up\\|down\\)?(from\\|to)\\|below\\|above\\|in\\(to\\)?\\|\ -on\\|=\\|then\\|across\\|being\\|each\\|the\\|of\\|using\\|\ -\\(present-\\|external-\\)?symbols?\\|fixnum\\|float\\|t\\|nil\\|of-type\\)" - "Regexp matching keywords introducing loop subclauses. -Always indented two.") - -(defvar sly--common-lisp-indenting-loop-macro-keyword - "\\(#?:\\)?\\(when\\|unless\\|if\\)" - "Regexp matching keywords introducing conditional clauses. -Cause subsequent clauses to be indented.") - -(defvar sly--lisp-indent-loop-macro-else-keyword "\\(#?:\\)?else") - -;;; Attempt to indent the loop macro ... -(defun sly--lisp-indent-loop-part-indentation (indent-point state type) - "Compute the indentation of loop form constituents." - (let* ((loop-start (nth 1 state)) - (loop-indentation (save-excursion - (goto-char loop-start) - (if (eq type 'extended/split) - (- (current-column) 4) - (current-column)))) - (indent nil) - (re "\\(\\(#?:\\)?\\sw+\\|)\\|\n\\)")) - (goto-char indent-point) - (back-to-indentation) - (cond ((eq type 'simple/split) - (+ loop-indentation sly-lisp-simple-loop-indentation)) - ((eq type 'simple) - (+ loop-indentation 6)) - ;; We are already in a body, with forms in it. - ((and (not (looking-at re)) - (save-excursion - (while (and (ignore-errors (backward-sexp) t) - (not (looking-at re))) - (setq indent (current-column))) - (and indent - (looking-at sly--common-lisp-body-introducing-loop-macro-keyword)))) - (list indent loop-start)) - ;; Keyword-style or comment outside body - ((or sly-lisp-loop-indent-forms-like-keywords - (looking-at re) - (looking-at ";")) - (if (and (looking-at ";") - (let ((p (sly--lisp-indent-trailing-comment))) - (when p - (setq loop-indentation p)))) - (list loop-indentation loop-start) - (list (+ loop-indentation 6) loop-start))) - ;; Form-style - (t - (list (+ loop-indentation 9) loop-start))))) - -(defun sly--lisp-indent-loop-advance-past-keyword-on-line () - (forward-word 1) - (while (and (looking-at "\\s-") (not (eolp))) - (forward-char 1)) - (unless (eolp) - (current-column))) - -(defun sly--lisp-indent-loop-macro-1 (parse-state indent-point) - (catch 'return-indentation - (save-excursion - ;; Find first clause of loop macro, and use it to establish - ;; base column for indentation - (goto-char (sly--lisp-indent-parse-state-start parse-state)) - (let ((loop-start-column (current-column))) - (sly--lisp-indent-loop-advance-past-keyword-on-line) - - (when (eolp) - (forward-line 1) - (end-of-line) - ;; If indenting first line after "(loop <newline>" - ;; cop out ... - (if (<= indent-point (point)) - (throw 'return-indentation - (+ loop-start-column - sly-lisp-loop-clauses-indentation))) - (back-to-indentation)) - - (let* ((case-fold-search t) - (loop-macro-first-clause (point)) - (previous-expression-start - (sly--lisp-indent-parse-state-prev parse-state)) - (default-value (current-column)) - (loop-body-p nil) - (loop-body-indentation nil) - (indented-clause-indentation (+ 2 default-value))) - ;; Determine context of this loop clause, starting with the - ;; expression immediately preceding the line we're trying to indent - (goto-char previous-expression-start) - - ;; Handle a body-introducing-clause which ends a line specially. - (if (looking-at sly--common-lisp-body-introducing-loop-macro-keyword) - (let ((keyword-position (current-column))) - (setq loop-body-p t) - (setq loop-body-indentation - (if (sly--lisp-indent-loop-advance-past-keyword-on-line) - (current-column) - (back-to-indentation) - (if (/= (current-column) keyword-position) - (+ 2 (current-column)) - (+ sly-lisp-loop-body-forms-indentation - (if sly-lisp-loop-indent-body-forms-relative-to-loop-start - loop-start-column - keyword-position)))))) - - (back-to-indentation) - (if (< (point) loop-macro-first-clause) - (goto-char loop-macro-first-clause)) - ;; If there's an "and" or "else," advance over it. - ;; If it is alone on the line, the next "cond" will treat it - ;; as if there were a "when" and indent under it ... - (let ((exit nil)) - (while (and (null exit) - (looking-at sly--common-lisp-prefix-loop-macro-keyword)) - (if (null (sly--lisp-indent-loop-advance-past-keyword-on-line)) - (progn (setq exit t) - (back-to-indentation))))) - - ;; Found start of loop clause preceding the one we're - ;; trying to indent. Glean context ... - (cond - ((looking-at "(") - ;; We're in the middle of a clause body ... - (setq loop-body-p t) - (setq loop-body-indentation (current-column))) - ((looking-at sly--common-lisp-body-introducing-loop-macro-keyword) - (setq loop-body-p t) - ;; Know there's something else on the line (or would - ;; have been caught above) - (sly--lisp-indent-loop-advance-past-keyword-on-line) - (setq loop-body-indentation (current-column))) - (t - (setq loop-body-p nil) - (if (or (looking-at sly--common-lisp-indenting-loop-macro-keyword) - (looking-at sly--common-lisp-prefix-loop-macro-keyword)) - (setq default-value (+ 2 (current-column)))) - (setq indented-clause-indentation (+ 2 (current-column))) - ;; We still need loop-body-indentation for "syntax errors" ... - (goto-char previous-expression-start) - (setq loop-body-indentation (current-column))))) - - ;; Go to first non-blank character of the line we're trying - ;; to indent. (if none, wind up poised on the new-line ...) - (goto-char indent-point) - (back-to-indentation) - (cond - ((looking-at "(") - ;; Clause body ... - loop-body-indentation) - ((or (eolp) (looking-at ";")) - ;; Blank line. If body-p, indent as body, else indent as - ;; vanilla clause. - (if loop-body-p - loop-body-indentation - (or (and (looking-at ";") (sly--lisp-indent-trailing-comment)) - default-value))) - ((looking-at sly--common-lisp-indent-indented-loop-macro-keyword) - indented-clause-indentation) - ((looking-at sly--common-lisp-indent-clause-joining-loop-macro-keyword) - (let ((stolen-indent-column nil)) - (forward-line -1) - (while (and (null stolen-indent-column) - (> (point) loop-macro-first-clause)) - (back-to-indentation) - (if (and (< (current-column) loop-body-indentation) - (looking-at "\\(#?:\\)?\\sw")) - (progn - (if (looking-at sly--lisp-indent-loop-macro-else-keyword) - (sly--lisp-indent-loop-advance-past-keyword-on-line)) - (setq stolen-indent-column (current-column))) - (forward-line -1))) - (or stolen-indent-column default-value))) - (t default-value))))))) - -(defalias 'sly--lisp-indent-if*-advance-past-keyword-on-line - #'sly--lisp-indent-loop-advance-past-keyword-on-line) - -;;;; IF* is not standard, but a plague upon the land -;;;; ...let's at least try to indent it. - -(defvar sly--lisp-indent-if*-keyword - "thenret\\|elseif\\|then\\|else" - "Regexp matching if* keywords") - -(defun sly--lisp-indent-if* - (path parse-state indent-point sexp-column normal-indent) - (ignore normal-indent path sexp-column) - (list (sly--lisp-indent-if*-1 parse-state indent-point) - (sly--lisp-indent-parse-state-start parse-state))) - -(defun sly--lisp-indent-if*-1 (parse-state indent-point) - (catch 'return-indentation - (save-excursion - ;; Find first clause of if* macro, and use it to establish - ;; base column for indentation - (goto-char (sly--lisp-indent-parse-state-start parse-state)) - (let ((if*-start-column (current-column))) - (sly--lisp-indent-if*-advance-past-keyword-on-line) - (let* ((case-fold-search t) - (if*-first-clause (point)) - (previous-expression-start - (sly--lisp-indent-parse-state-prev parse-state)) - (default-value (current-column)) - (if*-body-p nil) - (if*-body-indentation nil)) - ;; Determine context of this if* clause, starting with the - ;; expression immediately preceding the line we're trying to indent - (goto-char previous-expression-start) - ;; Handle a body-introducing-clause which ends a line specially. - (back-to-indentation) - (if (< (point) if*-first-clause) - (goto-char if*-first-clause)) - ;; Found start of if* clause preceding the one we're trying - ;; to indent. Glean context ... - (cond - ((looking-at sly--lisp-indent-if*-keyword) - (setq if*-body-p t) - ;; Know there's something else on the line (or would - ;; have been caught above) - (sly--lisp-indent-if*-advance-past-keyword-on-line) - (setq if*-body-indentation (current-column))) - ((looking-at "#'\\|'\\|(") - ;; We're in the middle of a clause body ... - (setq if*-body-p t) - (setq if*-body-indentation (current-column))) - (t - (setq if*-body-p nil) - ;; We still need if*-body-indentation for "syntax errors" ... - (goto-char previous-expression-start) - (setq if*-body-indentation (current-column)))) - - ;; Go to first non-blank character of the line we're trying - ;; to indent. (if none, wind up poised on the new-line ...) - (goto-char indent-point) - (back-to-indentation) - (cond - ((or (eolp) (looking-at ";")) - ;; Blank line. If body-p, indent as body, else indent as - ;; vanilla clause. - (if if*-body-p - if*-body-indentation - default-value)) - ((not (looking-at sly--lisp-indent-if*-keyword)) - ;; Clause body ... - if*-body-indentation) - (t (- (+ 7 if*-start-column) - (- (match-end 0) (match-beginning 0)))))))))) - - -;;;; Indentation specs for standard symbols, and a few semistandard ones. -(defun sly--lisp-indent-init-standard-indentation () - (let ((l '((block 1) - (case (4 &rest (&whole 2 &rest 1))) - (ccase (as case)) - (ecase (as case)) - (typecase (as case)) - (etypecase (as case)) - (ctypecase (as case)) - (catch 1) - (cond (&rest (&whole 2 &rest nil))) - ;; for DEFSTRUCT - (:constructor (4 &lambda)) - (defvar (4 2 2)) - (defclass (6 (&whole 4 &rest 1) - (&whole 2 &rest 1) - (&whole 2 &rest 1))) - (defconstant (as defvar)) - (defcustom (4 2 2 2)) - (defparameter (as defvar)) - (defconst (as defcustom)) - (define-condition (as defclass)) - (define-modify-macro (4 &lambda &body)) - (defsetf sly--lisp-indent-defsetf) - (defun (4 &lambda &body)) - (defgeneric (4 &lambda &body)) - (define-setf-method (as defun)) - (define-setf-expander (as defun)) - (defmacro (as defun)) - (defsubst (as defun)) - (deftype (as defun)) - (defmethod sly--lisp-indent-defmethod) - (defpackage (4 2)) - (defstruct ((&whole 4 &rest (&whole 2 &rest 1)) - &rest (&whole 2 &rest 1))) - (destructuring-bind (&lambda 4 &body)) - (do sly--lisp-indent-do) - (do* (as do)) - (dolist ((&whole 4 2 1) &body)) - (dotimes (as dolist)) - (eval-when 1) - (flet ((&whole 4 &rest (&whole 1 4 &lambda &body)) &body)) - (labels (as flet)) - (macrolet (as flet)) - (generic-flet (as flet)) - (generic-labels (as flet)) - (handler-case (4 &rest (&whole 2 &lambda &body))) - (restart-case (as handler-case)) - ;; single-else style (then and else equally indented) - (if (&rest nil)) - (if* sly--lisp-indent-if*) - (lambda (&lambda &rest sly--lisp-indent-function-lambda-hack)) - (let ((&whole 4 &rest (&whole 1 1 2)) &body)) - (let* (as let)) - (compiler-let (as let)) - (handler-bind (as let)) - (restart-bind (as let)) - (locally 1) - (loop sly--lisp-indent-loop) - (:method sly--lisp-indent-defmethod) ; in `defgeneric' - (multiple-value-bind ((&whole 6 &rest 1) 4 &body)) - (multiple-value-call (4 &body)) - (multiple-value-prog1 1) - (multiple-value-setq (4 2)) - (multiple-value-setf (as multiple-value-setq)) - (named-lambda (4 &lambda &rest sly--lisp-indent-function-lambda-hack)) - (pprint-logical-block (4 2)) - (print-unreadable-object ((&whole 4 1 &rest 1) &body)) - ;; Combines the worst features of BLOCK, LET and TAGBODY - (prog (&lambda &rest sly--lisp-indent-tagbody)) - (prog* (as prog)) - (prog1 1) - (prog2 2) - (progn 0) - (progv (4 4 &body)) - (return 0) - (return-from (nil &body)) - (symbol-macrolet (as let)) - (tagbody sly--lisp-indent-tagbody) - (throw 1) - (unless 1) - (unwind-protect (5 &body)) - (when 1) - (with-slots (as multiple-value-bind)) - (with-accessors (as multiple-value-bind)) - (with-condition-restarts (as multiple-value-bind)) - (with-compilation-unit ((&whole 4 &rest 1) &body)) - (with-output-to-string (4 2)) - (with-standard-io-syntax (2))))) - (dolist (el l) - (let* ((name (car el)) - (indentation (cadr el))) - (put name 'sly-common-lisp-indent-function indentation))))) - -(sly--lisp-indent-init-standard-indentation) - -(provide 'sly-cl-indent) - -;;; sly-cl-indent.el ends here diff --git a/elpa/sly-1.0.43/lib/sly-completion.el b/elpa/sly-1.0.43/lib/sly-completion.el @@ -1,764 +0,0 @@ -;;; sly-completion.el --- completion tricks and helpers -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 JoĂŁo Távora - -;; Author: JoĂŁo Távora -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; - -;;; Code: -;;; -(require 'cl-lib) -(require 'comint) -(require 'sly-messages "lib/sly-messages") - - -;;; Something to move to minibuffer.el, maybe - -;;; Backend completion - -;; This "completion style" delegates all the work to the completion -;; table which is then free to implement its own completion style. -;; Typically this is