emacs.d/clones/lispcookbook.github.io/cl-cookbook/vscode-alive.html
2022-08-04 11:37:48 +02:00

671 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Using VSCode with Alive</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Using VSCode with Alive</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Using VSCode with Alive</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
<p class="announce">
📹 <a href="https://www.udemy.com/course/common-lisp-programming/?couponCode=6926D599AA-LISP4ALL">NEW! Learn Lisp in videos and support our contributors with this 40% discount.</a>
</p>
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>The
<a href="https://marketplace.visualstudio.com/items?itemName=rheller.alive">Alive</a>
extension makes <a href="https://code.visualstudio.com">VSCode</a> a powerful
Common Lisp development platform. Alive hooks directly into the Swank
server that Emacs Slime uses and is fully compatible with VSCodes
ability to develop remotely in containers, WSL, Remote machines, etc.
It has no dependencies beyond a version of Common Lisp running on the
target platform that can run the Swank server. It currently supports:</p>
<ul>
<li>Syntax highlighting</li>
<li>Code completion</li>
<li>Code formatter</li>
<li>Jump to definition</li>
<li>Snippets</li>
<li>REPL integration</li>
<li>Interactive Debugger</li>
<li>REPL history</li>
<li>Inline evaluation</li>
<li>Macro expand</li>
<li>Disassemble</li>
<li>Inspector</li>
<li>Hover Text</li>
<li>Rename function args and let bindings</li>
<li>Code folding</li>
</ul>
<p><img src="assets/commonlisp-vscode-alive.png" alt="" /></p>
<h3 id="prerequisites">Prerequisites</h3>
<p>The Alive extension in VSCode is compatible with ANSI Common Lisp,
and these instructions should work for any of them as long as the Alive
REPL starts up successfully. The examples all use SBCL.</p>
<ul>
<li><a href="https://code.visualstudio.com">VsCode</a> with <a href="https://code.visualstudio.com/docs/setup/mac#_launching-from-the-command-line">command
line</a>
installed running the
<a href="https://marketplace.visualstudio.com/items?itemName=rheller.alive">Alive</a>
extension.</li>
<li><a href="http://www.sbcl.org">SBCL</a></li>
</ul>
<h4 id="connect-vscode-to-a-repl">Connect VSCode to a REPL</h4>
<p><img src="assets/vscode-gifs/attach-repl.gif" alt="" /></p>
<ol>
<li>Inside of VSCode, open a lisp file that you want to edit.
<ul>
<li>If you dont have one, create a new one called <code>hello.lisp</code></li>
</ul>
</li>
<li>Inside of VSCode, open the Command Palette on the menu at the top
where it says <code>View/Command Palette</code> and start an instance of SBCL
running a Swank server attached to your VSCode REPL by choosing:
<code>Alive: Start REPL And Attach</code>.
<ul>
<li>You will see a small window pop up that says <code>REPL Connected</code></li>
<li>If you dont get a <code>REPL Connected</code> message, open up VSCodes
Output on the menu at the top where it says <code>View:Output</code> and
choose <code>Swank Trace</code> from the pulldown. This output is the output
from the running lisp image and will get you started on figuring
out what might be going wrong.</li>
</ul>
</li>
</ol>
<p>Congrats, You now have a VSCode instance running a REPL attached to a
Swank server running on port 4005 of a running SBCL image. You can
now evaluate statements in your file and they will be processed in
your running SBCL instance.</p>
<p><em>To disconnect your REPL and shut down your SBCL instance, open the
Command Palette on the menu at the top where it says <code>View/Command
Palette</code> and choose: <code>Alive: Detach from REPL</code></em></p>
<p>There are keybindings for every operation, feel free to explore and
modify those as needed.</p>
<h3 id="recipes">Recipes</h3>
<p><em>All recipes assume you have a file open in VSCode running with an attached
REPL unless otherwise stated.</em></p>
<p><em>When evaluating an expression, you choose the expression to evaluate by
putting your cursor anywhere in or immediately following the
s-expression that you wish to evaluate.</em></p>
<h4 id="evaluate-a-statement-in-line">Evaluate a statement in-line</h4>
<p><img src="assets/vscode-gifs/in-line-eval.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(+ 2 2)
</code></pre>
<ol>
<li>Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code></li>
<li>
<p>You will see a small pop up that says <code>=&gt; 4 (3 bits, #x4, #o4,
#b100)</code>, which is the result</p>
<p><em>Evaluating a statement in-line is exactly the same as sending it to
the REPL. The only difference is how it is displayed.</em></p>
</li>
</ol>
<h4 id="evaluate-a-statement">Evaluate a statement</h4>
<p><img src="assets/vscode-gifs/eval.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(+ 2 2)
</code></pre>
<ol>
<li>Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Send To REPL</code></li>
<li>You will see the expression show up in the REPL along with the result.</li>
</ol>
<pre><code class="language-lisp">CL-USER&gt;
(+ 2 2)
4
CL-USER&gt;
</code></pre>
<h4 id="compile-a-file">Compile a file</h4>
<p><img src="assets/vscode-gifs/compile.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(+ 2 2)
</code></pre>
<ol>
<li>Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Compile</code></li>
<li>You will see details about the compile in your repl, and a fasl file
in your filesystem.</li>
</ol>
<pre><code class="language-lisp">CL-USER&gt;
; compiling file "/Users/jason/Desktop/hello.lisp" (written 14 SEP 2021 04:24:37 AM):
; wrote /Users/jason/Desktop/hello.fasl
; compilation finished in 0:00:00.001
</code></pre>
<h4 id="use-the-interactive-debugger-to-abort">Use the Interactive Debugger to abort</h4>
<p><img src="assets/vscode-gifs/debug-abort.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(defun divide (x y)
(/ x y))
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code> to load your <code>define</code>
function into your image.</li>
<li>In your open file, add another new line and enter:</li>
</ol>
<pre><code class="language-lisp">(divide 1 0)
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code> to run your divide function
in your image.</li>
<li>You will see the Interactive Debugger pop up. In the <code>Restarts</code>
section, choose option 2 to Abort.</li>
<li>Youre now back to your editor and still-running REPL and can
continue like it never happened.</li>
</ol>
<h4 id="use-the-interactive-debugger-to-fix-a-problem-at-runtime">Use the Interactive Debugger to fix a problem at runtime</h4>
<p><img src="assets/vscode-gifs/debug-fix.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(defun divide (x y)
(assert (not (zerop y))
(y)
"The second argument can not be zero. Please change it.")
(/ x y))
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code> to load your <code>define</code>
function into your image.</li>
<li>In your open file, add another new line and enter:</li>
</ol>
<pre><code class="language-lisp">(divide 1 0)
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code> to run your divide function
in your image.</li>
<li>You will see the Interactive Debugger pop up. In the <code>Restarts</code>
section, choose option 0 to “Retry assertion with new value for Y”.</li>
<li>In the popup menu, enter `y</li>
<li>In the next popup menu, enter <code>1</code></li>
<li>You should now see a small pop up that says <code>=&gt; 1 (1 bit, #x1, #o1,
#b1)</code>, which is the result of the new value. Youre now back to your
editor and still-running REPL after crashing out into the debugger,
having it let you change the value that caused the crash, and then
proceeding like you never typed that bad <code>0</code> value.</li>
</ol>
<p><em>More ideas for what can be done with the debugger can be found on the <a href="error_handling.html">error handling</a> page.</em></p>
<h4 id="expand-a-macro">Expand a macro</h4>
<p><img src="assets/vscode-gifs/macro-expand.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(loop for x in '(a b c d e) do
(print x))
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Macro Expand</code> to expand the for-loop macro.</li>
<li>You should see something like this:</li>
</ol>
<pre><code class="language-lisp">(BLOCK NIL
(LET ((X NIL)
(#:LOOP-LIST-559
(SB-KERNEL:THE* (LIST :USE-ANNOTATIONS T :SOURCE-FORM '(A B C D E))
'(A B C D E))))
(DECLARE (IGNORABLE #:LOOP-LIST-559)
(IGNORABLE X))
(TAGBODY
SB-LOOP::NEXT-LOOP
(SETQ X (CAR #:LOOP-LIST-559))
(SETQ #:LOOP-LIST-559 (CDR #:LOOP-LIST-559))
(PRINT X)
(IF (ENDP #:LOOP-LIST-559)
(GO SB-LOOP::END-LOOP))
(GO SB-LOOP::NEXT-LOOP)
SB-LOOP::END-LOOP)))
</code></pre>
<h4 id="disassemble-a-function">Disassemble a function</h4>
<p><img src="assets/vscode-gifs/disassemble.gif" alt="" /></p>
<ol>
<li>In your open file in your editor window, enter:</li>
</ol>
<pre><code class="language-lisp">(defun hello (name)
(format t "Hello, ~A~%" name))
</code></pre>
<ol>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Inline Eval</code> to load the function into
your image.</li>
<li>Put your cursor after the last parenthesis if it isnt already there.
Open the Command Palette on the menu at the top <code>View/Command
Palette</code> and choose <code>Alive: Disassemble</code> print out the machine code
of your compiled function.</li>
<li>It will start something like this:</li>
</ol>
<pre><code>; disassembly for HELLO
; Size: 172 bytes. Origin: #x70052478B4 ; HELLO
; 8B4: AC0A40F9 LDR R2, [THREAD, #16] ; binding-stack-pointer
; 8B8: 4C0F00F9 STR R2, [CFP, #24]
; 8BC: AC4642F9 LDR R2, [THREAD, #1160] ; tls: *STANDARD-OUTPUT*
; 8C0: 9F8501F1 CMP R2, #97
; 8C4: 61000054 BNE L0
; 8C8: 4AFDFF58 LDR R0, #x7005247870 ; '*STANDARD-OUTPUT*
; 8CC: 4C1140F8 LDR R2, [R0, #1]
; 8D0: L0: 4C1700F9 STR R2, [CFP, #40]
; 8D4: E0031BAA MOV NL0, CSP
; 8D8: 7A0701F8 STR CFP, [CSP], #16
; 8DC: EAFCFF58 LDR R0, #x7005247878 ; "Hello, "
; 8E0: 4B1740F9 LDR R1, [CFP, #40]
; 8E4: B6FBFF58 LDR LEXENV, #x7005247858 ; #&lt;SB-KERNEL:FDEFN WRITE-STRING&gt;
; 8E8: 970080D2 MOVZ NARGS, #4
; 8EC: FA0300AA MOV CFP, NL0
; 8F0: DE9240F8 LDR LR, [LEXENV, #9]
; 8F4: C0033FD6 BLR LR
; 8F8: 3B039B9A CSEL CSP, OCFP, CSP, EQ
; 8FC: E0031BAA MOV NL0, CSP
; 900: 7A0701F8 STR CFP, [CSP], #16
; 904: 4A2F42A9 LDP R0, R1, [CFP, #32]
; 908: D6FAFF58 LDR LEXENV, #x7005247860 ; #&lt;SB-KERNEL:FDEFN PRINC&gt;
; 90C: 970080D2 MOVZ NARGS, #4
; 910: FA0300AA MOV CFP, NL0
; 914: DE9240F8 LDR LR, [LEXENV, #9]
; 918: C0033FD6 BLR LR
; 91C: 3B039B9A CSEL CSP, OCFP, CSP, EQ
; 920: E0031BAA MOV NL0, CSP
; 924: 7A0701F8 STR CFP, [CSP], #16
; 928: 2A4981D2 MOVZ R0, #2633
; 92C: 4B1740F9 LDR R1, [CFP, #40]
; 930: D6F9FF58 LDR LEXENV, #x7005247868 ; #&lt;SB-KERNEL:FDEFN WRITE-CHAR&gt;
; 934: 970080D2 MOVZ NARGS, #4
; 938: FA0300AA MOV CFP, NL0
; 93C: DE9240F8 LDR LR, [LEXENV, #9]
; 940: C0033FD6 BLR LR
; 944: 3B039B9A CSEL CSP, OCFP, CSP, EQ
; 948: EA031DAA MOV R0, NULL
; 94C: FB031AAA MOV CSP, CFP
; 950: 5A7B40A9 LDP CFP, LR, [CFP]
; 954: BF0300F1 CMP NULL, #0
; 958: C0035FD6 RET
; 95C: E00120D4 BRK #15 ; Invalid argument count trap
</code></pre>
<h4 id="create-a-skeleton-common-lisp-system">Create a skeleton Common Lisp system</h4>
<p><img src="assets/vscode-gifs/skeleton.gif" alt="" /></p>
<p><em>This recipe creates a new Common Lisp System, so it does not need a
running REPL.</em></p>
<ol>
<li>Create a folder called <code>experiment</code> for your new project</li>
<li>Open vscode in your newly created directory</li>
</ol>
<pre><code class="language-bash">cd experiment
code .
</code></pre>
<ol>
<li>Create new Common Lisp System.
<ul>
<li>Inside of VSCode, Open Command Palette on the menu at the top
<code>View/Command Palette</code> and generate a system skeleton: <code>Alive: System Skeleton</code></li>
<li>The previous command should have generated the following directory
structure:</li>
</ul>
<ul>
<li>experiment.asd</li>
<li>src/
<ul>
<li>app.lisp</li>
</ul>
</li>
<li>test/
<ul>
<li>all.lisp</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>The content of those files is as follows:</p>
<p><code>experiment.asd</code>:</p>
<pre><code class="language-lisp">(in-package :asdf-user)
(defsystem "experiment"
:class :package-inferred-system
:depends-on ("experiment/src/app")
:description ""
:in-order-to ((test-op (load-op "experiment/test/all")))
:perform (test-op (o c) (symbol-call :test/all :test-suite)))
(defsystem "experiment/test"
:depends-on ("experiment/test/all"))
(register-system-packages "experiment/src/app" '(:app))
(register-system-packages "experiment/test/all" '(:test/all))
</code></pre>
<p><code>src/app.lisp</code>:</p>
<pre><code class="language-lisp">(defpackage :app
(:use :cl))
(in-package :app)
</code></pre>
<p><code>test/all.lisp</code>:</p>
<pre><code class="language-lisp">(defpackage :test/all
(:use :cl
:app)
(:export :test-suite))
(in-package :test/all)
(defun test-suite ()
(format T "Test Suite~%"))
</code></pre>
<h3 id="optional-custom-configurations">Optional Custom Configurations</h3>
<h4 id="configuring-vscode-alive-to-work-with-quicklisp">Configuring VSCode Alive to work with Quicklisp</h4>
<p>Assuming that you have quicklisp installed and configured to load on
init, quicklisp just works.</p>
<h4 id="configuring-vscode-alive-to-work-with-clpm-in-the-default-context">Configuring VSCode Alive to work with CLPM in the default context</h4>
<p>Assuming that you have <a href="https://clpm.dev">CLPM</a> installed and
configured, <a href="https://code.visualstudio.com/docs/getstarted/settings">modify your vscode settings</a> to
look like this:</p>
<ol>
<li>Add the following to to your VSCode settings:</li>
</ol>
<pre><code class="language-json"> "alive.swank.startupCommand":[
"clpm",
"exec",
"--",
"sbcl",
"--eval",
"(asdf:load-system :swank)",
"--eval",
"(swank:create-server)"
],
</code></pre>
<p><em>This will start up sbcl in the default clpm context</em></p>
<h4 id="configuring-vscode-alive-to-work-with-clpm-using-a-bundle-clpmfile">Configuring VSCode Alive to work with CLPM using a bundle clpmfile</h4>
<p>Assuming that you have <a href="https://clpm.dev">CLPM</a> installed and configured
and a bundle configured in the root of your home directory that contains
swank as a dev dependency, <a href="https://code.visualstudio.com/docs/getstarted/settings">modify your vscode
settings</a> to
look like this:</p>
<ol>
<li>Add the following to your VSCode settings:</li>
</ol>
<pre><code class="language-json"> "alive.swank.startupCommand":[
"clpm",
"bundle",
"exec",
"--",
"sbcl",
"--eval",
"(asdf:load-system :swank)",
"--eval",
"(swank:create-server)"
],
</code></pre>
<p><em>This will start up sbcl in your bundles clpm context</em></p>
<h4 id="configuring-vscode-alive-to-work-with-roswell">Configuring VSCode Alive to work with Roswell</h4>
<p>Assuming that you have <a href="https://roswell.github.io/">Roswell</a> installed,
<a href="https://code.visualstudio.com/docs/getstarted/settings">modify your vscode
settings</a> to
look like this:</p>
<pre><code class="language-json"> "alive.swank.startupCommand": [
"ros",
"run",
"--eval",
"(require :asdf)",
"--eval",
"(asdf:load-system :swank)",
"--eval",
"(swank:create-server)"
]
</code></pre>
<h4 id="connecting-vscode-alive-to-a-docker-container">Connecting VSCode Alive to a Docker container</h4>
<p><img src="assets/vscode-gifs/docker-connect.gif" alt="" /></p>
<p><em>These instructions will work for remote connections, wsl connections,
and github Codespaces as well using the <code>Remote - SSH</code> and <code>Remote -
WSL</code>, and <code>Github Codespaces</code> extensions, respectively assuming you have
the extensions installed. For this example, make sure you have the
<a href="https://code.visualstudio.com/docs/remote/containers">Containers extension installed and
configured</a>.</em></p>
<ol>
<li>Pull a docker image that has sbcl installed, in this example, well use
the latest clfoundations sbcl.</li>
</ol>
<pre><code class="language-sh">docker pull clfoundation/sbcl
</code></pre>
<ol>
<li>Run bash in the docker image to start it up and keep it running.</li>
</ol>
<pre><code class="language-sh">docker run -it clfoundation/sbcl bash
</code></pre>
<ol>
<li>In the VSCode Side Bar, click the <code>Remote Explorer</code> icon.</li>
<li>In the list of Dev Containers, right click on clfoundation/sbcl and choose
<code>Attach to Container</code>.</li>
<li>In the VSCode Side Bar of the new VSCode window that opens up, click
on <code>Explorer</code>. <em>You may need to tell it to view the files in your
container if it isnt already showing them.</em></li>
<li>Once youre viewing the files in the container, right click in the
VSCode <code>Side Bar</code> and choose <code>New File</code>. Name the file <code>hello.lisp</code></li>
<li>In the VSCode Site Bar, click the <code>Extensions</code> icon</li>
<li>Click the <code>Install in Container...</code> button for the <code>Alive</code> plugin</li>
<li>Open up your <code>hello.lisp</code> file and follow the “Connect VSCode to a
REPL” instructions at the beginning of these recipes</li>
<li>You now have VSCode running a REPL hooked to a Slime server running
on an SBCL image in a docker container.</li>
</ol>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/vscode-alive.md">vscode-alive.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2021 the Common Lisp Cookbook Project
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>