Per's UI blog entries

The DomTerm terminal emulator now supports sub-windows (panes) and tabs. These are resizable and draggable. This is implemented using the GoldenLayout JavaScript library.

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 mc 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 composable picture values which are displayed using embedded SVG.

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.

domterm-panes-2.png

Compared to GNU screen or tmux, 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 abduco or dtach to handle session management.

Created 5 Jul 2017 20:46 PDT. Last edited 6 Jul 2017 10:18 PDT. Tags:

The DomTerm terminal emulator has a number of unique features. In this article we will explore how it enables dynamic re-flow for Lisp-style pretty-printing.

The goal of pretty-printing is to split a text into lines with appropriate indentation, in a way that conforms to the logical structure of the text.

For example if we need to print the following list:

((alpha-1 alpha-2 alpha-3) (beta-1 beta-2 beta-3 beta-4))
in a window 40 characters wide, we want:
((alpha-1 alpha-2 alpha-3)
 (beta-1 beta-2 beta-3 beta-4))
but not:
((alpha-1 alpha-2 alpha-3) (beta-1
 beta-2 beta-3 beta-4))

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.

Modern terminal emulators do dumb 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 hard 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.

Until now. Below is a screenshot from Kawa running in DomTerm at 80 colutions.
pprint-1.png

We reduce the window size to 50 columns. The user input (yellow background) is raw text, so its line is split non-pretty, but the output (white background) gets pretty re-splitting. (Note the window size indicator in the lower-right.)
pprint-2.png

We reduce the window size to 35 columns:
pprint-3.png

It also works with saved pages

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 hlib directory, then dynamic line-breaking happens even for saved log files. (The lazy way is to create hlib as a symbolic link to the hlib directory of the DomTerm distribution.)

Try it yourself on a saved session. The --debug-print-expr 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.

Structured text

This is how it works.

When an application pretty-prints a structure, it calls special output procedures to mark which parts of the output logically belong together (a logical block), 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 pretty-printing stream. The pretty-printing library calculates where to put line-breaks and indentation, based on these commands and the specified line length.

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 escape sequences that get sent to DomTerm.

When DomTerm receives these escape sequences, it builds a nested DOM structure 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.

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.

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.

Hyphenation and word-breaking

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 backen becomes bak-ken. You can handle this using ck as the non-break text; k- as the pre-break text; and k as the post-break text.
Created 30 Jan 2017 13:23 PST. Last edited 3 Feb 2017 12:13 PST. Tags:

I have recently spent a lot of time on DomTerm, which is becoming a fairly decent terminal emulator. Being based on HTML5 technologies lets you do many interesting things, including embed graphics. The new qtdomterm standalone terminal emulator is especially nice; alternatively, you can also use the domterm --browser command to start a process that uses a window/tab in your default desktop browser.

The gnuplot package is a powerful graphing package. It has a command-line that lets you specify complicated functions and plots, and then give a plot 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 and 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!

The development version of gnuplot (i.e. 5.1, available from CVS) has native support for DomTerm. It has domterm as a new terminal type, which you can select explicitly (with the command set term domterm), or use by default (since gnuplot checks the DOMTERM environment variable, which is set by DomTerm).

This works by using the pre-existing svg 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 at the cursor.

You can save the session output to an html file. Here is an example. In qtdomterm you can use the File / Save As menu item; otherwise ctrl-shift-S should work. This is a single html file, with embedded SVG; images are embedded with a data: 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 hlib directory in the DomTerm source distribution.

1.png

Created 18 Aug 2016 20:45 PDT. Last edited 21 Sep 2016 23:06 PDT. Tags:

The new read-eval-print-loop (REPL) for JavaFX required some major changes in the scripting API for JavaFX. JavaFX had for a while supported the javax.script API specified by JSR-223. 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.)

Design limitations in javax.script

Top-level name-to-value bindings in JSR-223 are stored in a javax.script.Bindings instance, which is basically a Map. Unfortunately, Bindings is a concrete class that the programmer can directly instantiate and add bindings to, and there is no way to synchronize a Bindings 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 Bindings object to the language evaluator - or the script evaluator has to be modified to search the Bindings. Worse, if a script modifies or declares a binding, that has to be copied back to the Bindings, which is an even bigger pain.

