*vlime-tutor.txt* A tutorial for Vlime *vlime-tutor* * [soure of text](https://github.com/vlime/vlime/blob/master/vim/doc/vlime-tutor.txt) ====================================================================== 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 /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: [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 ||. 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 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 . 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 . Consider the expression we just wrote: (cons 1 2) Again, make sure the cursor is on or inside the parentheses, then press , 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 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 in interaction mode: (cons 1 2) (+ 1 (- 10 2)) (symbol-value (find-symbol "*PACKAGE*" "COMMON-LISP")) ; the new line The result should look like -- # 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 -- # 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 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 -- # 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 or 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 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 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 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 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 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 (#. The debugger will then disappear, leaving a printed condition in the REPL buffer to indicate the evaluation was aborted: -- # 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 (#) 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) #) 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: -- # 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: # =================================== Class: # -------------------- 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: # =================================== Class: # -------------------- 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 or . An "X" will appear to indicate that this slot is selected. Do the same with PARAM-B. The inspector should now look like this: # =================================== Class: # -------------------- 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 or . 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 . 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 in normal mode after you have done editing the expression. The inspector should be automatically updated and look like this: # =================================== Class: # -------------------- 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|.