The Right Lang for the Job

Exploring the abbyss of non-mainstream programming languages

A Smalltalk environment that Just Works

Fixing some glitches in Visual Works IDE plugin

Last updated on:

NOTE: unusually, there's no code on Github for this post; contact me if you'd like to get the fixed plugin.
NOTE: screenshots' dimensions may look awkward - it's to make them fit on the page

Visual Works - a modern Smalltalk IDE

Cincom® Visual Works™ is an implementation of Smalltalk language and, true to it's ancestry, also an Integrated Development Environment for Rapid Application Development[1]

There are many Smalltalk implementations, some of them fully open-source, others created and maintained by a vendor as a commercial product. The state of the open-source Smalltalks is not the greatest, to put it mildldy[2]. It would take a long-ish rant to explain the reasons for this, but I don't believe it would be constructive. Instead, let's focus solely on the hero of today's post: Visual Works™, a commercial implementation that also offers a free, non-commercial Personal Use License. What you get under PUL is a tad older version - 8.3, from 2017 - but otherwise (almost) fully functional Smalltalk system, with (almost) all its source code available, along with lots of tools, libraries, and documentation.

The free version of VW doesn't contain anything that deals with cryptography. The most prominent victim of this lack is HTTPS support - there's none[3]. Other than that, you get everything VW 8.3 has to offer, including an extensive collection of ready-to-use libraries and plugins. Let's take a quick look at the VW IDE.

Browser - Visual Works' IDE

Almost all Smalltalks are IDEs themselves. For languages like Java, while you can stick to Notepad++, at some point you will probably need JetBrains products or Eclipse or something - in addition to Java compiler and its virtual machine. This is different for Smalltalks: each implementation is its own IDE.

In case you're wondering: yes, there is a reason why I say they "are" IDEs, not that they "have" IDEs. It's easier to show than explain, but in brief: Smalltalk is a language plus a library of classes. In Smalltalk, an IDE is just a set of classes within the stdlib, in the same manner a String class is. And just as you can change the String class in the running system, you can also change the IDE code the same way: while it runs, live, within the Smalltalk system. The classes IDE consists of use the graphic primitives provided by Smalltalk, use the tools for refactoring and code searching provided by Smalltalk itself, and in general only communicate with the OS to draw themselves.

The part of Smalltalk that works as an IDE in Visual Works is called a Browser. It looks like this (I shrunk the window a lot so that the screenshot looks good on the blog, so keep in mind that everything you see is obviously resizeable)

Default view of the VW IDE

A very fast explanation of what's what follows:

  • A - a package name in a global list of all installed packages
  • B - Searchligh, a multipurpose search tool & indexer
  • C - class name in a list of classes local to the selected package
  • D - show normal (Instance) or static (Class) methods
  • E - name of the method in a list of all methods of the selected class
  • F - source code for the selected method that you can edit
  • G - built-in test-runner, here showing that all of 1 tests passed

Of course, this is just the default view you get when you open it. Beyond the many built-in capabilities, there are also community-provided packages and plugins that you can install.

"It's a bit ugly, but it works"

While Visual Works is an IDE, it's also a Smalltalk environment. Think Electron with dev tools enabled. You can inspect and modify any part of the environment and see immediate feedback. Loading plugins is just one of the multitude of things that you can do to the environment: you can create, remove, and change class and method definitions, you can edit graphical assets and fonts, you can create new and edit any of the existing windows. Everything you do will have immediate effect.

Leaving that side, I installed one plugin that promised to display information about classes in a more structured format, in a single window. It's convenient, because normally you need to switch between 2-3 tabs to get the full picture of a given class. Knowing this, I installed it without hesitation.

Unfortunately, it didn't look very good.

ClassDefinitionTool just after installation

There's a lot I didn't like about this UI, starting with how labels of radio-buttons don't have enough space between then and are displayed one over another. The tool is undeniably useful, though, so I decided to fix it.

Editing the GUI with UIPainter

The reason for the problem with labels was that the author of the plugin set positions of various widgets in pixels, making them absolute, and unable to change their sizes to accomodate a bigger font that I chose as a default.

So, how do we fix this and a few other problems with the GUI? First, we need to locate the class responsible for it. Turns out that searching for ClassDefinitionTool was enough to jump right to its source. Next, you need to locate a method where the "spec" is stored. Spec is a declarative description of the GUI, including all widgets, their positions, relations, event handlers, etc. It looks like this when looked at "raw":

