Acme: A User Interface for Programmers

Rob Pike

rob@plan9.bell-labs.com

ABSTRACT

A hybrid of window system, shell, and editor, Acme gives text-oriented applications a clean, expressive, and consistent style of interaction. Traditional window systems support interactive client programs and offer libraries of pre-defined operations such as pop-up menus and buttons to promote a consistent user interface among the clients. Acme instead provides its clients with a fixed user interface and simple conventions to encourage its uniform use. Clients access the facilities of Acme through a file system interface; Acme is in part a file server that exports device-like files that may be manipulated to access and control the contents of its windows. Written in a concurrent programming language, Acme is structured as a set of communicating processes that neatly subdivide the various aspects of its tasks: display management, input, file server, and so on.

Acme attaches distinct functions to the three mouse buttons: the left selects text; the middle executes textual commands; and the right combines context search and file opening functions to integrate the various applications and files in the system.

Acme works well enough to have developed a community that uses it exclusively. Although Acme discourages the traditional style of interaction based on typescript windows—teletypes—its users find Acme’s other services render typescripts obsolete.

History and motivation

The usual typescript style of interaction with Unix and its relatives is an old one. The typescript—an intermingling of textual commands and their output—originates with the scrolls of paper on teletypes. The advent of windowed terminals has given each user what amounts to an array of teletypes, a limited and unimaginative use of the powers of bitmap displays and mice. Systems like the Macintosh that do involve the mouse as an integral part of the interaction are geared towards general users, not experts, and certainly not programmers. Software developers, at least on time-sharing systems, have been left behind.

spacer

Figure 1. A small Acme screen—normally it runs on a larger display—demonstrating some of the details discussed in the text. The right column contains some guide files, a mailbox presented by Acme’s mail program, the columnated display of files in Acme’s own source directory, a couple of windows from the OED browser, a debugger window, and an error window showing diagnostics from a compilation. The left column holds a couple of source files (dat.h and acme.l), another debugger window displaying a stack trace, and a third source file (time.l). Time.l was opened from the debugger by clicking the right mouse button on a line in the stack window; the mouse cursor landed on the offending line of acme.l after a click on the compiler message.

Some programs have mouse-based editing of text files and typescripts; ones I have built include the window systems mux [Pike88] and [Pike91] and the text editor Sam [Pike87]. These have put the programmer’s mouse to some productive work, but not wholeheartedly. Even experienced users of these programs often retype text that could be grabbed with the mouse, partly because the menu-driven interface is imperfect and partly because the various pieces are not well enough integrated.

Other programs—EMACS [Stal93] is the prime example—offer a high degree of integration but with a user interface built around the ideas of cursor-addressed terminals that date from the 1970’s. They are still keyboard-intensive and dauntingly complex.

The most ambitious attempt to face these issues was the Cedar system, developed at Xerox [Swei86]. It combined a new programming language, compilers, window system, even microcode—a complete system—to construct a productive, highly integrated and interactive environment for experienced users of compiled languages. Although successful internally, the system was so large and so tied to specific hardware that it never fledged.

Cedar was, however, the major inspiration for Oberon [Wirt89], a system of similar scope but much smaller scale. Through careful selection of Cedar’s ideas, Oberon shows that its lessons can be applied to a small, coherent system that can run efficiently on modest hardware. In fact, Oberon probably errs too far towards simplicity: a single-process system with weak networking, it seems an architectural throwback.

Acme is a new program, a combined window system, editor, and shell, that applies some of the ideas distilled by Oberon. Where Oberon uses objects and modules within a programming language (also called Oberon), Acme uses files and commands within an existing operating system (Plan 9). Unlike Oberon, Acme does not yet have support for graphical output, just text. At least for now, the work on Acme has concentrated on producing the smoothest user interface possible for a programmer at work.

The rest of this paper describes Acme’s interface, explains how programs can access it, compares it to existing systems, and finally presents some unusual aspects of its implementation.

User interface

spacer

Figure 2. An Acme window showing a section of code. The upper line of text is the tag containing the file name, relevant commands, and a scratch area (right of the vertical bar); the lower portion of the window is the body, or contents, of the file. Here the scratch area contains a command for the middle button (mk) and a word to search for with the right button (cxfidalloc). The user has just clicked the right button on cxfidalloc and Acme has searched for the word, highlighted it, and moved the mouse cursor there. The file has been modified: the center of the layout box is black and the command Put appears in the tag.