This problem could have been avoided if Bindings were an abstract class to be implemented by the language script engine. An alternative solution could allow the evaluator to register itself as as listener on the Bindings object.

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.

The Invocable interface is also problematical. It invokes a function or method in a ScriptEngine, without passing in a ScriptContext, even though invoking a function is basically evaluation, which should require a ScriptContext.

The ScriptContext interface has an extra complication in that it consists of two Bindings objects, of which the global one seems to have limited utility. Making it worse is that some methods use the default ScriptContext of a ScriptEngine and some take an explicit ScriptContext parameter.

The new JavaFX scripting API

Because of these problems with javax.script, I implemented a new JavaFX-specific API. These classes are currently in the com.sun.tools.javafx.script package; after some shaking down they might be moved to some other package, perhaps javafx.script.

The class JavaFXScriptCompiler manages the compilation context, including the typed bindings preserved between compilations, and a MemoryFileManager pseudo-filesystem that saves the bytecode of previously compiled commands.

The main method of JavaFXScriptCompiler is compile. This takes a script, compiles it, and (assuming no errors) returns a JavaFXCompiledScript.

The main method of JavaFXCompiledScript is eval, which takes a JavaFXScriptContext, evaluates the script, and returns the result value.

The JavaFXScriptContext contains the evaluation state, which is primarily a ClassLoader. Creating a JavaFXScriptContext automatically creates a JavaFXScriptCompiler, so in the current implementation there is a one-to-one relationship between JavaFXScriptContext and JavaFXScriptCompiler. (In the future we might allow multiple JavaFXScriptContext instances to share a single JavaFXScriptCompiler.)

This API is very much subject to change.

The REPL API

The actual REPL is provided by the ScriptShell class, which is implemented using the above API. It first reads in commands from the command line (the -e option), or a named file (the -f option). It then enters the classic REPL loop: Print a prompt, read a line, evaluate it, and print the result.

Most of these behaviors can be customized by overriding a method. For example when the compiler finds an error it creates a Diagnostic object, which is passed to a report method. The default handling is to print the Diagnostic, but a sub-class of ScriptShell could override report to do something else with the Diagnostic.

The ScriptShell API is also very much subject to change as we get more experience with it.

The new javax.script wrapper

The class JavaFXScriptEngineImpl is the JavaFX implementation of the standard javax.script.AbstractScriptEngine class. It was re-written to be a wrapper on top of the API discussed earlier. Because of the previously-mentioned limitations of javax.script it is not completely faithful implementation of JSR-223. It uses a WeakHashMap to translate from the Bindings used by the javax.script API into the JavaFXScriptContext objects used by the internal API. Compilation and evaluation of a script need to use JavaFXScriptContext consistently. This means there are basically two usage modes that should work:

  • It is best to always use the default ScriptContext as returned by the ScriptEngine's getContext method. Don't call createBindings, getBindings, setBindings, or setContext.
  • If you really need to use any of the above four methods, don't try to use either of the compile 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.
Created 26 Feb 2009 16:52 PST. Last edited 2 Mar 2009 10:12 PST. Tags:
JavaFX now supports a read-eval-print-loop ("repl"). This will be in the next ("Marina") release, but you can get it now from the Mercurial repository - see the compiler home page, which links to these instructions.

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.

See this companion article on the scripting API and implementation issues.

The class com.sun.tools.javafx.script.ScriptShell implements the actual repl. It is an application that was based on the language-independent jrunscript command. but you can also subclass it if you want to customize it.

To run the REPL, start the ScriptShell application - for example:

$ CLASSPATH=/tmp:dist/lib/shared/javafxc.jar \
  java com.sun.tools.javafx.script.ScriptShell

Then you type commands at it:

/*fx1*/ var xy = 12
12
/*fx2*/ xy+10
12
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.

You can define functions, modify variables, and call functions:

/*fx3*/ function xy_plus (n:Integer):Integer { xy + n }
/*fx4*/ ++xy
13
/*fx5*/ xy_plus(100)
113

