Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

732 lines (534 sloc) 26.3 KB
*vlime-tutor.txt* A tutorial for Vlime *vlime-tutor*
======================================================================
CONTENTS *vlime-tutor-contents*
1. Introduction .............................. |vlime-tutor-intro|
2. Starting the Server ................ |vlime-tutor-start-server|
3. Connecting .............................. |vlime-tutor-connect|
4. The REPL ................................... |vlime-tutor-repl|
5. Coding ..................................... |vlime-tutor-code|
6. Compiling ............................... |vlime-tutor-compile|
7. Debugging ................................. |vlime-tutor-debug|
8. The inspector ......................... |vlime-tutor-inspector|
9. Conclusion ........................... |vlime-tutor-conclusion|
======================================================================
1. Introduction *vlime-tutor-intro*
This is a guided tour to show the major features of Vlime. For a
complete list of the features and key mappings etc., see |vlime.txt|.
This tutorial was written on a Linux machine, so there may be some
Linux-specific commands. Vlime also works fine on Windows, and maybe
MacOS, but these systems will not be covered here.
Before we get started, please make sure your system has all these
prerequisites:
* A decent Common Lisp implementation. SBCL is strongly
recommended, but other implementations that support the debugger
may also be fine. See |vlime-intro| for a list of supported
implementations.
* The Paredit plugin for Vim (https://github.com/kovisoft/paredit).
* Vlime being properly installed. See README.md in the Vlime
source repo for installation instructions.
We will be building a Fibonacci sequence generator in the rest of this
tutorial. Fascinating, right?
======================================================================
2. Starting the Server *vlime-tutor-start-server*
Vlime works in a client-server fashion. The server is written in
Common Lisp. You can let Vlime start the server for you (see
|vlime-start-up|), but let's do it manually for the first time. To
start the server on the local machine:
sbcl --load <vlime repo>/lisp/start-vlime.lisp
To run the server on a remote machine, see |vlime-remote-server|.
The server is fully functional when we see something like this in the
console:
<INFO> [10:08:07] vlime-usocket - Server created: ....
It will be listening on port 7002 by default.
======================================================================
3. Connecting *vlime-tutor-connect*
Now we can create a new *.lisp file in Vim:
:tabedit ad-hoc.lisp
Then type "\cc" (without the quote marks) in normal mode to create a
connection. The backslash "\" is the default mapping for
|<LocalLeader>|. See |vlime-mappings|.
Vlime will show a message in Vim if everything went well:
Vlime Connection 1 established.
There can be multiple Vlime connections in a single Vim process. You
can use "\cc" to make more connections to the same server (though that
usually doesn't make any sense), or call vlime#plugin#ConnectREPL() in
Vim to connect to another server. For example:
:call vlime#plugin#ConnectREPL("127.0.0.1", 9999)
This will try to make a connection to 127.0.0.1:9999.
Now, just for demostrating, let's type "\cc" a few more times, to
create more connections. Then type "\cs" (without the quote marks) in
the ad-hoc.lisp buffer to see the connection list. A list with three
connections should look like this:
Which connection to use?
1. Vlime Connection 1 (127.0.0.1:7002)
2. Vlime Connection 2 (127.0.0.1:7002)
3. Vlime Connection 3 (127.0.0.1:7002)
Type number and <Enter> or click with mouse (empty cancels):
From there you can also choose a connection for the current buffer to
use by typing it's ID and then <Enter>. See
|vlime-current-connection|.
When you're done with a connection, type "\cd" (without the quote
marks) in a *.lisp buffer to close it. The connection list may pop up
if Vlime was not sure which connection to close.
You may try out the "\cc", "\cs", and "\cd" commands now, until you
feel comfortable to move on. Please leave at least one connection open
before proceeding to the next section.
======================================================================
4. The REPL *vlime-tutor-repl*
Now that we have an active connection, let's try something simple.
Vlime has good REPL integration, but it's different from other Lisp
environments, in that Vlime's REPL buffer is dedicated for output,
i.e. you can not type and evaluate an expression directly in the REPL
buffer. To evaluate something in the REPL, you send it using key
mappings listed in |vlime-mappings-send|.
Let's go to ad-hoc.lisp, the file we created in |vlime-tutor-connect|,
and write a simple expression in it:
(cons 1 2)
Note: You may notice some "strange" behaviors when typing the
expression, such as parentheses being paired up automatically.
They are in fact features to aid you in code editing. We will
get to them in |vlime-tutor-code|.
Make sure the cursor is on or inside the parentheses, then type "\ss"
(without the quote marks) in normal mode. A new buffer will pop up at
the bottom, showing some info about the server and the evaluation
result:
SWANK version 2016-04-19, pid 14403
===================================
--
(1 . 2)
This is the REPL buffer.
Try typing "i" (without the quote marks) in the REPL buffer. It will
result in an error message:
E21: Cannot make changes, 'modifiable' is off
Indeed, it's read-only.
To make sending things to the REPL easier, Vlime has an interaction
mode for *.lisp buffers. Go back to the ad-hoc.lisp buffer at the top,
then type "\i" (without the quote marks) to activate interaction mode.
To send something to the REPL in interaction mode, simply press <CR>.
Consider the expression we just wrote:
(cons 1 2)
Again, make sure the cursor is on or inside the parentheses, then
press <CR>, the new result will also come out in the REPL buffer:
SWANK version 2016-04-19, pid 14403
===================================
--
(1 . 2)
--
(1 . 2)
Be careful with nested expressions though. Vlime will only match the
nearest parentheses that enclose the cursor. Let's add one more line
to ad-hoc.lisp:
(cons 1 2)
(+ 1 (- 10 2)) ; the new line
When <CR> is pressed, if the cursor was on the "+" operator, the whole
expression "(+ 1 (- 10 2))" will be sent, and the result will be 9. If
the cursor was on the "-" operator, only the nested expression
"(- 10 2)" will be sent, and the result will be 8.
To disable interaction mode, type "\i" (without the quote marks) again
in the ad-hoc.lisp buffer.
But in what package were those expressions evaluated? Let's have a
look. Add one more line to ad-hoc.lisp and evaluate it by typing "\ss"
(without the quote marks) in normal mode or <CR> in interaction mode:
(cons 1 2)
(+ 1 (- 10 2))
(symbol-value (find-symbol "*PACKAGE*" "COMMON-LISP")) ; the new line
The result should look like
--
#<PACKAGE "COMMON-LISP-USER">
When evaluating an expression, Vlime would use the package associated
with the buffer containing that expression (see
|vlime-current-package|). And the default package is COMMON-LISP-USER.
In the examples above, we never explicitly set the package for
ad-hoc.lisp, so it used the default package, and all the expressions
are evaluated in the default package too.
There are two methods to set the package for a buffer:
1. Write an "in-package" expression in the buffer. Change the
content of ad-hoc.list to
(cons 1 2)
(+ 1 (- 10 2))
(in-package :vlime) ; the new line
(symbol-value (find-symbol "*PACKAGE*" "COMMON-LISP"))
And then evaluate the last line again, the result would be
--
#<PACKAGE "VLIME">
2. Type "\p" (without the quote marks) in a buffer in normal mode.
Vim will then prompt for the new package name, with the current
package displayed as the default. You can edit the package name
like you edit any other text in a normal buffer, and then press
<CR> in normal mode to submit (see |vlime-input-buffer|). The
name to enter can be a nickname, and is case-insensitive. This
method will take precedence over method 1.
Now type "\p" in ad-hoc.lisp, and set the package to "cl-user"
(without the quote marks). Evaluate the last line again, the
result would be
--
#<PACKAGE "COMMON-LISP-USER">
You may try out the REPL and the interaction mode now, until you feel
comfortable to move on. Try writing some complex expressions in
ad-hoc.lisp, such as multi-line DEFUNs, and evaluate them using
interaction mode.
======================================================================
5. Coding *vlime-tutor-code*
Finally, we are going to write some real code. Our goal is to build a
Fibonacci sequence generator. Let's create a new file in Vim:
:tabedit fibonacci.lisp
And then type the content in it:
(in-package #:cl-user)
(defpackage #:fibonacci
(:use #:cl)
(:export #:generate))
(in-package #:fibonacci)
(defun generate (n &optional (a 0) (b 1) (acc (list)))
(if (<= n 0)
(reverse acc)
(generate (1- n) b (+ a b) (push acc a))))
The GENERATE function should build a list of N Fibonacci numbers. If
you typed the code manually instead of copy-and-paste, You may have
noticed a few things when typing:
1. The parentheses are automatically paired. This is done by the
Paredit plugin. It's an invaluable tool for editing Lisp code.
See |paredit.txt| for details and more advanced usage.
2. When pressing <Space> or <CR> inside a pair of parentheses in
insert mode, a small preview window may pop up at the top,
showing the argument list for the current expression.
3. The code is automatically indented. See |vlime-auto-indent|.
4. You can press <Tab> or the old-school CTRL-x CTRL-o keys in
insert mode to activate the omni-completion menu, and then
use CTRL-n or CTRL-p to select a candidate. See
|vlime-completions|.
Note: If you have other completion plugins installed, the <Tab> key
may be mapped to the other plugins, and may not work as
expected. You can fall back to CTRL-x CTRL-o, or remap the
<Tab> key as described in |vlime-mappings-remap|.
These are the most frequently used features when editing the source
code.
Vlime also has support for showing document strings and cross
references. Move the cursor inside the word "reverse", and type "\dda"
(without the quote marks), a preview buffer will then show a brief
description of the REVERSE function:
Documentation for the symbol REVERSE:
Function:
Arglist: (SEQUENCE)
Return a new sequence containing the same elements but in reverse order.
This information varies between Common Lisp implementations, but is
generally helpful. See |vlime-mappings-describe| for more operations
on describing symbols.
Now keep the cursor inside the word "reverse", and type "\xc" (without
the quote marks). The cross reference (a.k.a. xref) buffer will pop up
with some content like this:
SB-WALKER::LET*-BINDINGS
SB-SYS:CLOSE-SHARED-OBJECTS
SB-PCL::MAKE-PRELIMINARY-LAYOUT
SB-PCL::STD-COMPUTE-SLOTS
SB-PCL::STANDARD-COMPUTE-EFFECTIVE-METHOD
SB-PCL::COMPUTE-STD-CPL-PHASE-3
SB-PCL::%UPDATE-LISP-CLASS-LAYOUT
....
Note: Depending on how your Common Lisp implementation was built, you
may get the "No xref found" error message instead. Don't worry.
You can try other xrefs listed in |vlime-mappings-invoke-xref|,
or skip this operation and proceed to the next section.
These are the locations where the REVERSE function is called. Press
<CR> on one of them and Vlime will take you directly to the referenced
location, provided the source code is readable. Use |CTRL-O| to go
back to previous cursor locations. There are other kinds of cross
references, see |vlime-mappings-invoke-xref|.
Before proceeding to the next section, you can close all Vlime windows
in the current tab page by typing "\wA" (without the quote marks) in
fibonacci.lisp in normal mode. See |vlime-mappings-close-window| for
all window-manipulating commands.
======================================================================
6. Compiling *vlime-tutor-compile*
Make sure you have saved fibonacci.lisp, and then type "\of" (without
the quote marks) in it's buffer in normal mode. Go back to the REPL
buffer, you'll see the compilation result:
--
; compiling file "/home/user/fibonacci.lisp" (written 28 FEB 2017 11:16:53 AM):
; /home/user/fibonacci.fasl written
; compilation finished in 0:00:00.093
Note: If you have closed the window containing the REPL buffer, it
should now pop up again.
This result means our code compiled cleanly, with no errors or
warnings etc. To generate a warning message, let's change the
expression calling the GENERATE function from
(generate (1- n) b (+ a b) (push acc a))
To
(some-other-func (1- n) b (+ a b) (push acc a))
SOME-OTHER-FUNC is not defined, of course. Save the file, and Compile
it again by typing "\of" (without the quote marks). A buffer will pop
up with a warning message:
STYLE-WARNING: undefined function: SOME-OTHER-FUNC
Pressing <CR> on the message will take you to the exact location where
the undefined function is called.
Let's change the function call back to GENERATE, then save and compile
fibonacci.lisp again. The compiler messages (a.k.a. notes) buffer
should say:
No message from the compiler.
We will see whether the GENERATE function can work as expected in
|vlime-tutor-debug|. See |vlime-mappings-compile| for other compiling
operations.
You can close the notes window before proceeding to the next section.
======================================================================
7. Debugging *vlime-tutor-debug*
The first step of debugging in Common Lisp is usually trying out the
new function in the REPL. We'll do the same here.
Go back to ad-hoc.lisp, write an expression to call
FIBONACCI:GENERATE:
(fibonacci:generate 10)
And send it to the REPL, via either "\ss" or interaction mode.
A new buffer came out instead of the evaluation result. The buffer
content looks quite scary:
Thread: 1; Level: 1
The value
34
is not of type
LIST
[Condition of type TYPE-ERROR]
Restarts:
0. RETRY - Retry SLIME REPL evaluation request.
1. *ABORT - Return to SLIME's top level.
2. ABORT - abort thread (#<THREAD "repl-thread" RUNNING ...
Frames:
0. (SB-IMPL::LIST-REVERSE 34)
1. (SB-INT:SIMPLE-EVAL-IN-LEXENV (FIBONACCI:GENERATE 10) ...
2. (EVAL (FIBONACCI:GENERATE 10))
3. (SWANK::EVAL-REGION "(fibonacci:generate 10)")
4. ((LAMBDA NIL :IN SWANK-REPL::REPL-EVAL))
5. (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN ...
6. (SWANK::CALL-WITH-RETRY-RESTART "Retry SLIME REPL ...
7. (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA ...
8. (SWANK-REPL::REPL-EVAL "(fibonacci:generate 10)")
9. (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL ...
....
This is the debugger. It pops up whenever an unhandled condition
occurs. The content can be divided into four parts, separated by an
empty line:
* The thread and netsted level this condition occured in.
* The description of this condition.
* Some available restarts.
* The stack frames.
Our GENERATE function is recursive, but we see no GENERATE function
call in the stack frames. That's because the compiler did it's job and
optimized our code a bit, so the debugger has fewer info available.
We can't determine what went wrong yet, so let's abort the evaluation
for now. Select the restart labeled "ABORT - Return to SLIME's top
level" by moving the cursor to that line and press <CR>. The debugger
will then disappear, leaving a printed condition in the REPL buffer to
indicate the evaluation was aborted:
--
#<TYPE-ERROR expected-type: "LIST"datum: 34>
There's an option in Vlime to tell the compiler our preference. Let's
use it now to preserve all available debug info:
:let g:vlime_compiler_policy = {"DEBUG": 3}
See |g:vlime_compiler_policy| for a detailed description of this
variable.
With the compiler policy set, we compile fibonacci.lisp and call
FIBONACCI:GENERATE in the REPL again. Of course the debugger will
still pop up, but this time it has more detailed stack info:
Thread: 1; Level: 1
The value
34
is not of type
LIST
[Condition of type TYPE-ERROR]
Restarts:
0. RETRY - Retry SLIME REPL evaluation request.
1. *ABORT - Return to SLIME's top level.
2. ABORT - abort thread (#<THREAD "repl-thread" RUNNING {1003907F93}>)
Frames:
0. (SB-IMPL::LIST-REVERSE 34)
1. (FIBONACCI:GENERATE 0 55 89 (((# . 13) . 21) . 34))
2. (FIBONACCI:GENERATE 1 (((# . 13) . 21) . 34) 55 (((# . 8) . 13) . 21))
3. (FIBONACCI:GENERATE 2 (((# . 8) . 13) . 21) 34 (((# . 5) . 8) . 13))
4. (FIBONACCI:GENERATE 3 (((# . 5) . 8) . 13) 21 (((# . 3) . 5) . 8))
5. (FIBONACCI:GENERATE 4 (((# . 3) . 5) . 8) 13 (((# . 2) . 3) . 5))
6. (FIBONACCI:GENERATE 5 (((# . 2) . 3) . 5) 8 (((# . 1) . 2) . 3))
7. (FIBONACCI:GENERATE 6 (((# . 1) . 2) . 3) 5 (((# . 1) . 1) . 2))
8. (FIBONACCI:GENERATE 7 (((# . 1) . 1) . 2) 3 (((NIL . 0) . 1) . 1))
9. (FIBONACCI:GENERATE 8 (((NIL . 0) . 1) . 1) 2 ((NIL . 0) . 1))
10. (FIBONACCI:GENERATE 9 ((NIL . 0) . 1) 1 (NIL . 0))
11. (FIBONACCI:GENERATE 10 (NIL . 0) 1 NIL)
12. (SB-INT:SIMPLE-EVAL-IN-LEXENV (FIBONACCI:GENERATE 10) #<NULL-LEXENV>)
13. (EVAL (FIBONACCI:GENERATE 10))
....
This stack deserves careful examination. We can see each call of the
FIBONACCI:GENERATE function and the values of it's arguments. To see
more details, move the cursor to frame 2 and press "d" (without the
quote marks). A preview buffer will show all local variable names and
their values, plus other useful info:
Frame: 2 (Restartable)
Locals:
A: ((((# . 8) . 13) . 21) . 34)
ACC: ((((# . 5) . 8) . 13) . 21)
B: 55
N: 1
Location:
File: /home/user/fibonacci.lisp
Position: 239
Snippet:
(generate (1- n) b (+ a b) (push acc a))))
....
The variables A and B should be adjacent Fibonacci numbers, but A is
in fact a strange cons struct.
To make it more challenging, I'll let you use what you learnt to
figure out what's wrong with our program. Don't worry if you can't
make it right. There is a corrected version of fibonacci.lisp in the
next section. See |vlime-mappings-debugger| for all available debugger
operations.
======================================================================
8. The Inspector *vlime-tutor-inspector*
Now that we have fixed fibonacci.lisp, it should look like this:
(in-package #:cl-user)
(defpackage #:fibonacci
(:use #:cl)
(:export #:generate))
(in-package #:fibonacci)
(defun generate (n &optional (a 0) (b 1) (acc (list)))
(if (<= n 0)
(reverse acc)
(generate (1- n) b (+ a b) (push a acc))))
It works well, but It's a boring function after all. We should
probably add some fancy classes and methods:
(in-package #:cl-user)
(defpackage #:fibonacci
(:use #:cl)
(:export #:generate
#:generator
#:next))
(in-package #:fibonacci)
(defun generate (n &optional (a 0) (b 1) (acc (list)))
(if (<= n 0)
(values (reverse acc) a b)
(generate (1- n) b (+ a b) (push a acc))))
(defclass generator ()
((param-a :accessor generator-param-a :initform 0)
(param-b :accessor generator-param-b :initform 1)))
(defgeneric next (obj)
(:method ((obj generator))
(with-slots (param-a param-b) obj
(multiple-value-bind (result a b) (generate 1 param-a param-b)
(setf param-a a
param-b b)
(car result)))))
We use a modified GENERATE function as a backend to build a GENERATOR
class. Now let's test it by first creating an instance. Save
fibonacci.lisp, and compile it by typing "\of" (without the quote
marks), then go back to ad-hoc.lisp, write down this MAKE-INSTANCE
expression and send it to the REPL:
(make-instance 'fibonacci:generator)
The REPL buffer will show the result:
--
#<FIBONACCI:GENERATOR {1003D83903}>
We can call the accessors or SLOT-VALUE to see whether the slots are
initialized correctly, but there is an easier way. We should bring up
the inspector.
Go to the REPL buffer, and move the cursor to the printed
representation of our newly created instance, then type "\I" (without
the quote marks). The inspector buffer should appear. The info
displayed in the inspector will look like this:
#<FIBONACCI:GENERATOR {1003D83903}>
===================================
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR>
--------------------
Group slots by inheritance [ ]
Sort slots alphabetically [X]
All Slots:
[ ] PARAM-A = 0
[ ] PARAM-B = 1
[set value] [make unbound]
We can see that PARAM-A is 0 and PARAM-B is 1. They are initialized
correctly.
Now we are going to test the FIBONACCI:NEXT method. We want to call
the method on the instance we just created, and see if the slot values
are changed correctly.
We didn't save a reference to the generator instance, but that's not
a problem. The REPL buffer remembers all evaluation results it has
seen. We can just yank the result from the REPL buffer.
Keep the inspector open, go back to the REPL buffer, and make sure the
cursor is still on the printed representation of our
FIBONACCI:GENERATOR instance. Type "\y" (without the quote marks) to
yank the object into the register |quotequote|.
Then add a new line in ad-hoc.lisp:
(fibonacci:next )
Place the cursor on the space character just before the right
parentheses, and type "p" (without the quote marks). The new line
should now look like this:
(fibonacci:next (swank:lookup-presented-object 10))
You may have a number other than "10", and that's OK. The
SWANK:LOOKUP-PRESENTED-OBJECT expression returns the
FIBONACCI:GENERATOR instance saved by the REPL.
Evaluate the whole FIBONACCI:NEXT expression a few times, and we'll
get a series of Fibonacci numbers in the REPL buffer.
Now go to the inspector and press "R" (without the quote marks) to
refresh it. We can see that PARAM-A and PARAM-B are successfully
changed:
#<FIBONACCI:GENERATOR {100257FFE3}>
===================================
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR>
--------------------
Group slots by inheritance [ ]
Sort slots alphabetically [X]
All Slots:
[ ] PARAM-A = 377
[ ] PARAM-B = 610
[set value] [make unbound]
The inspector is also capable of setting slot values. The square
brackets in the inspector buffer denote buttons, we can use these
buttons to manipulate PARAM-A and PARAM-B.
Say we want to fast forward the generator to the 50th Fibonacci
number. Move the cursor inside the brackets just before PARAM-A, and
press <CR> or <Space>. An "X" will appear to indicate that this slot
is selected. Do the same with PARAM-B. The inspector should now look
like this:
#<FIBONACCI:GENERATOR {100257FFE3}>
===================================
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR>
--------------------
Group slots by inheritance [ ]
Sort slots alphabetically [X]
All Slots:
[X] PARAM-A = 377
[X] PARAM-B = 610
[set value] [make unbound]
Now move the cursor to "[set value]" and press <CR> or <Space>. A
buffer will pop up at the bottom prompting for the value of slot
PARAM-A. Let's write an expression to calculate the 50th Fibonacci
number:
; Set slot FIBONACCI::PARAM-A to (evaluated) :
(car (last (fibonacci:generate 50)))
Then go back to normal mode and press <CR>. Another buffer will
appear, prompting for the value of PARAM-B. We give it the 51st
number:
; Set slot FIBONACCI::PARAM-B to (evaluated) :
(car (last (fibonacci:generate 51)))
Remember to press <CR> in normal mode after you have done editing the
expression. The inspector should be automatically updated and look
like this:
#<FIBONACCI:GENERATOR {100257FFE3}>
===================================
Class: #<STANDARD-CLASS FIBONACCI:GENERATOR>
--------------------
Group slots by inheritance [ ]
Sort slots alphabetically [X]
All Slots:
[X] PARAM-A = 7778742049
[X] PARAM-B = 12586269025
[set value] [make unbound]
We can call FIBONACCI:NEXT again to confirm that the internal state of
our generator instance is indeed changed:
(fibonacci:next (swank:lookup-presented-object 10))
And the REPL buffer shows:
--
7778742049
The inspector can also look inside lists, vectors, hashtables, and
packages. You can try to create some of these data structures and peek
inside. See |vlime-mappings-inspector| for all available operations in
the inspector.
You can simply close the inspector window when you've done playing
with it.
======================================================================
9. Conclusion *vlime-tutor-conclusion*
Our Finbonacci sequence generator is working, and we have covered the
most frequently used features of Vlime. You should now be able to use
Vlime to write and debug Common Lisp programs. You can then read the
full documentation in |vlime.txt|.
You can’t perform that action at this time.