Attributes
wily-0.13.42/ 0000755 0236657 0000012 00000000000 10415262701 0012502 5 ustar 00oz staff 0000433 0000002 wily-0.13.42/Doc/ 0000755 0236657 0000012 00000000000 10333201523 0013201 5 ustar 00oz staff 0000433 0000002 wily-0.13.42/Doc/LSM 0000644 0236657 0000012 00000002166 10333201523 0013564 0 ustar 00oz staff 0000433 0000002 Begin3 Title: Wily - a simple, powerful, mouse-based text environment Version: 0.13.23 [November 1996] Entered-date: November 1996 Description: Interact in a general way with text on the screen using a mouse. Instead of menus or control keys, clicking on text with the middle mouse button executes it, clicking on text with the right mouse button "goes to" a file, directory, string or regular expression. Wily was written by programmers for programmers: there are very few rules and almost no exceptions. The learning curve is steep but short, and very few people have used wily for a few hours and gone back to another editor. Wily is an emulation for Unix and X of Acme which only works in the Plan 9 environment. Acme was inspired chiefly by Oberon. Keywords: text-editor window-manager integrated-development-environment Author: gary@cs.su.oz.au Maintained-by: gary@cs.su.oz.au Primary-site: ftp.cs.su.oz.au /gary/wily/ 312k wily.tgz Alternate-site: ftp.northsea.com /pub/plan9_unix/wily/ Original-site: Platform: UNIX Copying-policy: Artistic End wily-0.13.42/Doc/old/ 0000755 0236657 0000012 00000000000 10333201523 0013757 5 ustar 00oz staff 0000433 0000002 wily-0.13.42/Doc/old/tile.html 0000644 0236657 0000012 00000115503 10333201523 0015607 0 ustar 00oz staff 0000433 0000002
x
or y
coordinate).
The terms "min" and "max" are used for consistency with
the definition of Rectangle
.
<tile elements>= (<-U) [D->] int min, max;
<tile elements>+= (<-U) [<-D->] int base;
<tile elements>+= (<-U) [<-D->] int step;
<tile elements>+= (<-U) [<-D] TileList* list;
Instead of putting a pointer to a tile in all the windows and columns,
we simply insist that the first element in a window or column is a tile
structure. This way, (struct tile *) windowptr
makes sense.
Tile Lists
Tiles are strewn out in lists. Instead of using linked lists, I have
chosen to use arrays, but that is just an implementation detail.
TileLists need a rectangle to determine where its tiles go.
<data structures>+= (U->) [<-D] struct TileList { <tile list elements> };
The tile list consists of
self
is not used in anywhere in the tile code,
as the the tile list may be a member of a super-tile-list running
the other way.
<tile list elements>= (<-U) [D->] Tile self;
<tile list elements>+= (<-U) [<-D->] Tile **tiles;
<tile list elements>+= (<-U) [<-D->] int min, max;
<tile list elements>+= (<-U) [<-D->] ushort count, maxtiles;
hidden
is used to determine how many tiles
are hidden "to the left of" the tiles actually displayed (as a result of
a B3, for instance).
<tile list elements>+= (<-U) [<-D] ushort hidden;
};
<typedefs>= (U->) typedef struct TileList TileList;
Since the tile growth is rather blind at times, tile_list_reshaped
is called without a specific "untouchable" tile, thereby fixing a prior bug.
<public tile functions>= (U->) [D->] void tile_grow(Tile *tile, Growth grow) { TileList *list = tile->list; <tile_grow
locals> <tile consistency checks> switch (grow) { case Gsome: <growtile
a little> break; case Gmost: <growtile
lots> break; case Gall: <growtile
way lots> break; default: assert(grow != grow); } tile_list_reshaped(list, 0); }
<public function declarations>= (U->) [D->] void tile_grow(Tile *, Growth);
This is probably too small
, especially when a column is being grown, but it will work for now. Also, it does not check boundary conditions.<grow tile
a little>= (<-U)
tile->min -= tile->step;
tile->max += tile->step;
The B2 implementation of this code relies slightly on the way
tile_list_reshaped
is currently implemented, as it relies
on it not to hide any windows until it has tried shrinking
them all. Since this is good user interface policy anyhow, this
assumption presents little problem.
<grow tile
lots>= (<-U)
space = list->min;
for (i = list->hidden; i < list->count; i++) {
if (list->tiles[i] == tile) {
tile->min = space;
space = 0;
continue;
}
space += list->tiles[i]->base;
}
tile->max = list->max - space;
<tile_grow
locals>= (<-U)
int i, space;
To grow a tile way lots, the remaining tiles are added to the "hidden" list. For the moment, the tiles are added in order by simply swapping the selected tile to the end of the array, but heuristics should be added so that the "least hidden" tile is the one the user is most likely to want to see next.
A little trickery here -- the code only counts up to count - 1
.
This is because the tile being expanded way lots gets swapped to the end
position in the array during the loop. If the tile being expanded is already
at the end, then there is no problem. Of course, if the caller
called with an array where tile
was not in list
, then there
is a problem.
<grow tile
way lots>= (<-U)
for (i = list->hidden; i < list->count - 1; i++) {
if (list->tiles[i] == tile) {
list->tiles[i] = list->tiles[list->count - 1];
list->tiles[list->count - 1] = tile;
continue;
}
list->tiles[i]->min = list->tiles[i]->max;
}
list->hidden = list->count - 1;
tile->min = list->min;
tile->max = list->max;
L
ocating a Tile
This funtion simply relies on index_for_place
to find the right
tile and then looks the tile up in the array.
<public tile functions>+= (U->) [<-D->] Tile * point2tile(TileList *list, int spot) { int i = index_for_place(list, spot); check_list_consistency(list); return (i >= 0) ? list->tiles[i] : 0; }
<public function declarations>+= (U->) [<-D->] Tile *point2tile(TileList *, int);
M
aking a Tile Visible
As a gross heuristic,
the tile size is set to base
+3step
. If there is not enough room for
that, make the tile simply take the whole list over.
Of course, if the tile is already showing, there is no reason to expand it.
<public tile functions>+= (U->) [<-D->]
void
tile_show(Tile *tile) {
TileList *list;
<tile consistency checks>
list = tile->list;
check_list_consistency(list);
if (TILESIZE(tile) == 0) {
<expose the hidden tile tile
>
}
if (TILESIZE(tile) >= tile->base + tile->step)
return;
tile->max = tile->min + tile->base + 3 * tile->step;
if (tile->max > list->max) {
tile->max = list->max;
tile->min = tile->max - (tile->base + 3 * tile->step);
if (tile->min < list->min)
tile->min = list->min;
}
tile_list_reshaped(list, tile);
}
<public function declarations>+= (U->) [<-D->] void tile_show(Tile *);
Hidden tiles present a small problem -- they first must be "unhidden" so they can be displayed. For lack of a better place to put it,, the tile is added at the end of the tile list. +Errors windows probably belong at the bottom anyhow.
<expose the hidden tile tile
>= (<-U)
int i;
for (i = 0; i < list->count && tile != list->tiles[i]; i++)
;
while (++i < list->count)
list->tiles[i-1] = list->tiles[i];
list->tiles[i] = tile;
list->hidden--;
M
oving a Tile Within a Tile List
Just add and delete it for now.
<public tile functions>+= (U->) [<-D->] void tile_move(Tile *tile) { tile_del(tile); tile_add(tile->list, tile); }
<public function declarations>+= (U->) [<-D->] void tile_move(Tile *);
A
dding a Tile to a Tile List
This function uses the minimum coordinate of the new
tile to determine where it should go.
What if the caller doesn't care?
If the caller does not know how large it wants the tile, it should
specify a tile with a size of 0.
<public tile functions>+= (U->) [<-D->] void tile_add(TileList *list, Tile *tile) { <local variables for adding a tile> <tile consistency checks> check_list_consistency(list); <make sure tile shape is reasonable> <determine where to place the new tile in the list> <add the tile to the list> tile->list = list; tile_list_reshaped(list, tile); }
<public function declarations>+= (U->) [<-D->] void tile_add(TileList *, Tile *);
The tile location must be constrained to its collection. If the tile is so deformed after this reshaping that its size exceeds the bounds of its container, it is simply shrunk to zero size.
<make sure tile shape is reasonable>= (<-U) assert(tile->min < tile->max); if (tile->min < list->min) { tile->max += list->min - tile->min; tile->min = list->min; } if (tile->max > list->max) { tile->max = list->max; } if (tile->max - tile->min < tile->base) tile->min = tile->max = 0;
To determine where the tile should go in the list, we perform a
binary search on the tiles in the tile list to find the tile
containing tile->min
. This will be useful enough to warrant
a separate function. The program must then determine whether the
new tile will go before or after the tile it is being placed on.
The tile is placed so that the tile it is supplanting is moved as little
as possible.
<determine where to place the new tile in the list>= (<-U) if (list->count == list->hidden) { i = list->hidden; tile->min = list->min; tile->max = list->max; } else { i = index_for_place(list, tile->min); if ((<distance to move current tile up>) < (<distance to move current tile down>)) { i++; } }
<local variables for adding a tile>= (<-U) [D->] ushort i;
Moving a tile up means just moving it by the distance from its bottom to the desired top of the new window. Figuring out how far a tile down would move down is more complicated, but essentially works out to the distance from the top of the displaced tile to the bottom of the new tile.
<distance to move current tile up>= (<-U) list->tiles[i]->max - tile->min
<distance to move current tile down>= (<-U) tile->max - list->tiles[i]->min
Once we know where in the array the tile goes, the other tiles need to be shuffled out of the way.
<add the tile to the list>= (<-U) <make sure tile list has room for one more> for (j = ++list->count; j > i; j--) list->tiles[j] = list->tiles[j-1]; list->tiles[i] = tile;
<local variables for adding a tile>+= (<-U) [<-D] ushort j;
R
emoving a Tile from a List
This is pretty dull, actually -- the remaining elements
of the tile list are shuffled down in the array, and then
the tiles are resized to fit the new tile list.
One bug found: I was passing tile
into tile_list_reshaped
,
of all the silly things.
<public tile functions>+= (U->) [<-D->] void tile_del(Tile *tile) { TileList *list = tile->list; int i; for (i = 0; i < list->count; i++) { if (list->tiles[i] == tile) { while (++i < list->count) { list->tiles[i-1] = list->tiles[i]; } list->count--; tile_list_reshaped(list, 0); return; } } assert(0); }
<public function declarations>+= (U->) [<-D->] void tile_del(Tile *);
tile
argument
is used to indicate the tile that should not be adjusted.
Specifying a tile
of zero prevents any tile from being treated
as special.
Adjusting the tiles, then, is split into two phases -- adjusting
the tiles above tile
, and adjusting those below it. tileidx
is
used in the next chunk of code. It is initially set to a location
not in the tile list.
<public tile functions>+= (U->) [<-D->]
void
tile_list_reshaped(TileList *list, Tile *tile) {
int i;
int tileidx = -1;
<tile_list_reshaped
locals>
if (!tile)
adjust_sizes_in_range(list, list->hidden, list->count,
list->max - list->min);
else {
<tile consistency checks>
for (i = 0; list->tiles[i] != tile; i++) {
assert(i < list->count);
}
tileidx = i;
adjust_sizes_in_range(list, list->hidden, i, tile->min - list->min);
adjust_sizes_in_range(list, i + 1, list->count, list->max - tile->max);
}
<repair tile locations>
check_list_consistency(list);
}
<public function declarations>+= (U->) [<-D->] void tile_list_reshaped(TileList *, Tile *);
Once the tiles all fit, their locations must be updated
(adjust_sizes_in_range
does not do this). This is merely
a matter of sticking the tiles end-to-end.
<repair tile locations>= (<-U) for (prevmax = list->min, i = list->hidden; i < list->count; i++) { if (i != tileidx) { list->tiles[i]->max -= list->tiles[i]->min - prevmax; list->tiles[i]->min = prevmax; } prevmax = list->tiles[i]->max; }
<tile_list_reshaped
locals>= (<-U)
int prevmax;
The tiles in a particular range are adjusted through a simple series of steps. Note that this only adjusts the sizes of the tiles -- it does not adjust the tiles' locations.
Bugs found:
<static tile functions>= (U->) [D->] static void adjust_sizes_in_range(TileList *list, int start, int max, int available) { <local variables for tile adjustment> if (start == max) return; <determine amount of space needed> if (0 && needed < 0) { <expand tiles to fill slack> return; } <shrink tiles until space available> }
Determining the space required is easy -- determine the difference between the heights of the tiles and the collection they appear in.
<determine amount of space needed>= (<-U) needed = -available; for (i = start; i < max; i++) needed += TILESIZE(list->tiles[i]);
<local variables for tile adjustment>= (<-U) int i; int needed;
At this point, tiles are shrunk in a fairly arbitrary manner. The tile at the top gets shrunk first, and on down until the necessary space has been acquired. Tiles should be removed from view completely if adequate space is not available.
A small ugly here -- there may be a gap right before the "fixed" tile.
<shrink tiles until space available>= (<-U) for (i = start; needed && i < max; i++) { int base = list->tiles[i]->base; int step = list->tiles[i]->step; int shrink = TILESIZE(list->tiles[i]) - base; shrink = ((shrink - base) / step) * step + base; if (shrink > needed) shrink = needed; needed -= shrink; list->tiles[i]->max -= shrink; }
For the moment, expanding to fill a gap is handled is a relatively arbitrary manner -- the last tile is grown to fill the gap. What about any fuzz?
<expand tiles to fill slack>= (<-U) list->tiles[max-1]->max -= needed;
<public function declarations>+= (U->) [<-D->] #define COLTILE(t) (&(t)->tiles.self) static Col *WINCOL(Win *w) {return (Col *) w->tile.list;}
I
teration
There is no reason that every file dealing with tile lists need care
about the internals of the list. This also substantially helps if
the algorithm needs to be rearranged.
<public function declarations>+= (U->) [<-D->] #define FOR_ALL_TILES(t, l)\ { int __fral;\ for (__fral = 0; __fral < (l)->count && (t = (l)->tiles[__fral]); __fral++) #define FOR_ALL_SHOWING_TILES(t, l)\ { int __fral;\ for (__fral = (l)->hidden; __fral < (l)->count && (t = (l)->tiles[__fral]); __fral++) #define END_ALL }
C
omputing a Tile's Height
This is a trivial computation, but occurs frequently enough to
warrant its own macro.
<public function declarations>+= (U->) [<-D->] #define TILESIZE(tile) ((tile)->max - (tile)->min)
I
nternal Functions
<make sure tile list has room for one more>= (<-U) if (list->count == list->maxtiles) { list->maxtiles *= 2; list->tiles = realloc(list->tiles, list->maxtiles * sizeof(list->tiles[0])); }
start
is increased by at least 1 or end
is decreased by at
least one (because of C's rounding, start + (end - start / 2
is always less than start + (end - start)
).
<static tile functions>+= (U->) [<-D] static int index_for_place(TileList *list, int spot) { int start = list->hidden; int end = list->count; int mid; Tile *tile; while (start < end) { mid = start + (end - start) / 2; tile = list->tiles[mid]; if (spot < tile->min) end = mid; else if (spot > tile->max) start = mid + 1; else return mid; } return -1; }
A
ssertions
There will be some useful assertions to make at various points in the code.
Some are catastrophic, others are merely aeshetic problems.
<public tile functions>+= (U->) [<-D] void check_tile_consistency(Tile *tile) { <tile consistency checks> } void check_list_consistency(TileList *list) { int i; Tile *t; <tile list consistency checks> FOR_ALL_TILES(t, list) { check_tile_consistency(t); } END_ALL; }
<public function declarations>+= (U->) [<-D] void check_list_consistency(TileList *);
C
<tile list consistency checks>= (<-U) [D->] assert(list);
<tile list consistency checks>+= (<-U) [<-D->] assert(list->count <= list->maxtiles);
<tile list consistency checks>+= (<-U) [<-D->] assert(list->count == 0 || list->count > list->hidden);
<tile list consistency checks>+= (<-U) [<-D->] assert(list->count == 0 || list->tiles[list->hidden]->min >= list->min); assert(list->count == 0 || list->tiles[list->count - 1]->max <= list->max);
max
edge of one tile is not equal to the min
edge of the following tile.
<tile list consistency checks>+= (<-U) [<-D] for (i = list->hidden + 1; i < list->count; i++) assert(list->tiles[i-1]->max == list->tiles[i]->min);
tile.r.min
.
struct tile
is not consistent with
the information in the enclosing structure.
<tile consistency checks>= (<-U <-U <-U <-U <-U) assert(tile->min <= tile->max);
screen.r.max.y
.
font->height
above screen.r.max.x
.
<tiletypedef.h>= <typedefs>
<tiletype.h>= <data structures>
<tileproto.h>= <public function declarations>
<tile.c>= #include "wily.h" <static tile functions> <public tile functions>
tile
>: U1, D2
tile
a little>: U1, D2
tile
lots>: U1, D2
tile
way lots>: U1, D2
tile_grow
locals>: U1, D2
tile_list_reshaped
locals>: U1, D2
start
is increased by at least 1 or end
is decreased by at
least one (because of C's rounding, start + (end - start / 2
iswily-0.13.42/Doc/old/pythonpaper.html 0000644 0236657 0000012 00000051460 10333201523 0017224 0 ustar 00oz staff 0000433 0000002 This is a draft of a document which will be submitted to the 4th International Python Conference to be held at June 4-6, 1996 at the Lawrence Livermore labs in California.
This document describes aspects of Wily which are not yet released for beta testing.
Wily is an editing environment which can be extended with Python programs. Instead of using an embedded Python interpreter, Wily talks a simple message protocol, and a module lets Python speak the same protocol. This approach is general (one can use any language) and clean (the editor is responsible for only a small set of functions). The message interface promotes a clear separation of functions, with the Wily editing environment providing a standard way of interacting with client programs. The main disadvantage may be that the looser binding between programming language and editor is less efficient, but this is not expected to be a significant problem.The editor itself is simple, clean, and efficient, with a few features which combine consistently. It abandons backwards compatibility with the virtual terminals of yesteryear, to take full advantage of a bitmapped terminal and three button mouse.
This documet describes:
An appendix defines the Python interface in more detail.
This is necessarily very brief, and Wily and Acme are very different from what most people are used to. Please read Rob Pike's paper for a better and more complete description of the interface.
Wily's main attractions are that it is very simple (the User Manual describing the whole user interface is about seven pages), very quick to use, and integrates well with other Unix tools.
Wily emulates in the Unix and X Windows environment the Acme editor from Plan 9. Acme couldn't easily be ported to Unix because it is written in Alef (a concurrent programming language not widely available) and uses non-portable features of Plan 9 . Also, a port of Acme would not be freely distributable.
The name ``Wily'' derives from Wile E. Coyote, an avid consumer of Acme products.
The screen is divided into columns, which are divided into windows. A window may represent a file, a directory, or the output from some program. The screen, columns and windows each have a narrow horizontal ``tag'' at the top, which contains some useful text, and for windows contains the name of the window and a "file is modified" indicator.
Text is displayed in a propotionally spaced font by default. There is a built in function to toggle with a monospace font, but there are very few occasions when this is necessary or desirable. In most cases limiting a text editor to monospace fonts is blindly following the hardware limitations of the 1960s.
Text is read and written as UTF8-encoded Unicode, providing some support for editing multi-lingual and scientific text, but remaining backwardly compatible with plain 7-bit ASCII text.
All actions in Wily are through creating and interacting with text on the screen, using the three-button mouse and the keyboard. B1 (mouse button one) selects text, B2 executes text and B3 attempts to ``goto'' the text.
When executing text, there are some built in operations, such as
``Quit'', or ``Del'' which are executed by Wily itself. Otherwise, the
command is executed in a subprocess, with output redirected to a window
within Wily and input from /dev/null
. It is also possible
to use the current selection (currently selected text) as input for
and/or output of some child program. For example |fmt
formats the current selection, >spell
checks its
spelling and <date
replaces it with the current time.
The goto operation used with B3 is polymorphic in that it recognises three different patterns:
Wily's power derives in part from its general treatment of text. Whether text is found in a file, or directory listing, created by Wily, output by some other program, or typed by the user, it can all be treated the same way. A simple text file may be used as a menu of commands or a simple form of hypertext. Previously typed commands or ``usage'' messages can be edited and executed. There is no need for dialogs, menus, modes, buttons or accelerator keys, just operations on text on the screen.
Wily minimizes the number of unnecessary mouse or keyboard actions the user must make. For instance, placing newly created windows is done without needing the user's help, although the user can easily rearrange things if Wily's heuristics aren't acceptable. Wily also makes cutting, pasting, executing and searching for text so easy to do with the mouse that there is no need to retype text already on the screen.
Cutting and pasting text is made particularly convenient using combinations of mouse buttons. To cut text, select it with B1, and while still holding B1, also click B2: this is a B1-B2 chord. To paste text use a B1-B3 chord. This style is unusual, but once it becomes familiar other methods of cutting and pasting seem unbearably slow. Another chord (B2-B1) is used to execute some text with an argument.
To avoid having to position the mouse accurately, a mouse selection of zero length (i.e. a mouse click) is expanded in a ``reasonable'' manner. Clicking with B2 in a word expands to that word, which is then executed. Clicking with B3 in an address (e.g. foo.c:45) expands to that address. Double-clicking with B1 just inside paired braces, brackets or quotes selects the region thus delimited.
Wily's general approach to text as commands and addresses is compounded because these basic functions can be accessed so conveniently. Most operations can be achieved with a single mouse click or mouse combination.
The consistent and general use of text for input and output in Wily makes the combination of its features more than the sum of its parts.
Stepping through an example session might best illustrate this:
Susan has fetched the file frob.tar.Z. She clicks with B1 in the file name, and with B2 in the word untarz. The utility untarz uncompresses and untars the file, verbosely printing file names as it goes. She clicks with B3 on one of the file names, INSTALL, which opens the file. The INSTALL file suggests that she run configure then make. She clicks B2 in each of these words to run the suggested programs, but there's a problem. The make fails when gcc quits with the error message
keyboard.c:4: strings.h: No such file or directory.
She clicks with B3 in the first part of the error message, which opens the file and selects the line. On Susan's system there is a string.h but no strings.h. She removes the last s with a B1-B2 chord. When she modifies the file, Wily sets the ``file modified'' indicator, and adds the word ``Save'' to the tag for this window. Susan clicks B2 on the word Save to write the file, clicks B2 on make again, and she's done.
This whole process uses about ten mouse clicks and no typing.
Wily waits for a program to connect to it using a socket, and then sends and receives messages. The messages can be RPC requests and replies, or ``event'' messages generated by Wily when certain actions happen in windows which a remote program has asked to monitor.
The interface is deliberately at a rather high level. The intent is that Wily will take care of of the details of interaction, with other programs providing the semantics of executing a command, opening a window or searching for an address. This promotes a consistent set of tools and an efficient division of labour, but necessarily at the loss of some generality.
The requests allow the remote program to
A program may ask to be notified when events happen in a particular window. These events are generated when:
When a program is being sent B2 or B3 events, Wily doesn't act on them, merely sends them on. The program may opt to ``send back'' some events which Wily will then act on. Events which modify text, on the other hand, are always handled by Wily, which then may inform the remote program what has happened. In other words, the remote program cannot stop the user modifying any piece of text, although it can repair the text later if it wishes.
This is a very simple interface between Python and the Wily message protocol. It establishes a connection, provides remote procedure calls and queues events until they are requested.
A Connection is initialized with no arguments, and represents the connection to Wily. All the other functions are methods of a connection object.
The Connection has methods to return a list of windows representing all the windows currently open in Wily, to create a new window, and to return the next event from Wily. Windows are represented as integers, which correspond to window identifiers with Wily, and events are represented as tuples.
The Connection has a number of methods which provide operations on Wily windows. These operations allow a program, once it has an identifier for a window, to change its event mask, its name or its ``tools'' (useful text in the window's tag), read or modify some of its text, or simulate a B2 or B3 action.
This is a small Python module which provides a friendlier interface to Wily's functionality.
# Create a window called "hello",and fill it with # the text "Hello, world" import wily con = wily.Connection() win = con.win("hello") con.replace(win, 0,0, "Hello, world\n")
These are small tools written as proof of concept, to test and refine the message interface, and as templates for other applications.
This uses Python classes for threading, talking NNTP and reading and writing .newsrc files, and Wily for displaying newsgroups and articles. The program acts on B3 actions on article numbers, and B2 actions on some special function names (e.g. Catch-up, Follow-up) which it also places in the correct tags.
There are two programs to save and restore the ``state'' of an editing session. The ``save'' program determines from Wily what windows are open, what the current selection is for each, and the full text of windows which don't represent a file or directory. The ``restore'' program connects to Wily and uses the saved information to create and massage windows to hopefully return the session to the original state.
Win provides standard input and output for its client program, and creates and monitors a Wily window, maintaining an input point. When win sees a carriage return entered after the input point, it sends the text between the input point and the carriage return to the client program. Output coming back from the client program is inserted just before the input point.
Win does not attempt to provide a pseudo tty or work with any programs which attempt to use curses-style screen addressing. However, it works fine for programs such as those mentioned above, and was written with only 230 lines of commented C.
Wily's approach to extending the editor is to communicate with other processes using a socket or pipe. Here we examine some alternative strategies, and why Wily doesn't use them.
This is by far the most common design for an extensible editor.
There are a few advantages to this approach. Letting the extension language share the address space of the editor is more efficient than using IPC. The language can also be tailored to provide a close match to the editing requirements.
There are two reasons why this approach was rejected for Wily. The first reason is that it restricts users to a single language, and it often means designing a new (possibly half-baked) language. The second reason is that embedding a language interpreter in the editing environment seemed an unnecessary step towards monolithism and code bloat.
For comparison, below are the sizes of three stripped, dynamically linked executables on the author's sun4d Solaris system:
167264 | wily |
234272 | vi |
1873336 | xemacs |
Another approach is to dynamically link extension code with the editor. This is more general and more efficient than the built in interpreter design. CodeWright® from Premia uses this approach in the Microsoft Windows environment.
One problem with this approach is that dynamic linking is not standard on all UNIXes. Another is that one errant code fragment dynamically linked in might damage the integrity of the editor. The result might be lost data, or even worse, data quietly modified in some subtle manner.
When this approach becomes ubiquitously available and more secure, it will be re-examined.
It would seem that ILU and CORBA both provide a ready-made solution to turn an editor into a distributed object server. However, both seemed too heavy-weight for this simple task.
There is a strong belief amongst Wily's users and developers that ``creeping featurism'' is evil. Most of the ongoing work will be towards refining existing features, and improving the clarity and correctness of the code. Retaining the ``look and feel'' of Acme is also an important consideration.
There is still room for innovation, though. The message interface is still very young, and may change with the demands of application writers. The author hopes that the Python community will be able to make good use of an editor programmable in Python. Although there is no need for cursor keys to navigate around Wily, they may be added later, although they are a low priority.
Currently programs connect to Wily by writing to a UNIX named pipe. Wily's security relies on this pipe being writable only by its owner. This means Wily can't accept connections directly from remote machines, which would be desirable for some applications. A design which avoids this limitation securely and efficiently has not been decided upon.
Wily is freely available.
The prime source for information about Wily is the WWW page at
http://www.cs.su.oz.au/~gary/wily/
The wily
distribution contains wilymodule.c, and can be obtained at
ftp://ftp.cs.su.oz.au/gary/wily.tgz
The early design and source of Wily owe a lot to Bill Trost. Wily and this paper have also been improved by patches, suggestions and bug reports from Assar, Davor Cubranic, Kyle Dean, Stephen Gallimore, Mark H. Wilkinson, Bjorn Helgaas, Steve Kilbane, Beirne Konarski, Bob Krzaczek, Alberto Nava, Arnold Robbins, Rich Salz, Scott Schwartz, Chris Siebenmann, and Richard Wolff.
Wily builds on the Sam distribution by Rob Pike and Howard Trickey, and originally used libtext by James Farrow. James Farrow also makes available a set of Unicode fonts for X at ftp.cs.su.oz.au/matty/unicode/
con = wily.Con()
Connection objects have no attributes and suport the following methods:
The following methods of a connection object act on a window.
Events are returned as tuples. Every event has at least two elements,
which are a window identifer, and an event type identifier (which is one of
wily.GOTO, wily.EXEC, wily.REPLACE
or wily.DEL). All of these event types except DEL
also have a from, to and string attribute.
e always handled by Wily, which then may inform the
remote program what has happened. In other words, the remote program
cannot stop the user modifying any piece of text, although it can repair
the twily-0.13.42/Doc/old/auug.tex 0000644 0236657 0000012 00000030320 10333201523 0015440 0 ustar 00oz staff 0000433 0000002 \documentclass[twocolumn]{article}
\usepackage{times,a4wide,epsfig}
\title{The Wily User Interface (Acme for Unix)}
\author{Gary Capell\\
Basser Department of Computer Science,\\
University of Sydney, 2006\\
Australia\\
gary@cs.su.oz.au}
%\keywords{text editor, window manager, user interface}
\begin{document}
\maketitle
\begin{abstract}
The Wile E. Interface (wily) provides much of the feel of
Acme [Pike95] in the Unix/X environment.
This paper gives a brief overview of wily's interface, explains
the implementation, and includes a brief note on window management.
\end{abstract}
\section{History}
Wily is a reimplementation of Acme \cite{acme}
for the Unix/X environment. Acme itself was
heavily inspired by the Oberon \cite{oberon} user
interface. A port was not possible because the
Acme implentation relies on Alef \cite{alef}(a
concurrent programming language not widely
available) and non-portable features of Plan 9
\cite{9BLabs}.
The name "wily" derives from Wile E. Coyote, an avid user of
Acme products.
\section{The Interface}
This is a sketch of the interface, for details
refer to \cite{acme} or \cite{wilyu}.
\subsection{Why Wily?}
Wily attempts to take maximum advantage of a
bitmapped display and three-button mouse, and to
require a minimum of effort from its users to
achieve common tasks. Common editing operations
that with other editors require incantations of
control keys or selecting from lists of menu
items can be done with with very little effort
using the mouse. There have been no user studies,
but subjectively, wily seems to substantially
streamline the editing and debugging process.
The interface integrates some of the functions of editor,
window manager, file browser and shell, as well as providing
an interface for external programs such as mail and news
readers and debuggers.
The interface is not obvious or familiar, but is composed of
a few principles consistently applied, and does not take long to
learn. Once learnt, the interface is sufficiently addictive that
the author re-implemented Acme to work under Unix, rather
than coping with other editors.
\subsection{Appearance}
\begin{figure}
\epsfig{file=pane2.ps,width=8cm}
\caption{Wily windows}{}
\end{figure}
Wily appears as one or more vertical columns,
divided into windows of scrolling text. Each column or window is
headed by a thin horizontal tag which includes the
name of the file or directory the window represents, some
commands, and a marker indicating if the file contains
changes not yet written to disk. The text in a window may represent
a directory, a file, an interface to some external program such as
a news reader, or the output from some command.
\subsection{Mouse Actions}
\emph{Any} text on the screen can be selected by dragging
with a mouse button. The actions of the different buttons are:
\begin{description}
\item[Button 1] highlights the text for later actions
\item[Button 2] executes the text as a command, e.g. selecting
the word 'make' will run the make command as if it were typed
to the shell. There is a small set of built-in functions that
are searched for first.
\item[Button 3] attempts to \emph{open} the selected text.
What this means depends on the text selected.
If the text is the name of a file or a search pattern for
a part of some file, the file is opened if necessary, and
the pattern is searched for. Otherwise, the text is treated
as a string to be searched for in the current window.
\end{description}
Panes and columns can be moved and reshaped by clicking
or dragging in the \emph{buttons} to the left of their tags.
There are no menus, and five special keys
(page up, page down, kill word, kill line, select
recently typed text). All functions
are accessed through mouse actions on text.
\subsection{Context}
Commands and file names are interpreted with respect to
the directory of the window
in which they originate, so for example selecting \texttt{foo.h}
with button 3 in file \texttt{/src/wily/bar.c} will open the file
\texttt{/src/wily/foo.h}. Similarly selecting
\texttt{make} in the tag of \texttt{/src/wily/bar.c}
will run \texttt{make} in \texttt{/src/wily/},
with any output
sent to a window labelled \texttt{/src/wily/+Errors} (which
will be created if necessary).
\subsection{Short Cuts}
Double-clicking with button 1 selects either a word, a line or a
piece of text delimited by parentheses, quotes or brackets.
If buttons 2 or 3 make a selection of
zero length (i.e. a click) the selection is expanded to a whole word or
a file name with optional address (respectively).
Mouse \emph{chords} are used for cutting and pasting. To move
some text from one place to another, it first is selected by dragging with button 1.
While holding button 1 down, if button 2 is pressed, the text is \emph{cut}.
The text can later be pasted into place by holding button 1 and then clicking
button 3. This action is easier done than said, and is much quicker than searching
for a menu entry.
A mouse chord is also used as a short-hand for the idiom of executing a command
with some argument. While clicking with button 2 in the command, if button 1
is then pressed, the text most recently selected with button 1 is given
as an argument to the command. For example, one could click with button 1
in a program variable, chord buttons 2 and 1 on the name of a script to grep
through source files, and get a list of all occurrences of that variable.
Clicking with button 3 on any of the occurrences on that list will open the relevant
file at the correct line.
The mouse cursor is often warped to where the program predicts the next
action is likely to take place. For example, after opening a file at a particular
address, the cursor is warped to that position of that file.
The escape key selects any text typed since the last cursor movement, making
it easier to select with the mouse.
\subsection{Miscellanea}
Wily by default displays text proportionally spaced, which is much more pleasant
to read than constant width text. A builtin function lets the font be
switched temporarily to constant
width when necessary.
Wily provides a full Undo/Redo history for each window.
Newly created windows are placed by the program, not the user.
While it is possible for the user to reposition or resize the window after
it is created, window-positioning heuristics generally make
such positioning unnecessary.
\subsection{A Short Example}
Gary has been using wily to develop a program called wily.
He clicks button 2 in the word \texttt{make} which he's left in the
tag of the window for his source directory. A new window
pops up labelled \texttt{/src/wily/+Errors}, containing
amongst other things the line \texttt{builtins.c:78: parse error before `)'}.
He clicks button 3 anywhere in \texttt{builtins.c:78:}. A new window
labelled \texttt{/src/wily/builtins.c} appears, with line 78 highlighted,
and the cursor warps to this line.
Gary sees that the last \texttt{)} on that line is unnecessary. He
selects it with button 1, and while holding button 1 down, clicks button
2 to cut out the offending parenthesis. The button of the window
changes, to indicate that the window represents a file which is "dirty".
Gary clicks button 2 in the word "Put" in the window's tag, to write
the file to disk. He selects \texttt{make} in the tag of the window
of the directory, and the cycle begins again.
This whole interaction required six mouse clicks and much less
time than the time to read about it.
\section{The Implementation}
Wily is a single-threaded event-driven server process. The events it
waits for are keyboard and mouse actions, and output arriving
on pipes shared with child processes. Text display is handled
by text widgets.
\subsection{Child Processes}
To execute a command that isn't built in, the wily server forks a child, modifies
the child's execution environment, and the child \texttt{exec}s the required command.
The current directory is set to that which
the command was invoked from. The child's file descriptors are set so that
\texttt{stdin} is redirected from \texttt{/dev/null},
\texttt{stdout} and \texttt{stderr} are redirected to a file
descriptor the server listens to for output, and file descriptor 3 is a socket open
to pass messages between the server and child.
The child also inherits some environment variables which are helpful for writing
scripts: \texttt{WILYLABEL} is the context the command was invoked from,
and \texttt{WILYARGLABEL} is the context of the argument to the command,
if any.
The parent adds the file descriptors passed to the child to a list of
file descriptors that it monitors for output, and remembers which directory
that stream is associated with, so that output will appear in an appropriate window.
More sophisticated client processes can communicate
with the server through a message
protocol. The message protocol is still being designed, but basically
when the user interacts with a wily window, wily sends messages to
the client program to let it know what the user has done. For example
a news reading client might display an overview of a news group, and
wait for a message from wily to indicate that the user has clicked
with button 3 to indicate that they want to "open" a particular news item.
Wrappers for the shell and Python\cite{python}
have been written to make writing programs
using wily as their interface even easier. For example, here is a simple mail interface
written in \texttt{rc}:
\begin{verbatim}
#!/bin/rc
id= `{wnew} # create a new window
wcmd $id Label $home/Mail/inbox
wcmd $id Tag inc scan rmm repl
wout $id scan # display scan's output
\end{verbatim}
On the author's system,
Wily's binary (not including X shared libraries) is one-fourteenth the size
of Xemacs'. The source code for wily (not including libtext, libframe, libXg) takes
3045 lines (measured by \texttt{wc -l *.[ch]}).
\section{Window Management}
One of the reasons wily was written was to experiment with alternatives for window
management. Acme's tiling windows are occasionally quite annoying. An earlier implementation
of wily used one window (and one process) per pane, which communicated using two
X cut buffers and named pipes. Window management was then passed on to
the X window manager. This had the benefits of providing
overlapping windows, multiple work spaces (using the virtual root window), and the
option of iconifying windows.
Unfortunately, this arrangement turned out to be much more annoying than
the tiling window management of Acme, when used with the large numbers of
windows that come into play when, for example, developing software using wily.
Sadder but wiser, a tiling window manager was added to wily.
\section{Strengths, Weaknesses and Further Work}
The things the author misses most when using another editor are:
mouse chording for cut and paste (alternatives seem very awkward),
opening a file to a particular line with a single mouse click, and editing
in a proportional font.
Some of wily's strengths may also be seen as weaknesses: heavy exploitation
of bitmapped terminal and three button mouse mean wily is unusable on glass
and paper ttys. The terse mouse command language is opaque and even intimidating
for new users. Occasionally it seems that arrow keys would be helpful.
Wily doesn't yet provide all the features of Acme. Notably missing
are sophisticated window arrangement and regular expression searching.
These features are towards the top of the wily To Do list, along with
minimizing portability problems, simplifying the code, and firming up the messaging
interface.
Built-in functions may soon be provided to allow selected text to
be piped through, to be replaced by the output of, or to be used as the input for
some executed command.
The display of text may also be experimented with. One possibility is
to provide an \emph{outline} mode where subtrees of a hierarchy may be
hidden.
\section{Availability}
Wily is available for anonymous FTP from \texttt{ftp.cs.su.oz.au} in directory
\texttt{gary/wily}. The author welcomes comments, suggestions and
fixes to portability problems.
\section{Acknowledgements}
Many suggestions on wily and this paper, and much of the code were received
from Bill Trost \texttt{trost@cloud.rain.com}. Libraries
used include libXg and libframe, part of the Sam distribution by Rob Pike and
Howard Trickey, and libtext by James Farrow.
\bibliography{wily}
\bibliographystyle{plain}
\end{document}
wily-0.13.42/Doc/old/python.sgml 0000644 0236657 0000012 00000007422 10333201523 0016171 0 ustar 00oz staff 0000433 0000002
Attributes