Per's UI blog entriesPer Bothner's bloghttp://per.bothner.com/blog/UI/Per Bothner's blogikiwiki2019-10-16T20:03:28ZTerminal-Shell integration - a proposed specificationhttp://per.bothner.com/blog/2019/shell-integration-proposal/2019-10-16T20:03:28Z2019-06-14T23:27:53Z
<p>
<q>Shell integration</q> refers to a protocol
between a terminal emulator and a shell (or other REPL)
to provide various features, some shown below.
The main idea is that the shell sends extra escape sequences to mark
the various parts of a command: prompt, input, and output.
Terminals that have supported some variation of this concept include
<a href="https://www.xml.com/pub/2000/06/07/xmlterm/index.html">XMLterm</a>,
<a href="https://github.com/mitotic/graphterm">GraphTerm</a>,
<a href="https://github.com/p-e-w/finalterm">FinalTerm</a>,
<a href="https://iterm2.com/documentation-shell-integration.html">Iterm2</a>,
<a href="http://extraterm.org/">ExtraTerm</a>, and
<a href="http://domterm.org/Wire-byte-protocol.html">DomTerm</a>.
These protocols are not consistent and have missing funtionality,
so I have been working on
a <a href="https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md">proposed specification</a>.
<p>
The rest of this article will show some of the supported features,
and discuss some unresolved issues.
If you want to try out the current state, please
<a href="https://github.com/PerBothner/DomTerm">clone DomTerm</a>,
and follow the
<a href="https://domterm.org/Downloading-and-building.html">build instructions</a>.
Best results (and the following screenshots) use the current
<a href="https://github.com/fish-shell/fish-shell/">gitgub master of Fish</a>,
after sourcing <code>tools/shell-integration.fish</code>
(in the DomTerm sources).
<h3>Visually separate commands from each other</h3>
<p>
Shell integration enables each command (with its associated input and output)
to be separated from other commands.
The default DomTerm styling uses a thin partially-transparent green line
you can see in the screenshot below
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/colors1.png" alt="colors1.png" />
<h3>Different styling for prompt, input, and output</h3>
<p>
It is helpful to use a different styling for input and output.
A simple version is to add distinctive styling for the prompt,
but adding styling for entire input lines is even better.
The DomTerm default styling (for light mode) is a pale green background
for the prompt, a light yellow for the actual input, and a slighly darker
yellow for the rest of the input line.
Using pale background colors makes it less likely that it will conflict
with user color preferences, including syntax coloring.
<p>
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/colors2.png" alt="colors2.png" />
<p>
Note that the actual user input are distictively styled,
even spaces. It is helpful to see what are the actual input characters,
to avoid surprises in selection or cursor movement.
The above <code>echo -n foo \ </code> illustrates this -
the input line ends with two spaces:
The final space is removed by the fish parser, but the space before
is escaped by a backslash, as you can see in the output.
(The down-arrow <code>⏎</code> symbol is emitted by fish to indicate
a missing final newline in the output. The output ends in two spaces: one
following <code>foo</code> in the input and one following the backspash.)
<h3>Button to hide/show command output</h3>
<p>One useful feature enabled by shell integration
is a button to hide (or show) the output of a command.
DomTerm puts a downward triangle in the right <q>gutter</q> of the first
line if there is any output or more than one input line.
Clicking on the button hides all but the first line, and changes
the triangle to a right-pointing one
(that can be clicked to restore the hidden lines).
<p>
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/hidden1.png" alt="hidden1.png" />
<p>
DomTerm places the hide/show button in the same <q>right gutter</q>
that is also used to mark wrapped long lines (as shown above).
The same area is also used to show error status (below),
and for the cursor when at the end of a line.
<h3>Show error status</h3>
<p>
When a command finishes, the application can send the exit status
in an escape sequence to the terminal.
DomTerm handles a non-zero exit status by displaying a red circle
in the right gutter of the last line of the command.
(If there is no output, as with the <code>false</code> command,
the circle appears in the last input line.)
Hovering over the red circle displays the exit code.
<p>
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/errors1.png" alt="errors1.png" />
<h3>Move cursor using mouse</h3>
<p>
People new to shells usually expect that they can move the input cursor
using a mouse click, and are surprised when it doesn't work.
Experienced shell users also sometimes want this ability,
for example making an edit to a long command.
This can be implemented using xterm-style mouse handling,
but that is non-trivial to implement.
It also has some undesirable side-effects, including disabling
the default mouse behavior for selection and for context menus.
<p>
A simpler solution is to configure a terminal emulator to map
a mouse click to the appropriate number of cursor-motion keystrokes.
By default, the terminal can send the escape sequences emitted for
the Left and Right arrow keys.
This works well for many unmodified REPL applications.
(However, to avoid surprises, this has to be specifically enabled
by adding a special escape sequence to the prompt.)
<p>
DomTerm also allows you to <q>force</q> mouse-click translation:
If the Alt modifier is pressed during a left mouse-click,
the DomTerm sends the appropriate arrow-key sequence to move the cursor,
even if translation has not been eplicitly requested.
This is useful for an application like <code>vim</code>,
which understands arrow keys but does not have mouse handling by default.
<p>
Motion is easy to handle when moving within a single line.
Some input editors (such as <a href="https://fishshell.com/docs/current/index.html#multiline"><code>fish</code></a>_ support multi-line input areas.
Things get more complicated when the mouse-click is a different line
than the current position. When the terminal calculates the arrow-key
sequence it must be able to
ignore prompts and non-significant spacing, such as the indentation
<code>fish</code> emits, or the space before any right-edge prompt.
Another issue is what kind cursor motion the terminal should send:
For <code>fish</code> you only want to send Left/Right arrows,
since Up/Down moves through the history.
The draft protocol and the DomTerm implementation support multiple options.
<h3>Integrating the selection and clipboard</h3>
<p>
How to integrate the system selection and clipboard with the
input editor raises a number of questions that deserve a separate article.
The way Emacs integrates the "region" with the selection
seems a good approach (but the devil is in the details).
<p>
In DomTerm, if you make a selection that ends inside the input area,
then DomTerm moves the cursor to the end of the selection
(in the same way as if you clicked):
<p>
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/sel1.png" alt="sel1.png" />
<p>
If you type Shift-Right or Shift-Left (optionally with Ctrl)
the selection is extended and the cursor moved to the new endpoint.
For example, typing Ctrl-Shift-Right extends the selection by one word:
<p>
<img src="http://per.bothner.com/blog/2019/shell-integration-proposal/sel2.png" alt="sel2.png" />
<p>
Typing Ctrl-Shift-X copies the selection to the clipboard, and deletes it
by emulating the appropriate number of Delete or Backspace key-presses.
<p>
Coming soon is having Delete or Backspace delete the selection,
without copying to the clipboard.
<h3>Re-flow on window re-size</h3>
<p>Many terminals will automatically re-flow (re-break) wrapped
lines when the window width is changed.
The terminal will also send a notification to the application,
which may also re-display the output to take into account the changed size.
The re-display done by the terminal may be conflict with that
done by the application. For fullscreen applications it's not
a problem because the applications can clear the buffer and re-paint,
which clear any conflicting re-flow done by the terminal.
For shells it is trickier: Suppose the current input fits on one line,
but the window is made narrower. The terminal re-wraps the line as two lines.
Then the application gets the window-changed notification, and
redisplays the input line - but without taking into account the
terminal's re-wrap, making a mess of the display.
<p>This is a hard problem: Having the application assume re-wrap has happened
is unreliable, because another re-size and re-wrap may have happened in
the meanwhile. It is better to disable terminal re-wrap for the current line
(the one containing the cursor), but that breaks in the case of multi-line
input if the application re-sends previous input lines.
<p>
The proposed specification recommends that on re-size the application
re-sends a prompt-start escape sequence, but not the command-start
escape sequence. The terminal can if needed move the cursor
to the first input line when it sees the prompt-start sequence.
This seems to work pretty well.
<p>
(Better would be to let the terminal can handle the re-wrap, and the
application not do re-display at all. This avoids the race condition,
and it makes old input lines wrap the same way as the current line.
However, that may require more radical protocol changes:
It works best if the application can act as if the available
width is unlimited, and delegate line-breaking it to the terminals.)
<h3>Select only real user input</h3>
<p>When selecting and copying text that includes input it is usually
more useful to ignore indentation, prompts, and extra spacing
(such as between user input and a right prompt).
<h3>Right and continution prompts</h2>
Some shells and input libraries (including fish and JLine3) let the
application specify a <q>right prompt</q> displayed at the right of the line.
This may complicate selection/copy, as you may not want to include
the right prompt (especially in the middle of multi-line input),
nor do you want selection to include the spacing before the prompt.
Ideally, reflow after width change should adjust the spacing so the
prompt of old input lines stays at the right. (This is not yet implemented.)
Fish will only display the right prompt if there enough space;
ideally this should be taken account on reflow.
JavaScript menus for native-looking applicationshttp://per.bothner.com/blog/2018/jsMenus/2018-07-29T23:11:04Z2018-07-29T21:09:43Z
<p>
Using web technologies for the GUI of a desktop application
makes a lot of sense: they're portable, familiar, and powerful.
A popular option is to use a comprehensive framework such as
<a href="https://electronjs.org/">Electron</a>.
However, you might want the option of using a regular desktop browser —
maybe you want to be able to access your application remotely,
or you might prefer a lighter-weight embedded browser such as
<a href="https://github.com/zserge/webview/">webview</a>.
<p>
For a native-looking application you probably want menus:
a top menubar as well as a pop-up (context) menu.
While the various embedded platforms
(such as Electron) each have their own APIs for menus,
there is no standard JavaScript API.
The <a href="https://domterm.org">DomTerm terminal emulator</a>
can be run with a small Electron wrapper
(in which case it uses Electron menus), but it can also run
in a plain browser like Firefox or Chrome.
So I looked around for a lightweight menu library that would
work in a desktop browser, but I didn't find anything I liked:
They were unmaintained, or too big, or depended on some other library
(such as jQuery), or were incomplete (popup menus but no menubar
support, or missing keyboard navigation), or had an
API too different from Electron's relatively simple one.</p>
<p>
I finally found Sam Wray's <a href="https://github.com/2xAA/nwjs-menu-browser">nwjs-menu-browser</a>, which satisfied most of
the goals.
It was strandalone, modest size, handled menubars as well
as popups, and had an API similar to NW.js (which is similar to that of Electron).
However, it had some issues. Missing features included keyboard navigation,
inability to <q>share</q> menu-items between menus,
and some smaller problems.
I also found the build process too complicated for me.</p>
<p>
The <a href="https://github.com/PerBothner/jsMenus">jsMenus</a> library is a much-changed fork of nwjs-menu-browser.
The functionality (and API) are very similar to that of Electron.
In fact DomTerm uses much of the <a href="https://github.com/PerBothner/DomTerm/blob/master/hlib/domterm-menus.js">same JavaScript</a> to construct menus
whether using Electron or jsMenus.
To see most of the features in action, check this
<a href="https://rawgit.com/PerBothner/jsMenus/master/demo.html">live demo</a>.
It should be easy to figure out how it works from the <a href="https://github.com/PerBothner/jsMenus/blob/master/demo.html">demo.html source</a>. You can also read the <a href="https://github.com/PerBothner/jsMenus/blob/master/Documentation.md">API documentation</a>.</p>
<p>
Finally, here is screenshot of jsMenus in action. This is DomTerm,
using the Chrome browser.
Chrome was started with the <code>--app</code> command-line option,
which (among other things) disables the default menubar and location bar.</p>
<img src="http://per.bothner.com/blog/2018/jsMenus/domterm-context-menu.png" alt="domterm-context-menu.png" />
DomTerm terminal emulator now has draggable panes and tabshttp://per.bothner.com/blog/2017/domterm-draggable-panes/2017-07-06T17:18:08Z2017-07-06T03:46:43Z
<p>
The <a href="http://domterm.org/">DomTerm</a> terminal emulator
now supports sub-windows (panes) and tabs.
These are resizable and draggable.
This is implemented using the
<a href="http://golden-layout.com/">GoldenLayout</a> JavaScript library.
<p>
The screenshot below shows 3 sub-windows.
The lower one has two tabs, domterm-2 and domterm-3.
The latter has focus (indicated by the magenta border),
and is running the <code>mc</code> file manager.
The upper left pane (domterm-1) shows how to embed html from the shell.
The domterm-4 pane shows using Kawa to
create <a href="https://www.gnu.org/software/kawa/Composable-pictures.html">composable <q>picture values</a></a>
which are displayed using embedded SVG.
<p>
You can resize panes by dragging the separator between them, and
you can re-arrange panes or tabs by dragging a title bar.
The screenshot shows as we are dragging the splitter between the
two upper panes; the blue rectangles temporarily display the resulting sizes.
<p>
<img src="http://per.bothner.com/blog/2017/domterm-draggable-panes/domterm-panes-2.png" alt="domterm-panes-2.png" />
<p>
Compared to
<a href="https://www.gnu.org/software/screen/">GNU screen</a>
or <a href="https://github.com/tmux/tmux/wiki">tmux</a>,
DomTerm supports more flexible layouts and easier manipulation
using either mouse or keyboard.
However, DomTerm does not yet support sessions that can
be detached or accessed by multiple users at once,
though I do hope to add it.
Until then, I suggest using
<a href="http://www.brain-dump.org/projects/abduco/">abduco</a>
or <a href="http://dtach.sourceforge.net">dtach</a>
to handle session management.
Dynamic Pretty-printing in DomTermhttp://per.bothner.com/blog/2017/dynamic-prettyprinting/2017-02-03T20:13:58Z2017-01-30T21:23:48Z
<p>
The <a href="http://domterm.org/">DomTerm</a> terminal emulator
has a number of <a href="http://domterm.org/Features.html">unique features</a>.
In this article we will explore how it enables dynamic re-flow for
Lisp-style pretty-printing.
<p>
The goal of <dfn><a href="https://en.wikipedia.org/wiki/Prettyprint">pretty-printing</a></dfn> is to split a text into lines with appropriate indentation,
in a way that conforms to the logical structure of the text.
<p>
For example if we need to print the following list:
<pre>
((alpha-1 alpha-2 alpha-3) (beta-1 beta-2 beta-3 beta-4))
</pre>
in a window 40 characters wide, we want:
<pre>
((alpha-1 alpha-2 alpha-3)
(beta-1 beta-2 beta-3 beta-4))
</pre>
but not:
<pre>
((alpha-1 alpha-2 alpha-3) (beta-1
beta-2 beta-3 beta-4))
</pre>
<p>
Pretty-printing is common in Lisp environments to display complicated
nested data structures. Traditionally, it is done by the
programming-language runtime, based on a given line width.
However, the line width of a console can change if the
user resizes the window or changes font size. In that case,
previously-emitted pretty-printed lines will quickly become ugly:
If the line-width decreases, the breaks will be all wrong and the
text hard to read; if the line-width increases we may be using
more lines than necessary.
<p>
Modern terminal emulators do <q>dumb</q> line-breaking: Splitting a long
lines into screen lines, but regardless of structure or even word boundaries.
Some emulators remember for each line whether an overflow happened, or
whether a <q>hard</q> newline was printed.
Some terminal emulators (for example Gnome Terminal) will use this
to re-do the splitting when a window is re-sized.
However, that does not help with pretty-printer output.
<p>
Until now.
Below is a screenshot from Kawa running in DomTerm at 80 colutions.
<br />
<img src="http://per.bothner.com/blog/2017/dynamic-prettyprinting/pprint-1.png" alt="pprint-1.png" />
<p>
We reduce the window size to 50 columns.
The user input (yellow background) is raw text, so its line is split <q>non-pretty</q>,
but the output (white background) gets <q>pretty</q> re-splitting.
(Note the window size indicator in the lower-right.)
<br />
<img src="http://per.bothner.com/blog/2017/dynamic-prettyprinting/pprint-2.png" alt="pprint-2.png" />
<p>
We reduce the window size to 35 columns:
<br />
<img src="http://per.bothner.com/blog/2017/dynamic-prettyprinting/pprint-3.png" alt="pprint-3.png" />
<h3>It also works with saved pages</h3>
<p>
DomTerm allows you to save the current session as
a static HTML page.
If the needed DomTerm CSS and JavaScript files are provided
in the <code>hlib</code> directory, then dynamic line-breaking
happens even for saved <q>log</q> files.
(The lazy way is to create <code>hlib</code> as a symbolic link
to the <code>hlib</code> directory of the DomTerm distribution.)
<p>
Try it yourself on a <a href="http://per.bothner.com/static/2017/domterm-saved-1.html">saved session</a>.
The <code>--debug-print-expr</code> flag causes Kawa to print out each command
before it is compiled and evaluated. The result (shown in red because it
is sent to the standard error stream) is pretty-printed dynamically.
<h3>Structured text</h3>
<p>
This is how it works.
<p>
When an application pretty-prints a structure, it calls
<a href="https://www.gnu.org/software/kawa/Pretty-printing.html">special output
procedures</a> to mark which parts of the output logically belong
together (a <q>logical block</q>),
and where line-breaks and indentation may be inserted.
In Kawa the default print formatting for lists and vectors automatically calls
these procedures when the output is a <q>pretty-printing stream</q>.
The pretty-printing library calculates where to put line-breaks and indentation,
based on these commands and the specified line length.
<p>
However, when the output stream is a DomTerm terminal,
Kawa's pretty-printing library does not actually calculate the line-breaks.
Instead it encodes the above-mentioned procedure calls
as special <a href="http://domterm.org/Wire-byte-protocol.html#escapes-pretty-printing">escape sequences</a>
that get sent to DomTerm.
<p>
When DomTerm receives these escape sequences, it builds a
<a href="http://domterm.org/DOM-structure.html#DOM-pretty-printing">nested DOM structure</a> that corresponds to the orginal procedure calls.
DomTerm calculates how to layout that structure using a variant
of the Common Lisp pretty-printing algorithm, inserting soft left-breaks and indentation
as needed.
<p>
When DomTerm detects that its window has been re-sized or zoomed,
it first removes old soft line-breaks and identation. It does re-runs the layout
algorithm.
<p>
When a page is saved, the nested DOM structure is written out too.
If the saved page is loaded in a browser, and the necessary JavaScript libraries are
available, then the pretty-printing algorithm is run on the saved page,
both on initial load, and whenever the window is re-sized.
<h3>Hyphenation and word-breaking</h3>
DomTerm supports general soft (optional) line breaks:
You can specify separate texts (optionally with styling) for each of the following:
The text used when the line is not broken at that point; the text to use
before the line-break, when broken (commonly a hyphen); the text to use
after the line-break (following indentation).
One use for this words that change their spelling when hyphenated, as may
happen in German. For example the word <q>backen</q> becomes <q>bak-ken</q>.
You can handle this using <q><code>ck</code></q> as the non-break text;
<q><code>k-</code></q> as the pre-break text; and <q><code>k</code></q> as the post-break text.
Gnuplot display on DomTermhttp://per.bothner.com/blog/2016/gnuplot-in-domterm/2016-09-22T06:06:05Z2016-08-19T03:45:56Z
<p>
I have recently spent a lot of time on
<a href="http://domterm.org">DomTerm</a>, which
is becoming a fairly decent terminal emulator.
Being based on HTML5 technologies lets you do many
interesting things, including embed graphics.
The new <code>qtdomterm</code> standalone terminal
emulator is especially nice;
alternatively, you can also use the <code>domterm --browser</code>
command to start a process that uses a window/tab in your default
desktop browser.
<p>
The <a href="http://www.gnuplot.info/">gnuplot</a>
package is a powerful graphing package.
It has a command-line that lets you specify complicated
functions and plots, and then give a <code>plot</code> command
to display the resulting plot. Unfortunately, it is difficult
to find a a good front-end that is usable for both command-line
interaction <em>and</em> graphics. People usually have to
specify output to either a file or an external viewer.
Would that one could insert the plots directly in the REPL output stream!
<p>
The development version of gnuplot (i.e. 5.1, available from CVS)
has native support for DomTerm.
It has <code>domterm</code> as a new terminal type, which you can select
explicitly (with the command <code>set term domterm</code>),
or use by default (since <code>gnuplot</code>
checks the <code>DOMTERM</code> environment variable,
which is set by DomTerm).
<p>
This works by using the pre-existing <code>svg</code> output driver
to generate SVG, but surrounding the SVG by escape sequences, and then printing
the result to standard output. DomTerm recognizes the escape sequence,
extracts the SVG (or more generally clean HTML) and inserts it
<q>at the cursor</q>.
<p>
You can save the session output to an html file.
Here is <a href="http://per.bothner.com/static/2016/gnuplot-1.html">an example</a>.
In qtdomterm you can use the <code>File</code> / <code>Save As</code> menu item;
otherwise ctrl-shift-S should work.
This is a single html file, with embedded SVG;
images are embedded with a <code>data:</code> URL.
The file is actually XML (xhtml), to make it easier to process.
The saved file does need to css and js (JavaScript) files to be
readable, so you need to link or copy
the <code>hlib</code> directory in the DomTerm source distribution.
<p>
<img src="http://per.bothner.com/blog/2016/gnuplot-in-domterm/1.png" alt="1.png" />
Scripting API for compiled JavaFXhttp://per.bothner.com/blog/2009/JavaFX-scripting-changes/2009-03-02T18:12:38Z2009-02-27T00:52:50Z
<p>
The new <a href="http://per.bothner.com/blog/2009/REPL-for-JavaFX/">read-eval-print-loop (REPL) for JavaFX</a>
required some major changes in the <q>scripting</q> API
for JavaFX.
JavaFX had for a while supported the <a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html"><code>javax.script</code> API</a>
specified by <a href="http://jcp.org/en/jsr/detail?id=223">JSR-223</a>.
However, that JavaFX implementation didn't allow you to declare a variable in
one "eval" and make it visible in future commands, which made it
rather useless for a REPL.
This turns out to be somewhat tricky for a
compiled language with static name binding and typing.
Unfortunately, JSR-223 has some fundamental design
limitations in that respect.
(Embarrassingly, I was a not-very-active member of the JSR-223 expert group.
It would have been better if we could have thought a little more about
scripting staticly-typed languages, but we didn't have time.)
<h3>Design limitations in <code>javax.script</code></h3>
<p>
Top-level name-to-value bindings in JSR-223 are
stored in a <code>javax.script.Bindings</code> instance, which is
basically a <code>Map</code>. Unfortunately, <code>Bindings</code> is a
concrete class that the programmer can directly instantiate
and add bindings to, and there is no way to synchronize
a <code>Bindings</code> instance with the internal evaluator state.
(Note that top-level bindings have to be maintained in a language-dependent
data structure if the language supports static typing, since typing
is language-dependent.) This means the script engine has to
explicitly copy every binding from the <code>Bindings</code> object
to the language evaluator - or the script evaluator has to be
modified to search the <code>Bindings</code>. Worse, if a script
modifies or declares a binding, that has to be copied back to
the <code>Bindings</code>, which is an even bigger pain.
<p>
This problem could have been avoided
if <code>Bindings</code> were an abstract class to be implemented
by the language script engine.
An alternative solution could allow the evaluator to register
itself as as <q>listener</q> on the <code>Bindings</code> object.
<p>
JSR-223 does support compilation being separate from evaluation.
Unfortunately, it assumes that name-to-value bindings are needed
during evaluation but not during compilation.
This is problematic in a language with lexical binding, and
fails utterly in a language with static typing: The types of
bindings have to be available at compile-time, not just run-time.
<p>
The <code>Invocable</code> interface is also problematical.
It invokes a function or method in a <code>ScriptEngine</code>,
without passing in a <code>ScriptContext</code>, even though
invoking a function is basically evaluation, which should require
a <code>ScriptContext</code>.
<p>
The <code>ScriptContext</code> interface has an extra
complication in that it consists of two <code>Bindings</code> objects,
of which the <q>global</q> one seems to have limited utility.
Making it worse is that some methods use
the default <code>ScriptContext</code> of a <code>ScriptEngine</code>
and some take an explicit <code>ScriptContext</code> parameter.
<h3>The new JavaFX scripting API</h3>
<p>
Because of these problems with <code>javax.script</code>,
I implemented a new JavaFX-specific API.
These classes are currently in the <code>com.sun.tools.javafx.script</code>
package; after some <q>shaking down</q> they might be moved to
some other package, perhaps <code>javafx.script</code>.
<p>
The class <code>JavaFXScriptCompiler</code> manages the <q>compilation
context</q>, including the typed bindings preserved between
compilations, and a <code>MemoryFileManager</code> pseudo-filesystem
that saves the bytecode of previously compiled commands.
<p>
The main method of <code>JavaFXScriptCompiler</code> is <code>compile</code>.
This takes a script, compiles it, and (assuming no errors) returns
a <code>JavaFXCompiledScript</code>.
<p>
The main method of <code>JavaFXCompiledScript</code> is <code>eval</code>,
which takes a <code>JavaFXScriptContext</code>, evaluates the script,
and returns the result value.
<p>
The <code>JavaFXScriptContext</code> contains the evaluation state,
which is primarily a <code>ClassLoader</code>. Creating a <code>JavaFXScriptContext</code>
automatically creates a <code>JavaFXScriptCompiler</code>, so in the
current implementation there is a one-to-one relationship between
<code>JavaFXScriptContext</code> and <code>JavaFXScriptCompiler</code>.
(In the future we might allow multiple <code>JavaFXScriptContext</code>
instances to share a single <code>JavaFXScriptCompiler</code>.)
<p>
This API is very much subject to change.
<h3>The REPL API</h3>
<p>
The actual REPL is provided by the <code>ScriptShell</code> class,
which is implemented using the above API. It first reads in commands
from the command line (the <code>-e</code> option), or a named
file (the <code>-f</code> option). It then enters the classic
REPL loop: Print a prompt, read a line, evaluate it, and
print the result.
<p>
Most of these behaviors can be customized by overriding
a method. For example when the compiler finds an error it creates a
<code>Diagnostic</code> object, which is passed to a <code>report</code> method.
The default handling
is to print the <code>Diagnostic</code>, but a sub-class of
<code>ScriptShell</code> could override <code>report</code> to do
something else with the <code>Diagnostic</code>.
<p>
The <code>ScriptShell</code> API is also very much subject to change
as we get more experience with it.
<h3>The new <code>javax.script</code> wrapper</h3>
<p>
The class <code>JavaFXScriptEngineImpl</code> is the JavaFX implementation
of the standard <code>javax.script.AbstractScriptEngine</code> class.
It was re-written to be a wrapper on top of the API discussed earlier.
Because of the previously-mentioned limitations of <code>javax.script</code>
it is not completely faithful implementation of JSR-223.
It uses a <code>WeakHashMap</code> to translate from the
<code>Bindings</code> used by the <code>javax.script</code> API
into the <code>JavaFXScriptContext</code> objects used by the
internal API. Compilation and evaluation of a script need to use
<code>JavaFXScriptContext</code> consistently.
This means there are basically two usage modes that should work:
<ul>
<li>
It is best to always use the default <code>ScriptContext</code> as returned by
the <code>ScriptEngine</code>'s <code>getContext</code> method. Don't call
<code>createBindings</code>, <code>getBindings</code>,
<code>setBindings</code>, or <code>setContext</code>.
<li>
If you really need to use any of the above four methods, don't
try to use either of the <code>compile</code> methods.
The reason for this is that compilation does need to use the
script context, and it should be the same as used for evaluation.
</li>
A read-eval-print-loop for JavaFXhttp://per.bothner.com/blog/2009/REPL-for-JavaFX/2009-02-28T01:38:08Z2009-02-27T00:51:28Z
JavaFX now supports
a <a href="http://en.wikipedia.org/wiki/REPL">read-eval-print-loop ("repl")</a>.
This will be in the next ("Marina") release, but you can get it now
from the <a href="https://kenai.com/hg/openjfx-compiler~marina-master">Mercurial repository</a> - see
the <a href="https://openjfx-compiler.dev.java.net/">compiler home page</a>,
which links to <a href="https://openjfx-compiler.dev.java.net/usingKenaiAndMercurial.html">these instructions</a>.
<p>
A read-eval-print-loop is a well-known feature of many language
implementations, but isn't that common with compiled languages,
especially ones that have static typing and lexical name lookup.
<p>See this companion article on the <a href="http://per.bothner.com/blog/2009/JavaFX-scripting-changes/">scripting API and implementation issues</a>.
<p>
The class <code>com.sun.tools.javafx.script.ScriptShell</code> implements the
actual repl. It is an application that was based on
the language-independent <a href="http://java.sun.com/javase/6/docs/technotes/tools/share/jrunscript.html"><code>jrunscript</code></a> command.
but you can also subclass it if you want to customize it.
<p>
To run the REPL, start the <code>ScriptShell</code> application - for example:
<pre>
$ CLASSPATH=/tmp:dist/lib/shared/javafxc.jar \
java com.sun.tools.javafx.script.ScriptShell
</pre>
<p>
Then you type commands at it:
<pre>
/*fx1*/ var xy = 12
12
/*fx2*/ xy+10
12
</pre>
The prompt has the form of a comment, to make it easier to
cut-and-paste. The name inside the comments is used as
the name of a "scriptlet" which shows up in error messages
and exceptions.
<p>
You can define functions, modify variables, and call functions:
<pre>
/*fx3*/ function xy_plus (n:Integer):Integer { xy + n }
/*fx4*/ ++xy
13
/*fx5*/ xy_plus(100)
113
</pre>
<p>
You get warnings:
<pre>
/*fx6*/ xy=2.5
fx6:1: possible loss of precision
found : Number
required: Integer
2
</pre>
<p>
and errors:
<pre>
/*fx7*/ xy="str"
fx7:1: incompatible types
found : String
required: Integer
</pre>
<p>
and exceptions:
<pre>
/*fx8*/ xy/0
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.sun.tools.javafx.script.JavaFXCompiledScript.eval(JavaFXCompiledScript.java:59)
at com.sun.tools.javafx.script.ScriptShell.evaluate(ScriptShell.java:350)
at com.sun.tools.javafx.script.ScriptShell.evaluateString(ScriptShell.java:311)
at com.sun.tools.javafx.script.ScriptShell.processSource(ScriptShell.java:244)
at com.sun.tools.javafx.script.ScriptShell$1.run(ScriptShell.java:177)
at com.sun.tools.javafx.script.ScriptShell.main(ScriptShell.java:51)
Caused by: java.lang.ArithmeticException: / by zero
at fx8.javafx$run$(fx8]:1)
... 10 more
</pre>
<p>
Each <q>command</q> is single line. In the future I hope we'll be able to
tell when a command is <q>incomplete</q>, so the reader can keep reading
more lines until it has a complete command.
<h3>Re-definition</h3>
You can redefine variables (and functions and classes),
but previously-compiled references refer to
the earlier definition:
<pre>
/*fx9*/ var xy:String="str"
str
/*fx10*/ xy
str
/*fx11*/ xy_plus(100)
102
</pre>
<p>
Now this is <q>correct</q> in terms of static name-binding,
but perhaps not the most useful. What you probably want
is for <code>xy_plus</code> to use the new definition of <code>xy</code>.
This is what would happen in a typical dynamic language with
dynamic typing and dynamic name-lookup, but with
of compilation and static name-lookup <code>xy_plus</code> uses the
the defintion seen when it was compiled.
<p>
<q>Fixing</q> this would involve some combination of automatic
re-compilation (if <code>xy_plus</code> depends on <code>xy</code>,
and <code>xy</code> is redefined, then re-compile <code>xy_plus</code>),
and extra indirection.
<p>
A related issue is that forward references aren't allowed,
which means you can't declare mutually dependent functions or classes - unless
you type them all in the same command - i.e. on one line!
Automatic recompilation could alleviate this: When a command is compiled, we
not only remember the declarations it depends on, but also any missing symbols,
so we can re-compile it if those later get declared.
<h3>Memory and start-up issues</h3>
<p>Be aware that executing the first command causes a
noticable pause. While I haven't profiled this, I'm guessing
it is just because we need to load most of <code>javafxc</code> (which
includes most of (a hacked-up version of) <code>javac</code>), and that takes
some time. Subsequent commands run much faster.
<p>
Right now we don't do any smart reclaiming of classes.
Each command compiles into one or more classes, which are
kept around because they may be needed for future commands.
Also, the bytecode array of a command is also kept around,
since it may be needed when compiling future commands.
Hopefully in the future we'll be more clever, so we
can get-rid of no-longer-useful classes.
A Swing-based read-eval-print interfacehttp://per.bothner.com/blog/2007/ReplPane/2007-09-11T15:52:50Z2007-09-11T15:52:50Z
<p>
I've long been interested in improved commend-line interfaces.
The latest <a href="http://www.gnu.org/software/kawa/Getting-Kawa.html">Kawa (in SVN)</a> has a new implementation
of a command interface window. If you start up Kawa
with the -w flag:</p>
<pre>
kawa -w
</pre>
<p>you get a new Swing <code>JFrame</code>, whose
interesting component is a <code>JTextPane</code>
for typing in expressions, and displaying the results.
The following example uses Kawa's default language Scheme,
but works for other Kawa languages, including XQuery.</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane1.png" alt="ReplPane1.png" /></p>
<h3>Color coding</h3>
<p>
Here the command prompt is in green, and the input command is
in bold-face, because it seems useful to emphasize the input
as a way of visually separating one command from the previous.
The standard error output is in red.
Standard output has the plain default style.
This is because standard output may have embedded in it other
objects and nested styled text.</p>
<p>
All of these are implemented using Swing style objects.
There is currently no mechanism to modify one these styles,
except by modifying the source code, but at some point we will support that.</p>
<h3>Input editing</h3>
<p>
While the color-coding is pretty, even more important is
command-line editing. That is you can move the input cursor
along the input line, and insert, delete, or replace text
before hitting <code>Enter</code>.
This is previously available using
the <a href="http://tiswww.case.edu/php/chet/readline/rltop.html">GNU readline</a> library, which has some nice features, including
a searchable history mechanism. However, you cannot move the
cursor using the mouse, which is surprising to many.</p>
<p>Currently, the entire text pane is editable using the
default <code>JTextPane</code> keystrokes and mouse handlers.
(It would be better that at least the prompt be non-editable.)
Nothing gets sent to the receiving reader until you type
<code>Enter</code>. At that point the entire line containing
the cursor, except for the prompt, is sent to the reader.
(More precisely: If the cursor is at or after the output position,
the rest of the line after the output position is sent.
Otherwise, usually it's a way of repeating or modifying a previous
line. In that case the contents of that line, except any initial
prompt segment, are first copied to the end of the text buffer,
as if typed there, before being sent to the reader.)</p>
<p>If you paste a multi-line string (commonly using ctrl-V),
then <code>ReplPane</code> magically interleaves the prompt string and
output with the input text. For example if you paste the following text:</p>
<pre>
(display "foo")
(newline)
(display
"bar")
(newline)
</pre>
<p>You get this result:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-multiline1.png" alt="ReplPane-multiline1.png" /></p>
<p>Note that the prompt for line 4 is different because it's a continuation
line.
(Alas, this feature is not very robust; it is easy to get
Swing exceptions.)</p>
<h3>Embedding Components</h3>
<p>A powerful feature is that you "print" <code>java.awt.Component</code>
objects. These are embedded in the <code>JTextPane</code>.
The following Scheme example creates a list of 3 <code>JButton</code>
objects, and that list is then "printed":</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane2.png" alt="ReplPane2.png" /></p>
<p>We can of course save a reference to the button in a variable:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-but1a.png" alt="ReplPane-but1a.png" /></p>
<p>That allows us to modify properies of the button.
Here we change its <code>text</code> property, in line 4,
and the button is updated as soon as we hit enter:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-but1b.png" alt="ReplPane-but1b.png" /></p>
<p>One anomaly when "printing" a <code>Component</code>
is that a <code>Component</code> can only appear once.
If we "print" it a second time, it is as a side-effect
removed from the first place it was printed:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-but1c.png" alt="ReplPane-but1c.png" /></p>
<p>
This isn't really the right behavior, but it's unavoidable
when "printing" a <code>Component</code>.
To avoid the problem, we need to "print" a some kind of
"model" object rather than "view" objects.</p>
<h3>Embedding Viewable objects</h3>
<p>The experimental <code>swing-gui</code> library provides
"viewable model" objects that can also be "printed" to a <code>TextPane</code>.
For example, the <a href="http://www.gnu.org/software/kawa/api/gnu/kawa/models/Button.html"><code>gnu.kawa.models.Button</code></a> class
goes beyond Swing's <code>ButtonModel</code> in also having
text and an action procedure.
When a <code>gnu.kawa.models.Button</code> is "printed" the
implementation automatically creates a <code>JButton</code> that
is co-ordinated with the <code>gnu.kawa.models.Button</code>:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-but2a.png" alt="ReplPane-but2a.png" /></p>
<p>Again, we can change the text property:</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-but2b.png" alt="ReplPane-but2b.png" /></p>
<p>Here we do some more complex layout,
creating a <code>Row</code> displaying the same button twice,
separated by a spacer. Then we create a column
that displays the same row twice, separated by a text field.
(For some reason the text field isn't displaying properly.)</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-col2.png" alt="ReplPane-col2.png" /></p>
<h3>Playing with composable Java2D pictures</h3>
<p>The <code>swing-gui</code> library also provides
convenience wrappers to the
<a href="http://www.gnu.org/software/kawa/api/gnu/kawa/models/Paintable.html"><code>gnu.kawa.models.Paintable</code></a> interface,
which makes it easy to create, compose, and transform Java2D <code>Shape</code>
objects.</p>
<p><img src="http://per.bothner.com/blog/2007/ReplPane-2D1.png" alt="ReplPane-2D1.png" /></p>
<h3>Issues</h3>
<ul>
<li>The <code>ReplPane</code> is not very solid. It's not hard to type
something which causes a Swing exception, which is usually not recoverable.</li>
<li>There should be a <code>View</code> menu to modify fornts and styles,
and a standard <code>Edit</code> menu to Cut/Copy/Paste, at least.
The <code>File</code> menu should have a way to save the typescript.</li>
<li>The <code>Utilities->Purge Buffer</code> menu item doesn't work.</li>
<li>The display should be integrated with the Kawa pretty-printer,
so that changing the window width recalculates line-breaks.</li>
<li>It would be nice to emit styled "inline" (text) objects,
such as a "red string".
Maybe we should be using Swing's <code>HTMLDocument</code>
instead of <code>DefaultStyledDocument</code>.
Another toolkit to consider
is <a href="https://xhtmlrenderer.dev.java.net/">Flying Saucer</a>.</li>
</ul>