Acme windows are arrayed in columns (Figure 1) and are used more dynamically than in an environment like X Windows or [Sche86, Pike91]. The system frequently creates them automatically and the user can order a new one with a single mouse button click. The initial placement of a new window is determined automatically, but the user may move an existing window anywhere by clicking or dragging a layout box in the upper left corner of the window.

Acme windows have two parts: a tag holding a single line of text, above a body holding zero or more lines (Figure 2). The body typically contains an image of a file being edited or the editable output of a program, analogous to an EMACS shell window. The tag contains the name of the window (usually the name of the associated file or directory), some built-in commands, and a scratch area to hold arbitrary text. If a window represents a directory, the name in the tag ends with a slash and the body contains a list of the names of the files in the directory. Finally, each non-empty body holds a scroll bar at the left of the text.

Each column of windows also has a layout box and a tag. The tag has no special meaning, although Acme pre-loads it with a few built-in commands. There is also a tag across the whole display, also loaded with helpful commands and a list of active processes started by Acme.

Typing with the keyboard and selecting with the left button are as in many other systems, including the Macintosh, , and Sam. The middle and right buttons are used, somewhat like the left button, to ‘sweep’ text, but the indicated text is treated in a way that depends on the text’s location—context—as well as its content. This context, based on the directory of the file containing the text, is a central component of Acme’s style of interaction.

Acme has no single notion of ‘current directory’. Instead, every command, file name, action, and so on is interpreted or executed in the directory named by the tag of the window containing the command. For example, the string mammals in a window labeled /lib/ or /lib/insects will be interpreted as the file name /lib/mammals if such a file exists.

Throughout Acme, the middle mouse button is used to execute commands and the right mouse button is used to locate and select files and text. Even when there are no true files on which to operate—for example when editing mail messages—Acme and its applications use consistent extensions of these basic functions. This idea is as vital to Acme as icons are to the Macintosh.

The middle button executes commands: text swept with the button pressed is underlined; when the button is released, the underline is removed and the indicated text is executed. A modest number of commands are recognized as built-ins: words like Cut, Paste, and New name functions performed directly by Acme. These words often appear in tags to make them always available, but the tags are not menus: any text anywhere in Acme may be a command. For example, in the tag or body of any window one may type Cut, select it with the left button, use the middle button to execute it, and watch it disappear again.

If the middle button indicates a command that is not recognized as a built-in, it is executed in the directory named by the tag of the window holding the text. Also, the file to be executed is searched for first in that directory. Standard input is connected to /dev/null, but standard and error outputs are connected to an Acme window, created if needed, called dir/+Errors where dir is the directory of the window. (Programs that need interactive input use a different interface, described below.) A typical use of this is to type mk (Plan 9’s make) in the scratch area in the tag of a C source window, say /sys/src/cmd/sam/regexp.c, and execute it. Output, including compiler errors, appears in the window labeled /sys/src/cmd/sam/+Errors, so file names in the output are associated with the windows and directory holding the source. The mk command remains in the tag, serving as a sort of menu item for the associated window.

Like the middle button, the right button is used to indicate text by sweeping it out. The indicated text is not a command, however, but the argument of a generalized search operator. If the text, perhaps after appending it to the directory of the window containing it, is the name of an existing file, Acme creates a new window to hold the file and reads it in. It then moves the mouse cursor to that window. If the file is already loaded into Acme, the mouse motion happens but no new window is made. For example, indicating the string sam.h in

#include "sam.h"

in a window on the file /sys/src/cmd/sam/regexp.c will open the file /sys/src/cmd/sam/sam.h.

If the file name is followed immediately by a colon and a legal address in Sam notation (for example a line number or a regular expression delimited in slashes or a comma-separated compound of such addresses), Acme highlights the target of that address in the file and places the mouse there. One may jump to line 27 of dat.h by indicating with the right button the text dat.h:27. If the file is not already open, Acme loads it. If the file name is null, for example if the indicated string is :/^main/, the file is assumed to be that of the window containing the string. Such strings, when typed and evaluated in the tag of a window, amount to context searches.

If the indicated text is not the name of an existing file, it is taken to be literal text and is searched for in the body of the window containing the text, highlighting the result as if it were the result of a context search.

