704 lines
31 KiB
HTML
704 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta name="generator" content=
|
||
"HTML Tidy for HTML5 for Linux version 5.2.0">
|
||
<title>LispWorks review</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> – LispWorks review</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> – LispWorks review</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 style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPMACROSPOWER" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover vindarel's Lisp course in videos with this September coupon.</a>
|
||
<strong>
|
||
Recently added: 18 videos on MACROS.
|
||
</strong>
|
||
<a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>.
|
||
</p>
|
||
<p class="announce-neutral">
|
||
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
|
||
</p>
|
||
|
||
|
||
<div id="content"
|
||
<p><a href="http://www.lispworks.com/">LispWorks</a> is a Common Lisp implementation that
|
||
comes with its own Integrated Development Environment (IDE) and its share of
|
||
unique features, such as the CAPI GUI toolkit. It is <strong>proprietary</strong> and
|
||
provides a <strong>free limited version</strong>.</p>
|
||
|
||
<p>Here, we will mainly explore its IDE, asking ourselves what it can
|
||
offer to a seasoned lisper used to Emacs and Slime. The short answer
|
||
is: more graphical tools, such as an easy to use graphical stepper, a
|
||
tracer, a code coverage browser or again a class browser. Setting and
|
||
using breakpoints was easier than on Slime.</p>
|
||
|
||
<p>LispWorks also provides more integrated tools (the Process browser
|
||
lists all processes running in the Lisp image and we can
|
||
stop, break or debug them) and presents many information in the form of
|
||
graphs (for example, a graph of function calls or a graph of all the
|
||
created windows).</p>
|
||
|
||
<p><img src="assets/lispworks/two-sided-view.png" alt="LispWorks' listener and editor in the Mate desktop environment" title="The LispWorks listener an the editor in the Mate desktop environment" /></p>
|
||
|
||
<h2 id="lispworks-features">LispWorks features</h2>
|
||
|
||
<p>We can see a matrix of LispWorks features by edition and platform here: <a href="http://www.lispworks.com/products/features.html">http://www.lispworks.com/products/features.html</a>.</p>
|
||
|
||
<p>We highlight:</p>
|
||
|
||
<ul>
|
||
<li>32-bit, 64-bit and ARM support on Windows, MacOS, Linux, Solaris, FreeBSD,</li>
|
||
<li><a href="http://www.lispworks.com/documentation/lw61/CAPRM/html/capiref.htm">CAPI portable GUI toolkit</a>: provides native look-and-feel on Windows, Cocoa, GTK+ and Motif.
|
||
<ul>
|
||
<li>comes with a graphical “Interface Builder” (think QtCreator) (though not available on MacOS (nor on mobile))</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="http://www.lispworks.com/products/lw4mr.html">LispWorks for mobile runtime</a>, for Android and iOS,</li>
|
||
<li>optimized application delivery: LispWorks can use a tree shaker to
|
||
remove unused lisp code from the delivered applicatiion, thus
|
||
shipping lighter binaries than existing open-source implementations.</li>
|
||
<li>ability to deliver a dynamic library,</li>
|
||
<li>a <a href="http://www.lispworks.com/documentation/lw71/LW/html/lw-113.htm">Java interface</a>, allowing to call from Lisp to Java or the other way around,</li>
|
||
<li>an Objective-C and Cocoa interface, with drag and drop and multi-touch support,</li>
|
||
<li>a Foreign Language Interface,</li>
|
||
<li>TCP/UDP sockets with SSL & IPv6 support,</li>
|
||
<li>natived threads and symmetric multiprocessing, unicode support, and all other Common Lisp features, and all other LispWorks Enterprise features.</li>
|
||
</ul>
|
||
|
||
<p>And, of course, a built-in IDE.</p>
|
||
|
||
<p>LispWorks is used in diverse areas of the industry. They maintain <a href="http://www.lispworks.com/success-stories/index.html">a list of success stories</a>. As for software that we can use ourselves, we find <a href="https://scorecloud.com/">ScoreCloud</a> amazing (a music notation software: you play an instrument, sing or whistle and it writes the music) or <a href="https://github.com/openmusic-project/openmusic/">OpenMusic</a> (opensource composition environment).</p>
|
||
|
||
<h3 id="free-edition-limitations">Free edition limitations</h3>
|
||
|
||
<p>The download instructions and the limitations are given <a href="http://www.lispworks.com/downloads/index.html">on the download page</a>.</p>
|
||
|
||
<p>The limitations are the following:</p>
|
||
|
||
<ul>
|
||
<li>There is a <strong>heap size limit</strong> which, if exceeded, causes the image to exit. A warning is provided when the limit is approached.</li>
|
||
</ul>
|
||
|
||
<p>What does it prevent us to do? As an illustration, we can not load this set of libraries together in the same image:</p>
|
||
|
||
<pre><code class="language-lisp">(ql:quickload '("alexandria" "serapeum" "bordeaux-threads" "lparallel" "dexador" "hunchentoot" "quri" "ltk" "cl-ppcre" "mito"))
|
||
</code></pre>
|
||
|
||
<ul>
|
||
<li>
|
||
<p>There is a <strong>time limit of 5 hours</strong> for each session, after which LispWorks Personal exits, possibly without saving your work or performing cleanups such as removing temporary files. You are warned after 4 hours of use.</p>
|
||
</li>
|
||
<li>
|
||
<p>It is <strong>impossible to build a binary</strong>. Indeed, the functions <a href="http://www.lispworks.com/documentation/lw71/LW/html/lw-95.htm">save-image</a>, <a href="http://www.lispworks.com/documentation/lw71/DV/html/delivery-4.htm#pgfId-852223">deliver</a> (<em>the</em> function to create a stand-alone executable), and load-all-patches are not available.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Initialization files are not loaded</strong>. If you are used to initializing Quicklisp from your <code>~/.sbclrc</code> on Emacs, you’ll have to load an init file manually every time you start LispWorks (<code>(load #p"~/.your-init-file</code>)).</p>
|
||
</li>
|
||
</ul>
|
||
|
||
<p>For the record, the snippet provided by Quicklisp to put in one’s startup file is the following:</p>
|
||
|
||
<pre><code class="language-lisp">;; provided you installed quicklisp in ~/quicklisp/
|
||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
|
||
(when (probe-file quicklisp-init)
|
||
(load quicklisp-init)))
|
||
</code></pre>
|
||
|
||
<p>You’ll have to paste it to the listener window (with the <code>C-y</code> key, y as “yank”).</p>
|
||
|
||
<ul>
|
||
<li>Layered products that are part of LispWorks Professional and Enterprise Editions (CLIM, KnowledgeWorks, Common SQL and LispWorks ORB) are not included. But <strong>we can try the CAPI toolkit</strong>.</li>
|
||
</ul>
|
||
|
||
<p>The installation process requires you to fill an HTML form to receive
|
||
a download link, then to run a first script that makes you accept the
|
||
terms and the licence, then to run a second script that installs the software.</p>
|
||
|
||
<h3 id="licencing-model">Licencing model</h3>
|
||
|
||
<p>LispWorks actually comes in four paid editions. It’s all explained by themselves here: <a href="http://www.lispworks.com/products/lispworks.html">http://www.lispworks.com/products/lispworks.html</a>. In short, there is:</p>
|
||
|
||
<ul>
|
||
<li>a Hobbyist edition with <code>save-image</code> and <code>load-all-patches</code>, to apply updates of minor versions, without the obvious limitations, for non-commercial and non-academic use,</li>
|
||
<li>a HobbyistDV edition with the <code>deliver</code> function to create executables (still for non-commercial and non-academic uses),</li>
|
||
<li>a Professional edition, with the <code>deliver</code> function, for commercial and academic uses,</li>
|
||
<li>an Enterprise one, with their enterprise modules: the Common SQL interface, LispWorks ORB, KnowledgeWorks.</li>
|
||
</ul>
|
||
|
||
<p>At the time of writing, the licence of the hobbyist edition costs 750 USD, the pro version the double. They are bought for a LW version, per platform. They have no limit of time.</p>
|
||
|
||
<div class="info-box info">
|
||
<!-- if inside a <p> then bootstrap adds 10px padding to the bottom -->
|
||
<strong>NB:</strong> Please double check their upstream resources and don't hesitate to contact them.
|
||
</div>
|
||
|
||
<h2 id="lispworks-ide">LispWorks IDE</h2>
|
||
|
||
<p>The LispWorks IDE is self-contained, but it is also possible to use LispWorks-the-implementation from Emacs and Slime (see below). The IDE runs <em>inside</em> the Common Lisp image, unlike Emacs which is an external program that communicates with the Lisp image through Swank and Slime. User code runs in the same process.</p>
|
||
|
||
<h3 id="the-editor">The editor</h3>
|
||
|
||
<p>The editor offers what’s expected: a TAB-completion pop-up, syntax
|
||
highlighting, Emacs-like keybindings (including the <code>M-x</code> extended
|
||
command). The menus help the discovery.</p>
|
||
|
||
<p>We personally found the editing experience a bit “raw”. For example:</p>
|
||
|
||
<ul>
|
||
<li>indention after a new line is not automatic, one has to press TAB again.</li>
|
||
<li>the auto-completion is not fuzzy.</li>
|
||
<li>there are no plugins similar to <del>Paredit</del> (there is a brand new (2021) <a href="https://github.com/g000001/lw-paredit">Paredit for LispWorks</a>) or Lispy, nor a Vim layer.</li>
|
||
</ul>
|
||
|
||
<p>We also had an issue, in that the go-to-source function bound to <code>M-.</code>
|
||
did not work out for built-in Lisp symbols. Apparently, LispWorks
|
||
doesn’t provide much source code, and mostly code of the editor. Some
|
||
other commercial Lisps, like Allegro CL, provide more source code</p>
|
||
|
||
<p>The editor provides an interesting tab: Changed Definitions. It lists the functions and methods that were redefined since, at our choosing: the first edit of the session, the last save, the last compile.</p>
|
||
|
||
<p>See also:</p>
|
||
|
||
<ul>
|
||
<li>the <a href="http://www.lispworks.com/documentation/lw71/EDUG-U/html/eduser-u.htm">Editor User Guide</a>.</li>
|
||
</ul>
|
||
|
||
<h3 id="keybindings">Keybindings</h3>
|
||
|
||
<p>Most of the keybindings are similar to Emacs, but not all. Here are some differences:</p>
|
||
|
||
<ul>
|
||
<li>to <strong>compile a function</strong>, use <code>C-S-c</code> (control, shift and c), instead of C-c C-c.</li>
|
||
<li>to <strong>compile the current buffer</strong>, use <code>C-S-b</code> (instead of C-c C-k).</li>
|
||
</ul>
|
||
|
||
<p>Similar ones include:</p>
|
||
|
||
<ul>
|
||
<li><code>C-g</code> to cancel what you’re doing,</li>
|
||
<li><code>C-x C-s</code> to save the current buffer,</li>
|
||
<li><code>M-w</code> and <code>C-y</code> to copy and paste,</li>
|
||
<li><code>M-b</code>, <code>M-f</code>, <code>C-a</code>, <code>C-e</code>… to move around words, to go to the beginning or the end of the line,</li>
|
||
<li><code>C-k</code> to kill until the end of the line, <code>C-w</code> to kill a selected region,</li>
|
||
<li><code>M-.</code> to find the source of a symbol,</li>
|
||
<li><code>C-x C-e</code> to evaluate the current defun,</li>
|
||
<li>…</li>
|
||
</ul>
|
||
|
||
<p>Some useful functions don’t have a keybinding by default, for example:</p>
|
||
|
||
<!-- - delete selected text with `M-x delete-region` (or kill the region with `C-w`) -->
|
||
<ul>
|
||
<li>clear the REPL with <code>M-x Clear Listener</code></li>
|
||
<li><code>Backward Kill Line</code></li>
|
||
</ul>
|
||
|
||
<p>It is possible to use <strong>classical keybindings</strong>, à la KDE/Gnome. Go to the
|
||
Preferences menu, Environment and in the Emulation tab.</p>
|
||
|
||
<p>There is <strong>no Vim layer</strong>.</p>
|
||
|
||
<h3 id="searching-keybindings-by-name">Searching keybindings by name</h3>
|
||
|
||
<p>It is possible to search for a keybinding associated to a function, or
|
||
a function name from its keybinding, with the menu (Help -> Editing ->
|
||
Key to Command / Command to Key) or with <code>C-h</code> followed by a key,
|
||
as in Emacs. For example type <code>C-h k</code> then enter a keybinding to
|
||
get the command name. See more with <code>C-h ?</code>.</p>
|
||
|
||
<h3 id="tweaking-the-ide">Tweaking the IDE</h3>
|
||
|
||
<p>It is possible to change keybindings. The editor’s state is accessible
|
||
from the <code>editor</code> package, and the editor is built with the CAPI
|
||
framework, so we can use the <code>capi</code> interface too. Useful functions
|
||
include:</p>
|
||
|
||
<pre><code class="language-lisp">`
|
||
editor:bind-key
|
||
editor:defcommand
|
||
editor:current-point
|
||
editor:with-point ;; save point location
|
||
editor:move-point
|
||
editor:*buffer-list*
|
||
editor:*in-listener* ;; returns T when we are in the REPL
|
||
…
|
||
</code></pre>
|
||
|
||
<p>Here’s how you can bind keys:</p>
|
||
|
||
<pre><code class="language-lisp">;; Indent new lines.
|
||
;; By default, the point is not indented after a Return.
|
||
(editor:bind-key "Indent New Line" #\Return :mode "Lisp")
|
||
|
||
;; Insert pairs.
|
||
(editor:bind-key "Insert Parentheses For Selection" #\( :mode "Lisp") ;;
|
||
(editor:bind-key "Insert Double Quotes For Selection" #\" :mode "Lisp")
|
||
</code></pre>
|
||
|
||
<p>Here’s how to define a new command. We make the <code>)</code> key
|
||
to go past the next closing parenthesis.</p>
|
||
|
||
<pre><code class="language-lisp">(editor:defcommand "Move Over ()" (p)
|
||
"Move past the next close parenthesis.
|
||
Any indentation preceeding the parenthesis is deleted."
|
||
"Move past the next close parenthesis."
|
||
;; thanks to Thomas Hermann
|
||
;; https://github.com/ThomasHermann/LispWorks/blob/master/editor.lisp
|
||
(declare (ignore p))
|
||
(let ((point (editor:current-point)))
|
||
(editor:with-point ((m point))
|
||
(cond ((editor::forward-up-list m)
|
||
(editor:move-point point m)
|
||
(editor::point-before point)
|
||
(loop (editor:with-point ((back point))
|
||
(editor::back-to-indentation back)
|
||
(unless (editor:point= back point)
|
||
(return)))
|
||
(editor::delete-indentation point))
|
||
(editor::point-after point))
|
||
(t (editor:editor-error))))))
|
||
|
||
(editor:bind-key "Move Over ()" #\) :mode "Lisp")
|
||
</code></pre>
|
||
|
||
<p>And here’s how you can change indentation for special forms:</p>
|
||
|
||
<pre><code class="language-lisp">(editor:setup-indent "if" 1 4 1)
|
||
</code></pre>
|
||
|
||
<p>See also:</p>
|
||
|
||
<ul>
|
||
<li>a list of LispWork keybindings: <a href="https://www.nicklevine.org/declarative/lectures/additional/key-binds.html">https://www.nicklevine.org/declarative/lectures/additional/key-binds.html</a></li>
|
||
</ul>
|
||
|
||
<h3 id="the-listener">The listener</h3>
|
||
|
||
<p>The listener is the REPL we are expecting to find, but it has a slight
|
||
difference from Slime.</p>
|
||
|
||
<p>It doesn’t evaluate the input line by line or form by form, instead it
|
||
parses the input while typing. So we get some errors instantly. For
|
||
example, we type <code>(abc</code>. So far so good. Once we type a colon to get
|
||
<code>(abc:</code>, an error message is printed just above our input:</p>
|
||
|
||
<pre><code>Error while reading: Reader cannot find package ABC.
|
||
|
||
CL-USER 1 > (abc:
|
||
</code></pre>
|
||
|
||
<p>Indeed, now <code>abc:</code> references a package, but such a package doesn’t exist.</p>
|
||
|
||
<p>Its interactive debugger is primarily textual but you can also
|
||
interact with it with graphical elements. For example, you can use the
|
||
Abort button of the menu bar, which brings you back to the top
|
||
level. You can invoke the graphical debugger to see the stacktraces
|
||
and interact with them. See the Debugger button at the very end of the
|
||
toolbar.</p>
|
||
|
||
<p><img src="assets/lispworks/toolbar-debugger.png" alt="" /></p>
|
||
|
||
<p>If you see the name of your function in the stacktraces (you will if
|
||
you wrote and compiled your code in a file, and not directly wrote it
|
||
in the REPL), you can double-click on its name to go back to the
|
||
editor and have it highlight the part of your code that triggered the
|
||
error.</p>
|
||
|
||
<div class="info-box info">
|
||
<!-- if inside a <p> then bootstrap adds 10px padding to the bottom -->
|
||
<strong>NB:</strong> this is equivalent of pressing <code>M-v</code> in Slime.
|
||
</div>
|
||
|
||
<p>It is possible to choose the graphical debugger to appear by default, instead of the textual one.</p>
|
||
|
||
<p>The listener provides some helper commands, not unlike Slime’s ones starting with a comma <code>,</code>:</p>
|
||
|
||
<pre><code>CL-USER 1 > :help
|
||
|
||
:bug-form <subject> &key <filename>
|
||
Print out a bug report form, optionally to a file.
|
||
:get <variable> <command identifier>
|
||
Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
|
||
:help Produce this list.
|
||
:his &optional <n1> <n2>
|
||
List the command history, optionally the last n1 or range n1 to n2.
|
||
:redo &optional <command identifier>
|
||
Redo a previous command, found by its number or a symbol/subform within it.
|
||
:use <new> <old> &optional <command identifier>
|
||
Do variant of a previous command, replacing old symbol/subform with new symbol/subform.
|
||
</code></pre>
|
||
|
||
<h3 id="the-stepper-breakpoints">The stepper. Breakpoints.</h3>
|
||
|
||
<p>The <a href="http://www.lispworks.com/documentation/lw61/IDE-W/html/ide-w-496.htm">stepper</a> is one
|
||
of the areas where LispWorks shines.</p>
|
||
|
||
<p>When your are writing code in the editor window, you can set
|
||
breakpoints with the big red “Breakpoint” button (or by calling <code>M-x Stepper Breakpoint</code>).
|
||
This puts a red mark in your code.</p>
|
||
|
||
<p>The next time your code is executed, you’ll get a comprehensive Stepper pop-up window showing:</p>
|
||
|
||
<ul>
|
||
<li>your source code, with an indicator showing what expression is being evaluated</li>
|
||
<li>a lower pane with two tabs:
|
||
<ul>
|
||
<li>the backtrace, showing the intermediate variables, thus showing their evolution during the execution of the program</li>
|
||
<li>the listener, in the context of this function call, where you can evaluate expressions</li>
|
||
</ul>
|
||
</li>
|
||
<li>and the menu bar with the stepper controls: you can step into the next expression, step on the next function call, continue execution until the position of the cursor, continue the execution until the next breakpoint, etc.</li>
|
||
</ul>
|
||
|
||
<p><img src="assets/lispworks/stepper.gif" alt="" /></p>
|
||
|
||
<p>That’s not all. The non-visual, REPL-oriented stepper is also nice. It shows the forms that are being evaluated and their results.</p>
|
||
|
||
<p>In this example, we use <code>:s</code> to “step” though the current form and its subforms. We are using the usual listener, we can write any Lisp code after the prompt (the little ` -> ` here), and we have access to the local variables (<code>X</code>).</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER 4 > (defun my-abs (x) (cond ((> x 0) x) ((< x 0) (- x)) (t 0)))
|
||
CL-USER 5 > (step (my-abs -5))
|
||
(MY-ABS -5) -> :s
|
||
-5 -> :s
|
||
-5
|
||
(COND ((> X 0) X) ((< X 0) (- X)) (T 0)) <=> (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0)))
|
||
;; Access to the local variables:
|
||
(IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> (format t "Is X equal to -5? ~a~&" (if (equal x -5) "yes" "no"))
|
||
Is X equal to -5? yes
|
||
(IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> :s
|
||
(> X 0) -> :s
|
||
X -> :s
|
||
-5
|
||
0 -> :s
|
||
0
|
||
NIL
|
||
(IF (< X 0) (- X) (PROGN 0)) -> :s
|
||
(< X 0) -> :s
|
||
X -> :s
|
||
-5
|
||
0 -> :s
|
||
0
|
||
T
|
||
(- X) -> :s
|
||
X -> :s
|
||
-5
|
||
5
|
||
5
|
||
5
|
||
5
|
||
</code></pre>
|
||
|
||
<p>Here are the available stepper commands (see <code>:?</code>):</p>
|
||
|
||
<pre><code>:s Step this form and all of its subforms (optional +ve integer arg)
|
||
:st Step this form without stepping its subforms
|
||
:si Step this form without stepping its arguments if it is a function call
|
||
:su Step up out of this form without stepping its subforms
|
||
:sr Return a value to use for this form
|
||
:sq Quit from the current stepper level
|
||
:bug-form <subject> &key <filename>
|
||
Print out a bug report form, optionally to a file.
|
||
:get <variable> <command identifier>
|
||
Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
|
||
:help Produce this list.
|
||
:his &optional <n1> <n2>
|
||
List the command history, optionally the last n1 or range n1 to n2.
|
||
:redo &optional <command identifier>
|
||
Redo a previous command, found by its number or a symbol/subform within it.
|
||
:use <new> <old> &optional <command identifier>
|
||
Do variant of a previous command, replacing old symbol/subform with new symbol/subform.
|
||
</code></pre>
|
||
|
||
<h3 id="the-class-browser">The class browser</h3>
|
||
|
||
<p>The class browser allows us to examine a class’s slots, parent classes, available methods, and some more.</p>
|
||
|
||
<p>Let’s create a simple class:</p>
|
||
|
||
<pre><code class="language-lisp">(defclass person ()
|
||
((name :accessor name
|
||
:initarg :name
|
||
:initform "")
|
||
(lisper :accessor lisperp
|
||
:initform t)))
|
||
</code></pre>
|
||
|
||
<p>Now call the class browser:</p>
|
||
|
||
<ul>
|
||
<li>use the “Class” button from the listener,</li>
|
||
<li>or use the menu Expression -> Class,</li>
|
||
<li>or put the cursor on the class and call <code>M-x Describe class</code>.</li>
|
||
</ul>
|
||
|
||
<p><img src="assets/lispworks/class-browser.png" alt="" /></p>
|
||
|
||
<p>It is composed of several panes:</p>
|
||
|
||
<ul>
|
||
<li>the <strong>class hierarchy</strong>, showing the superclasses on the left and the subclasses on the right, with their description to the bottom,</li>
|
||
<li>the <strong>superclasses viewer</strong>, in the form of a simple schema, and the same for subclasses,</li>
|
||
<li>the <strong>slots pane</strong> (the default),</li>
|
||
<li>the available <strong>initargs</strong>,</li>
|
||
<li>the existing <strong>generic functions</strong> for that class</li>
|
||
<li>and the <strong>class precedence list</strong>.</li>
|
||
</ul>
|
||
|
||
<p>The Functions pane lists all methods applicable to that class, so we can discover public methods provided by the CLOS object system: <code>initialize-instance</code>, <code>print-object</code>, <code>shared-initialize</code>, etc. We can double-click on them to go to their source. We can choose not to include the inherited methods too (see the “include inherited” checkbox).</p>
|
||
|
||
<p>You’ll find buttons on the toolbar (for example, Inspect a generic
|
||
function) and more actions on the Methods menu, such as a way to see
|
||
the <strong>functions calls</strong>, a menu to <strong>undefine</strong> or <strong>trace</strong> a function.</p>
|
||
|
||
<p>See more:</p>
|
||
|
||
<ul>
|
||
<li><a href="http://www.lispworks.com/documentation/lw71/IDE-U/html/ide-u-55.htm#pgfId-871798">Chapter 8 of the documentation: the Class Browser</a></li>
|
||
</ul>
|
||
|
||
<h3 id="the-function-call-browser">The function call browser</h3>
|
||
|
||
<p>The function call browser allows us to see a graph of the callers and
|
||
the callees of a function. It provides several ways to filter the
|
||
displayed information and to further inspect the call stack.</p>
|
||
|
||
<div class="info-box info">
|
||
<!-- if inside a <p> then bootstrap adds 10px padding to the bottom -->
|
||
<strong>NB:</strong> The Slime functions to find such cross-references are <code>slime-who-[calls, references, binds, sets, depends-on, specializes, macroexpands]</code>.
|
||
</div>
|
||
|
||
<p>After loading a couple packages, here’s a simple example showing who calls the <code>string-trim</code> function.</p>
|
||
|
||
<p><img src="assets/lispworks/function-call-browser.png" alt="The function call browser" /></p>
|
||
|
||
<p>It shows functions from all packages, but there is a select box to restrict it further, for example to the “current and used” or only to the current packages.</p>
|
||
|
||
<p>Double click on a function shown in the graph to go to its source. Again, as in many LispWorks views, the Function menu allows to further manipulate selected functions: trace, undefine, listen (paste the object to the Listener)…</p>
|
||
|
||
<p>The Text tab shows the same information, but textually, the callers and callees side by side.</p>
|
||
|
||
<p>We can see cross references for compiled code, and we must ensure the feature is on. When we compile code, LispWorks shows a compilation output likes this:</p>
|
||
|
||
<pre><code>;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 1
|
||
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
|
||
;;; Source level debugging is on
|
||
;;; Source file recording is on
|
||
;;; Cross referencing is on
|
||
</code></pre>
|
||
|
||
<p>We see that cross referencing is on. Otherwise, activate it with <code>(toggle-source-debugging t)</code>.</p>
|
||
|
||
<p>See more:</p>
|
||
|
||
<ul>
|
||
<li><a href="http://www.lispworks.com/documentation/lw71/IDE-U/html/ide-u-114.htm#pgfId-852601">Chapter 15: the function call browser</a></li>
|
||
</ul>
|
||
|
||
<h3 id="the-process-browser">The Process Browser</h3>
|
||
|
||
<p>The Process Browser shows us a list of all threads running. The input area allows to filter by name. It accepts regular expressions. Then we can stop, inspect, listen, break into these processes.</p>
|
||
|
||
<p><img src="assets/lispworks/process-browser.png" alt=""The process browser"" /></p>
|
||
|
||
<p>See more:</p>
|
||
|
||
<ul>
|
||
<li><a href="http://www.lispworks.com/documentation/lw71/IDE-U/html/ide-u-178.htm#pgfId-852666">Chapter 28: the Process Browser</a></li>
|
||
</ul>
|
||
|
||
<h3 id="saving-images">Saving images</h3>
|
||
|
||
<p>Saving images with LispWorks is different than with SBCL:</p>
|
||
|
||
<ul>
|
||
<li>we can save an image now, or schedule snapshots later in time</li>
|
||
<li>the new created image becomes the default core image for our LispWorks environment</li>
|
||
<li>the REPL session is saved</li>
|
||
<li>the windows configuration is saved</li>
|
||
<li>threads are saved</li>
|
||
</ul>
|
||
|
||
<p>So, effectively, we can save an image and have our development
|
||
environment back to the same state, effectively allowing to take
|
||
snapshots of our current work and to continue where we left of.</p>
|
||
|
||
<p>For example, we can start a game from the REPL, play a little bit in
|
||
its window, save an image, and when restored we will get the game and
|
||
its state back.</p>
|
||
|
||
<h3 id="misc">Misc</h3>
|
||
|
||
<p>We like the <code>Search Files</code> functionality. It is like a recursive
|
||
<code>grep</code>, but we get a typical LispWorks graphical window
|
||
that displays the results, allows to double-click on them and that offers
|
||
some more actions.</p>
|
||
|
||
<p>Last but not least, have a look at the <strong>compilation conditions
|
||
browser</strong>. LispWorks puts all warnings and errors into a special
|
||
browser when we compile a system. From now on we can work on fixing
|
||
them and see them disappear from the browser. That helps keeping track
|
||
of warnings and errors during development.</p>
|
||
|
||
<h2 id="using-lispworks-from-emacs-and-slime">Using LispWorks from Emacs and Slime</h2>
|
||
|
||
<p>To do that, you have two possibilities. The first one is to start LispWorks normally, start a Swank server and connect to it from Emacs (Swank is the backend part of Slime).</p>
|
||
|
||
<p>First, let’s load the dependencies:</p>
|
||
|
||
<pre><code class="language-lisp">(ql:quickload "swank")
|
||
;; or
|
||
(load "~/.emacs.d/elpa/slime-20xx/swank-loader.lisp")
|
||
</code></pre>
|
||
|
||
<p>Start a server:</p>
|
||
|
||
<pre><code class="language-lisp">(swank:create-server :port 9876)
|
||
;; Swank started at port: 9876.
|
||
9876
|
||
</code></pre>
|
||
|
||
<p>From Emacs, run <code>M-x slime-connect</code>, choose <code>localhost</code> and <code>9876</code> for the port.</p>
|
||
|
||
<p>You should be connected. Check with: <code>(lisp-implementation-type)</code>. You are now able to use LispWorks’ features:</p>
|
||
|
||
<pre><code class="language-lisp">(setq button
|
||
(make-instance 'capi:push-button
|
||
:data "Button"))
|
||
(capi:contain button)
|
||
</code></pre>
|
||
|
||
<p>The second possibility is to create a non-GUI LispWorks image, with
|
||
Swank loaded, and to run this image from SLIME or SLY. For example, to
|
||
create a so-called <code>console</code> image with multiprocessing enabled:</p>
|
||
|
||
<pre><code class="language-lisp">(in-package "CL-USER")
|
||
(load-all-patches)
|
||
(save-image "~/lw-console"
|
||
:console t
|
||
:multiprocessing t
|
||
:environment nil)
|
||
</code></pre>
|
||
|
||
<p>and run LispWorks like this to create the new image ~/lw-console:</p>
|
||
|
||
<pre><code>lispworks-7-0-0-x86-linux -build /tmp/resave.lisp
|
||
</code></pre>
|
||
|
||
<p>However: <code>console</code> is implemented <strong>only for Windows and Mac</strong>.</p>
|
||
|
||
<p>See LispWorks’ documentation.</p>
|
||
|
||
<h2 id="see-also">See also</h2>
|
||
|
||
<ul>
|
||
<li><a href="http://www.lispworks.com/documentation/lw71/IDE-U/html/ide-u.htm">LispWorks IDE User Guide</a> (check out the sections we didn’t cover)</li>
|
||
<li><a href="https://en.wikipedia.org/wiki/LispWorks">LispWorks on Wikipedia</a></li>
|
||
<li>the <a href="https://github.com/fourier/awesome-lispworks">Awesome LispWorks</a> list</li>
|
||
<li><a href="https://www.youtube.com/watch?v=nsKx40ab9SY">Real Image-based approach in Common Lisp</a> - differences between SBCL and LispWorks.</li>
|
||
</ul>
|
||
|
||
|
||
<p class="page-source">
|
||
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/lispworks.md">lispworks.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/>
|
||
© 2002–2023 the Common Lisp Cookbook Project
|
||
<div>
|
||
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">vindarel's Lisp course on Udemy</a>
|
||
</div>
|
||
</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>
|