Raw view of windowSpec

But you rarely do it, and you never edit the spec by hand. Instead, you use a plugin called UIPainter, which allows you to manipulate the spec graphically:

Window spec open for editing in UIPainter

In this particular case, I was going to add some space between the labels for "indexing" and "visibility" rows, set min width and height in a few places, add some margins, and change some fonts. You can do all that from the UIPainter interface - choose a widget in the tree displayed on the left, go to "Position" tab, and edit the field you find there:

Setting position of a widget

Click "Apply" and you're done. After a few more trivial fixes, the UI for the plugin started looking quite decent:

UIPainter with fixed ClassDefinitionTool

The orange dividers are my preferrence, sorry if I offended your sensibilities.. Anyway, it took less than five minutes to fix the things that irritated me the most. After finishing, you need to "Install" the edited UI back into the windowSpec method - there's a menu item in UIPainter, in "Edit" menu, that serializes the edited spec, updates the method, searches for all instances of the plugin, and makes them redisplay themselves with new spec. Here's the effect, visible immediately after editing the UI:

Displaying fixed ClassDefinitionTool

What's worth noting is that every single element visible on the screen is yours to manipulate in a similar way. You might have noticed that the UIPainter, itself, had some problems with labels sizes. No problem! You can open the UIPainter on itself, and edit those as easily as with the ClassDefinitionTool.

You can see the real window and one of its widgets being edited in the screenshot below. From here it's literally one click to fix the problem.

Now, can you do the same with any other IDE - and as easily as in Visual Works? The UIPainter is not a new idea at all, and it's included in multiple IDEs to generate some kind of interface description. I'm quite sure none of these allows you to open the tool on *itself* and make changes *while the code is running* and without any additional setup? All of the above was done with just the default install and with just two plugins loaded.

But... what about Emacs?

Emacs - or rather, Emacs Lisp development - is in many ways very similar to the Smalltalk environment. Due to its roots and the need to work in a terminal, Emacs is keyboard-centric and the mouse use is not as prevalent. On the other hand, the ability to change pretty much anything while Emacs is running makes for an experience similar to that of Smalltalk.

Among other things Emacs provides, it also has powerful text editing capabilities. There's a lot to like: expand-region, multiple-cursors, iedit, editable occur, transpose-* commands, jumping to closing/opening paren, bulk indenting of multiple lines, recording macros, and so on. Almost none of these are implemented in Visual Works (nor any other Smalltalk, to my knowledge) editor and, yes, their lack hurts a lot.

Fortunately, both Visual Works and Emacs are fully programmable environments. It's not a big deal to make them talk to each other and work together. I made a little - around 80 loc - Emacs mode that listens for TCP connections on a local port and when a connection is established, it reads the content of the message and displays it in a new window (frame in Emacs parlance). You can edit the source there, and when you press C-c C-c it sends the edited contents back through the socket.

On the other side, I added a command accessible in SourceCodeTool, ie. in the bottom half of the browser window. Once you activate the command (currently via right mouse button/context menu) it opens a connection to Emacs, sends the currently selected method source through the socket, then waits for a response. It replaces the code with whatever comes back through the socket.

It took less than 150 loc in total to set this up. Here you can see it in action:

Current implementation is on the level of Proof of Concept and making it more robust overall and asynchronous on the Smalltalk side (currently the window controller process is blocked while it waits for response) would require a bit more effort. On the other hand, making the PoC took me two or three hours at most - and even in that state it is very useful. Reformatting longer methods to my liking is now a lot easier, with all the power of Emacs available. Adding features like auto-complete on the Emacs side with symbols provided by the Smalltalk side may not be trivial, but it's definitely doable in a weekend or two.

In any case: with environments as easily extensible as Emacs and Smalltalk both are you don't really have to choose one over the other. You can connect them and use them together, with just a little bit of code.

  • RAD on Wikipedia
  • One person's experience with Pharo. It's old, but many of the problems they complained about have not been addressed, to this day.
  • Though of course you can hack it trivially, for example by loading libcurl dynamically and calling it, or you can set reverse proxy with Nginx, or use one of the multitude of other possible workarounds, but you're on your own then. Not that I ever wasn't...