For the rare occasion when a file name is just text to search for, it can be selected with the left button and used as the argument to a built-in Look command that always searches for literal text.

Nuances and heuristics

A user interface should not only provide the necessary functions, it should also feel right. In fact, it should almost not be felt at all; when one notices a user interface, one is distracted from the job at hand [Pike88]. To approach this invisibility, some of Acme’s properties and features are there just to make the others easy to use. Many are based on a fundamental principle of good design: let the machine do the work.

Acme tries to avoid needless clicking and typing. There is no ‘click-to-type’, eliminating a button click. There are no pop-up or pull-down menus, eliminating the mouse action needed to make a menu appear. The overall design is intended to make text on the screen useful without copying or retyping; the ways in which this happens involve the combination of many aspects of the interface.

Acme tiles its windows and places them automatically to avoid asking the user to place and arrange them. For this policy to succeed, the automatic placement must behave well enough that the user is usually content with the location of a new window. The system will never get it right all the time, but in practice most windows are used at least for a while where Acme first places them. There have been several complete rewrites of the heuristics for placing a new window, and with each rewrite the system became noticeably more comfortable. The rules are as follows, although they are still subject to improvement. The window appears in the ‘active’ column, that most recently used for typing or selecting. Executing and searching do not affect the choice of active column, so windows of commands and such do not draw new windows towards them, but rather let them form near the targets of their actions. Output (error) windows always appear towards the right, away from edited text, which is typically kept towards the left. Within the column, several competing desires are balanced to decide where and how large the window should be: large blank spaces should be consumed; existing text should remain visible; existing large windows should be divided before small ones; and the window should appear near the one containing the action that caused its creation.

Acme binds some actions to chords of mouse buttons. These include Cut and Paste so these common operations can be done without moving the mouse. Another is a way to apply a command in one window to text (often a file name) in another, avoiding the actions needed to assemble the command textually.

Another way Acme avoids the need to move the mouse is instead to move the cursor to where it is likely to be used next. When a new window is made, Acme moves the cursor to the new window; in fact, to the selected text in that window. When the user deletes a newly made window, the cursor is returned to the point it was before the window was made, reducing the irritation of windows that pop up to report annoying errors.

When a window is moved, Acme moves the cursor to the layout box in its new place, to permit further adjustment without moving the mouse. For example, when a click of the left mouse button on the layout box grows the window, the cursor moves to the new location of the box so repeated clicks, without moving the mouse, continue to grow it.

Another form of assistance the system can offer is to supply precision in pointing the mouse. The best-known form of this is ‘double-clicking’ to select a word rather than carefully sweeping out the entire word. Acme provides this feature, using context to decide whether to select a word, line, quoted string, parenthesized expression, and so on. But Acme takes the idea much further by applying it to execution and searching. A single click, that is, a null selection, with either the middle or right buttons, is expanded automatically to indicate the appropriate text containing the click. What is appropriate depends on the context.

For example, to execute a single-word command such as Cut, it is not necessary to sweep the entire word; just clicking the button once with the mouse pointing at the word is sufficient. ‘Word’ means the largest string of likely file name characters surrounding the location of the click: click on a file name, run that program. On the right button, the rules are more complicated because the target of the click might be a file name, file name with address, or just plain text. Acme examines the text near the click to find a likely file name; if it finds one, it checks that it names an existing file (in the directory named in the tag, if the name is relative) and if so, takes that as the result, after extending it with any address that may be present. If there is no file with that name, Acme just takes the largest alphanumeric string under the click. The effect is a natural overloading of the button to refer to plain text as well as file names.

First, though, if the click occurs over the left-button-selected text in the window, that text is taken to be what is selected. This makes it easy to skip through the occurrences of a string in a file: just click the right button on some occurrence of the text in the window (perhaps after typing it in the tag) and click once for each subsequent occurrence. It isn’t even necessary to move the mouse between clicks; Acme does that. To turn a complicated command into a sort of menu item, select it: thereafter, clicking the middle button on it will execute the full command.

As an extra feature, Acme recognizes file names in angle brackets <> as names of files in standard directories of include files, making it possible for instance to look at <stdio.h> with a single click.

Here’s an example to demonstrate how the actions and defaults work together. Assume /sys/src/cmd/sam/regexp.c is open and has been edited. We write it (execute Put in the tag; once the file is written, Acme removes the word from the tag) and type mk in the tag. We execute mk and get some errors, which appear in a new window labeled /sys/src/cmd/sam/+Errors. The cursor moves automatically to that window. Say the error is