You get warnings:

/*fx6*/ xy=2.5
fx6:1: possible loss of precision
found   : Number
required: Integer
2

and errors:

/*fx7*/ xy="str"
fx7:1: incompatible types
found   : String
required: Integer

and exceptions:

/*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

Each command is single line. In the future I hope we'll be able to tell when a command is incomplete, so the reader can keep reading more lines until it has a complete command.

Re-definition

You can redefine variables (and functions and classes), but previously-compiled references refer to the earlier definition:
/*fx9*/ var xy:String="str"
str
/*fx10*/ xy
str
/*fx11*/ xy_plus(100)
102

Now this is correct in terms of static name-binding, but perhaps not the most useful. What you probably want is for xy_plus to use the new definition of xy. 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 xy_plus uses the the defintion seen when it was compiled.

Fixing this would involve some combination of automatic re-compilation (if xy_plus depends on xy, and xy is redefined, then re-compile xy_plus), and extra indirection.

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.

Memory and start-up issues

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 javafxc (which includes most of (a hacked-up version of) javac), and that takes some time. Subsequent commands run much faster.

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.

Created 26 Feb 2009 16:51 PST. Last edited 27 Feb 2009 17:38 PST. Tags:

I've long been interested in improved commend-line interfaces. The latest Kawa (in SVN) has a new implementation of a command interface window. If you start up Kawa with the -w flag:

kawa -w

you get a new Swing JFrame, whose interesting component is a JTextPane 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.

ReplPane1.png

Color coding

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.

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.

Input editing

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 Enter. This is previously available using the GNU readline 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.

Currently, the entire text pane is editable using the default JTextPane 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 Enter. 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.)

If you paste a multi-line string (commonly using ctrl-V), then ReplPane magically interleaves the prompt string and output with the input text. For example if you paste the following text:

(display "foo")
(newline)
(display
  "bar")
(newline)

You get this result:

ReplPane-multiline1.png

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.)

Embedding Components

A powerful feature is that you "print" java.awt.Component objects. These are embedded in the JTextPane. The following Scheme example creates a list of 3 JButton objects, and that list is then "printed":

ReplPane2.png

We can of course save a reference to the button in a variable:

ReplPane-but1a.png

That allows us to modify properies of the button. Here we change its text property, in line 4, and the button is updated as soon as we hit enter:

ReplPane-but1b.png

One anomaly when "printing" a Component is that a Component 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:

ReplPane-but1c.png

This isn't really the right behavior, but it's unavoidable when "printing" a Component. To avoid the problem, we need to "print" a some kind of "model" object rather than "view" objects.

Embedding Viewable objects

The experimental swing-gui library provides "viewable model" objects that can also be "printed" to a TextPane. For example, the gnu.kawa.models.Button class goes beyond Swing's ButtonModel in also having text and an action procedure. When a gnu.kawa.models.Button is "printed" the implementation automatically creates a JButton that is co-ordinated with the gnu.kawa.models.Button:

ReplPane-but2a.png

Again, we can change the text property:

ReplPane-but2b.png

Here we do some more complex layout, creating a Row 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.)

ReplPane-col2.png

Playing with composable Java2D pictures

The swing-gui library also provides convenience wrappers to the gnu.kawa.models.Paintable interface, which makes it easy to create, compose, and transform Java2D Shape objects.

ReplPane-2D1.png

Issues

  • The ReplPane is not very solid. It's not hard to type something which causes a Swing exception, which is usually not recoverable.
  • There should be a View menu to modify fornts and styles, and a standard Edit menu to Cut/Copy/Paste, at least. The File menu should have a way to save the typescript.
  • The Utilities->Purge Buffer menu item doesn't work.
  • The display should be integrated with the Kawa pretty-printer, so that changing the window width recalculates line-breaks.
  • It would be nice to emit styled "inline" (text) objects, such as a "red string". Maybe we should be using Swing's HTMLDocument instead of DefaultStyledDocument. Another toolkit to consider is Flying Saucer.
Created 11 Sep 2007 08:52 PDT. Last edited 11 Sep 2007 08:52 PDT. Tags:
Tags: