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!
This section is very empty, in the meantime try to be sensible and emulate or improve on SLY's existing style.
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
(oradd-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.
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!
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. Usesslynk-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.
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:
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
,
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".
(: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.
(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.
(: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 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.
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 can one day be more easily implemented.
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.
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 or have non-SLIME clients connect to it such as Robert Brown's swank-client.
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))
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.
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.
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.
TODO
TODO
- Read how to properly contribute to open source projects on Github.
- 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 that relates to only one subject with a clear title and description in grammatically correct, complete sentences.