main.c:112: incompatible types on assignment to ‘pattern’

We move the mouse slightly and click the right button at the left of the error message; Acme makes a new window, reads /sys/src/cmd/main.c into it, selects line 112 and places the mouse there, right on the offending line.

Coupling to existing programs

Acme’s syntax for file names and addresses makes it easy for other programs to connect automatically to Acme’s capabilities. For example, the output of

grep -n variable *.[ch]

can be used to help Acme step through the occurrences of a variable in a program; every line of output is potentially a command to open a file. The file names need not be absolute, either: the output appears in a window labeled with the directory in which grep was run, from which Acme can derive the full path names.

When necessary, we have changed the output of some programs, such as compiler error messages, to match Acme’s syntax. Some might argue that it shouldn’t be necessary to change old programs, but sometimes programs need to be updated when systems change, and consistent output benefits people as well as programs. A historical example is the retrofitting of standard error output to the early Unix programs when pipes were invented.

Another change was to record full path names in the symbol table of executables, so line numbers reported by the debugger are absolute names that may be used directly by Acme; it’s not necessary to run the debugger in the source directory. (This aids debugging even without Acme.)

A related change was to add lines of the form

#pragma src "/sys/src/libregexp"

to header files; coupled with Acme’s ability to locate a header file, this provides a fast, keyboardless way to get the source associated with a library.

Finally, Acme directs the standard output of programs it runs to windows labeled by the directory in which the program is run. Acme’s splitting of the output into directory-labeled windows is a small feature that has a major effect: local file names printed by programs can be interpreted directly by Acme. By indirectly coupling the output of programs to the input, it also simplifies the management of software that occupies multiple directories.

Coupling to new programs

Like many Plan 9 programs, Acme offers a programmable interface to other programs by acting as a file server. The best example of such a file server is the window system [Pike91], which exports files with names such as screen, cons, and mouse through which applications may access the I/O capabilities of the windows. provides a distinct set of files for each window and builds a private file name space for the clients running ‘in’ each window; clients in separate windows see distinct files with the same names (for example /dev/mouse). Acme, like the process file system [PPTTW93], instead associates each window with a directory of files; the files of each window are visible to any application. This difference reflects a difference in how the systems are used: tells a client what keyboard and mouse activity has happened in its window; Acme tells a client what changes that activity wrought on any window it asks about. Putting it another way, enables the construction of interactive applications; Acme provides the interaction for applications.

The root of Acme’s file system is mounted using Plan 9 operations on the directory /mnt/acme. In that root directory appears a directory for each window, numbered with the window’s identifier, analogous to a process identifier, for example /mnt/acme/27. The window’s directory contains 6 files: /mnt/acme/27/addr, body, ctl, data, event, and tag. The body and tag files contain the text of the respective parts of the window; they may be read to recover the contents. Data written to these files is appended to the text; seeks are ignored. The addr and data files provide random access to the contents of the body. The addr file is written to set a character position within the body; the data file may then be read to recover the contents at that position, or written to change them. (The tag is assumed small and special-purpose enough not to need special treatment. Also, addr indexes by character position, which is not the same as byte offset in Plan 9’s multi-byte character set [Pike93]). The format accepted by the addr file is exactly the syntax of addresses within the user interface, permitting regular expressions, line numbers, and compound addresses to be specified. For example, to replace the contents of lines 3 through 7, write the text

3,7

to the addr file, then write the replacement text to the data file. A zero-length write deletes the addressed text; further writes extend the replacement.

The control file, ctl, may be written with commands to effect actions on the window; for example the command

name /adm/users

sets the name in the tag of the window to /adm/users. Other commands allow deleting the window, writing it to a file, and so on. Reading the ctl file recovers a fixed-format string containing 5 textual numbers—the window identifier, the number of characters in the tag, the number in the body, and some status information—followed by the text of the tag, up to a newline.

The last file, event, is the most unusual. A program reading a window’s event file is notified of all changes to the text of the window, and is asked to interpret all middle- and right-button actions. The data passed to the program is fixed-format and reports the source of the action (keyboard, mouse, external program, etc.), its location (what was pointed at or modified), and its nature (change, search, execution, etc.). This message, for example,

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.