Introduction to Sheets

The Sheets Hypercode Environment (or simply "Sheets") is an advanced programming environment for Java. In Sheets, programs are divided into fragments, which are stored in ordered containers called sheets. Sheets are themselves fragments, so you can have nested sheets.

Although a sheet-full of fragments looks a lot like a file, it can do things that a file can't. For example, the same fragment can exist on multiple sheets, so that you can show several different organizations of the same code. Note that these are not just copies of other code, but exactly the same objects. When a fragment is edited on one sheet, the change is instantly reflected on all other sheets.

By internally representing programs this way, Sheets can provide features that other environments do not. In particular, Sheets provides very good navigation facilities. For example, with a single keystroke, you can find the definition of a method, or any place that calls it.

Getting Started

The easiest way to learn about Sheets is to try it out on real Java code. If you have some existing Java code, you can use the new project wizard to create a database. Simply run sheets with no arguments. The wizard's dialogs allow you to say what files to import, what package to use, etc.

Alternatively, you can use the SunDial demo project:

    sheets SunDial.sheets

Basic Usage

When Sheets starts up, you will get three windows:
upper right corner
The projects window lists all the projects loaded into the database, and is the starting point for browsing.
lower right corner
The edits window lists the fragments which are currently being edited, see committing.
lower left corner
The context help window displays summaries of the definitions that are referenced at the current cursor location, and also has a status line. Closing the context help window exits sheets (but the database still exists, see databases.)
To use most of the features of Sheets, you will need to open an edit window containing some code. Navigate starting in the projects window. You can expand subsheets by clicking on the plus sign (+), or can open a sheet in its own window by double clicking on it. If you expand a code sheet in the projects window using +, you'll see the names of all of the definitions, but can't do any editing. Double-click on the sheet you opened (which now has a -) to create an edit window for that sheet. (If this seems too clumsy, don't worry. The query facilities described below allow you to warp directly to pretty much any fragment that you can name.)

The edit window has two panels:

Views

Note that the edit panel is not displaying all the code. Rather, each fragment is in the header view. For methods, it shows parameter lists, return types, and header comments, but not the implementation. Similarly, for fields it leaves out the initial value. The code is still there, it just isn't displayed. To see the entire fragment, double-click on it.

Most fragments support at least three views:

The easiest way to change to any view is to left-click on the control block, which is the funny dot in the sidebar to the left of the component. This will show you a menu which controls many aspects of the fragment's view. Also, under the Container menu are commands for changing the view of all fragments on the sheet.

Navigating Using the Mouse

As in any other editor, left-click will move the cursor to the location that you clicked at. If you double left-click, on a fragment, you'll text-select the word under the cursor and turn the fragment into the full view at the same time. Similarly, if you double click on something in the TOC panel, you'll expand it into the full view in addition to moving the cursor there.

Editing

Sheets supports all the usual text editor features, with a few twists for fragment-based editing. Fragment editing has been added on as transparently as possible, but some operations can only be done at the fragment level, and fragment select/cut/paste works differently than a purely text based approach does. 

Selection: 
Read this section or go Insane!

As in most editors, a mouse-based selection mechanism is used to indicate blocks of stuff that should be moved, deleted, etc. In Sheets, the current selection can be either a text selection or a fragment selection.  A text selection indicates both a block of text inside a single fragment and the fragment in its entirety. A fragment selection indicates several fragments in their entirety.

A text selection works basically the same as the selection mechanism in any text editor. The selection is indicated by a mouse drag from one end of the selection to the other. However, in Sheets a text selection cannot extend across a fragment boundary. If you attempt to select more than one fragment by dragging, you will be baffled because the drag is interpreted as a fragment move from the start location to the end location.

A fragment selection selects one or more entire fragments. There is no way in Sheets to select the end of one fragment and the beginning of the next. The fragment that the cursor is in is always part of the selection, so you can select a single fragment by just left-clicking on it. This selection is indicated by the black line drawn around the current fragment.

You can select multiple fragments by using shift-left-click and control-left-click. shift-left-click selects all of the fragments between the current fragment and the mouse position. control-left-click adds a single fragment to the selection without deselecting any of the already selected fragments. If there is a multiple selection, all of the selected fragments have their background changed to yellow and are outlined by a red line box.

If there is a multiple selection in effect and you want to go back to the normal mode of having a single fragment, you can double-click on the fragment. (Double-clicking is a highly magical operation. When in doubt, give it a try.)

Selecting Entire Sheets

Since clicks inside a sheet are normally interpreted as selection of fragments inside that sheet, some special way is needed to select an entire sheet. To select a sheet, click on the normally gray header bar of the sheet, which will then turn light blue. At this point, if you do text editing, it will edit the sheet description displayed in the header bar.

Note that this is not the same as selecting all of the fragments inside the sheet. If you cut and paste a sheet, you preserve the title and all of the other meta-information associated with the sheet.

When a sheet is selected, fragment operations such as Destroy will affect the sheet itself, and only implicitly affect the contents insofar as the affected sheet may be the only reference to those fragments. Some fragment operations such as Fragment/Cut are forbidden on the outermost sheet displayed in a window, but are still valid on all contained fragments including sub-sheets.

Edit Mode and Commit/Abort

To edit a fragment, left-click to put the cursor inside the fragment and start typing. The fragment will switch to the full view and turn pink when you do this. If you want to edit a fragment that is not in the full view, you may not be able to see the text that you want to edit. In this case, double-click somewhere in the header to put the fragment in full view, find the code you want, and start editing.

When you are done making changes, you commit the edit via the Commit Edit option on the right-click menu or the green Commit button which appears at the bottom of the edited component. This explicit commit step serves two purposes: it tells Sheets when to redo syntactic and semantic analysis, and it allows a change to easily be discarded. Instead of committing an edit, you can also abort it using the red Abort button or right-click Abort Edit. This discards any changes and places the fragment back as it was before editing began.

The pink color of an fragment being edited helps to to remind you that editing is incomplete. All fragments being edited also appear in the Fragment Edits window, which helps to keep track of multiple edits in progress. The ability to have several uncommitted edits makes it easy to back out of an exploratory modification which spans several fragments. (If you have too many uncommitted edits, you can use the Window/Commit Edits command in the Fragment Edits to commit them all at once.)

Note that changes to a fragment are not written to the database until you commit the edit, so any uncommitted edits will be lost on a system crash, power failure, etc. (If you really like living on the edge, you can turn off the auto-sync profile variable, which will write the database even less often, but will give you slightly crisper performance.)

A fragment must be syntactically correct before it can be commited. If there is a parse error, a pop-up error message is displayed and the fragment stays in edit mode.

Creating Fragments

The most general way to create a fragment is to use the Insert New submenu which is placed off the right-click menu and on the Fragment menu-bar menu. However, creating fragments is such a common operation that several shortcuts are also provided.

Java fragments and documentation paragraph fragments can also be created by editing an existing Java fragment or documentation paragraph. You can simply type what you want, without worrying about explicitly creating a new fragment. If you are at the end of a complete fragment, Sheets may automatically commit it and create a new fragment to contain your new text. Otherwise, Sheets will wait until you commit, at which point the parser will detect that there is more than one fragment in the result, and create several new fragments. When one fragment is replaced with two (or more), this replacement is done in all sheets that contain the fragment -- the new fragment is added to all sheets containing the old one.

Another way to create fragments is by "extending" an existing fragment. This rather poorly named operation has nothing to do with extending in the object-oriented sense, but simply means to create a new fragment like the current fragment and place it immediately after this fragment. Extending can be done by Fragment/Extend Fragment or by right-click Extend Fragment. Unlike the automatic extension that you get in the previous case, this operation only affects the current sheet. (This can either be an advantage or a disadvantage depending on the situation.)

If you find yourself with a new empty fragment which you don't want, you can get rid of it by committing or aborting it, or (usually) by simply hitting the enter key.

Java for Sheets

Since each method or other class member is a separate fragment in Sheets, and is not syntactically contained in a class definition, Sheets must have some other way of keeping track of which class a fragment belongs to. The class of a Java member is indicated by a minor syntactic change. Instead of the main method in the Foo class being displayed as:
    public static void main (String args[])
the class is typically displayed as part of the method name:
    public static void Foo.main (String args[])
A static initializer in the class Foo looks like:
    Foo.<init> {
    }
When you are entering a Java object, you can choose to explicitly specify the class that it belongs to, or you can leave it out and leave Sheets to figure out for you. You also have the option to turn off display of the class name, via click-control-block show without/java class name.

Similarly, you can explicity specify the package along with the class for each individual fragment, and choose to explicitly display the package name with the fragment. (Typically, you won't need to worry about packages for Java fragments as long as you create new fragments by extending old ones.)

Removing and Destroying Fragments

Sheets distinguishes between removing and destroying a fragment (we avoid the ambiguous term "delete".) Removing a fragment from a sheet means that the fragment will still exist, just not on the sheet you removed it from. Destroying a fragment, on the other hand, destroys it everywhere. To remove the selected fragment, select Remove fragment from the Fragment menu. To destroy a fragment, you can select Destroy fragment from the same menu.

If you edit a Java or Paragraph fragment to contain no text and then commit it, the result is no fragments. This is the same as destroying the fragment.

If you remove the last reference to a fragment, then it is automatically destroyed. In this case, removing and destroying are equivalent.

Cutting and Pasting Fragments

Pasting fragments is not the same as pasting text. If the clipboard contains entire fragments, then pasting fragments will insert all those fragments after the selected fragment in the current sheet. If you paste those fragments as text, you will insert the text of all those fragments inside the current fragment.

Both of these are useful operations, so there are two separate commands: Fragment/Paste and Edit/Paste. By default, these are bound to ctrl-shift-V and ctrl-V.

The way that you get fragments into the clipboard is by using Fragment/Cut (ctrl-shift-X). If multiple fragments are selected, multiple fragments will be cut. Since there is often both a text and fragment selection, this command is distinct from Edit/Cut (ctrl-X). If the clipboard contains only text, then it can't be pasted as a fragment.

You can copy a fragment from one sheet to another by selecting the fragment and using Fragment/Copy (ctrl-shift-C.) When you paste the fragment on the new sheet, you will then have the same fragment on two different sheets. If instead, you want to make a new fragment with similar text in it, you can paste the fragment as text within an editable fragment.

Cutting vs. Removing

Cutting a fragment is almost the same as removing it. Because the fragment may continue to exist in some other sheet, cutting a fragment is not a reliable way to remove it from the program. You should instead use Fragment/Destroy or right-click/Destroy Fragment, see Removing and Destroying. We recommend using cut only as a way to move fragments from one sheet to another, and to use remove or destroy when you want to remove or destroy.

Moving Entire Fragments Using Drag-and-Drop

Sheets support limited drag and drop. You can drag a fragment around on a sheet by holding down the mouse button over the fragment (not over the control block), moving the cursor to another fragment, and letting go of the mouse button. When you are dragging a fragment, a black line is drawn at the place where the fragment would be inserted if you drop at the current mouse position. You can do the dragging in either the edit panel or the TOC panel, although you can't start your drag in one panel and end in the other. If your mouse drag never crosses the fragment boundary, it will be interpreted as a text selection; your selection will turn yellow.

Deficiencies in AWT prevent the implementation of drag and drop between separate windows. However, you can drag a fragment between sheets when the sheets are displayed as inline sub-sheets in the same window.

Word Completion

The complete-java-word command will attempt to complete the word under the cursor. If there is a unique completion to what you are typing, it will finish typing the identifier for you. If there are several possibilities, it will only insert the text which is common to all completions and then stop. The complete-java-word command is by default not bound to any key, although we recommend binding it to the TAB key. See the Customization section for details on how to do this.

Undo

Sheets has two different forms of undo: local undo and global undo. Local undo is capable of undoing text edits that happened during editing of a fragment before the edit is commited. Each edited fragment has its own local undo history. Local undo is invoked by Edit/Undo (ctrl-Z) and Edit/Redo (ctrl-Y) or right-click Undo Edit.

Global undo (Edit/Global Undo) backs up the state of the entire database, which will undo operations such as committing an edit, destroying a fragment or moving a fragment within a sheet. When you run the global undo command, it pops up a dialog that allows you to step forward and backward through the global undo history.

There are two text areas describing commands in the history. The bottom pane displays the command which will be undone if you click on the Undo button. The top pane displays the command which you just undid (if any), and which will be redone if you click the Redo button.

When you've undone all the stuff that you want to undo, click Done in the global undo dialog. This closes the window and removes the commands that you have undone from the undo history; they can then no longer be redone. Normal sheets browsing can be done while the global undo dialog is active, but any command that modifies anything will close the dialog as though you had clicked Done.

NOTE:  The global undo facility is still somewhat experimental.  We believe that it is stable enough to do its job without corrupting your data, but it will quite possibly wreak havok on your on-screen displays.  In this case, your best bet is to simply close and then re-open the affected windows.

Navigation

One of the most important aspects of Hypercode is the ability to easily explore the underlying structure of the program. Sheets semantically analyzes Java code and automatically links identifiers to their uses, definition sites, etc. In addition, attributes can be used to create explicit links between conceptually related fragments.

Sheets only knows about code that is in the database. If you want Sheets to know about third party libraries, or even standard Java libraries, then you must make sure that these libraries or their summaries are loaded into your Sheets database.

Querying

The Sheets query mechanism lets you search for fragments with particular properties. To begin a general query, select Search/Query. This will pop up the query dialog, which provides an interface for performing queries. Performing a query means specifying two things: what you're looking for, and what you want to do with it once you find it. The query dialog allows you to look for a variety of different things, and hopefully that part of the dialog is self explanatory.

Once you have specified what you're looking for, you press one of the buttons on the right of the dialog. This tells Sheets to find what you're looking for, and also what to do with the results. The buttons require some explanation:

Do What I Mean (DWIM)
Attempts to guess how you want the results displayed. If the query results in fragments that are all on one sheet then DWIM is equivalent to View in context. Otherwise, DWIM will create a new sheet containing the fragments. (Note that this is the default behavior if you simply hit ENTER.)
View in context
Shows you a sheet that already contains the fragment. If the query found more than one fragment, but they are all on the same sheet, then matching fragments will be selected. If this happens, often you will want to type right-click/Copy to Temp Sheet (ctrl-N) in order to place the matching fragments where you can examine them without overlooking anything. If more than one sheet contains the fragment, a heuristic is used to rank the sheets, and the best sheet is displayed. If this is not the context you are looking for, you can step through the ranking by repeatedly using the show-fragment-in-context command (ctrl-G.)
Make new sheet
Creates a new sheet which contains all the fragments you were looking for. With this option, you can (for example) create a sheet with all fragments that reference the method foo(), and use that new sheet to add a new argument to all calls to foo().
Add to current sheet
Takes the fragments in the result set, and adds them to the end of the sheet you launched the query from. If a fragment in the result set is already on the current sheet, it is not added again.
Make Graph
Takes the result set of the query, tries to find relationships between them, and then makes all of these into a graphical display. At present, the only query that produces a meaningful graph is the Hierarchy query.
Because queries are very common operations, there are a lot of shortcuts available. Most of the time, you can just right-click on a fragment -- the right-click menu prominently features the queries which are most meaningful for the given fragment. There are also many keyboard shortcuts for launching the query dialog with the defaults set intelligently; for details see the section on Navigation commands. Inside the query dialog, you can use control key combinations instead of pressing a "how to show it" button, or simply hit ENTER for the DWIM behavior.

Context-Based Queries

The general query dialog operates on names, and so doesn't let you look up the possibly applicable methods to some particular call. This capability is provided by the context help window and shortcuts such as go-to-definition (middle-click).

To tell which methods are applicable, Sheets needs to be able to determine the type of Java expressions. Sheets provides contextual information even on code which is potentially incorrect due to edits in progress.  It employs heuristics which usually make a good guess about the meaning of your code, but can make no guarantees when it is dealing with code which is incomplete or syntactically incorrect.

However, usually when Sheets doesn't find a definition, it is the code that is wrong. The method may be private or in the wrong package or not exported or an instance method being called from a static method, etc. Try doing a general query for definitions of the name to see what the definition really is.

The Context Help Window

The context window (usually found in the lower left corner of the screen) is a special sheet which provides information about the text under the text cursor. For instance, when you are in the middle of writing a method call, the context window will show you a summary of the method you are calling. Also, if you type in part of an identifier, the context window will display all possible completions of that identifier.

The context window is itself a sheet, albeit a somewhat unusual one. You can select items in it, change the default view, even edit fragments in the context window. Also, you can double-click upon it to show the entire fragment in its original context. (This is a shortcut for the show-context-sheet command.)

The context help display acts somewhat differently when you are sitting inside the argument list of a function. In this case, it considers only the class of the instance whose method is being invoked, and not the classes of any arguments that may be in the call. This ensures that you see all of the useful options when you are in the process of changing the argument list.

For efficiency purposes, the Sheets waits for a short time before updating the window. If you issue several commands in quick succession (e.g. by holding down the BACKSPACE key) the context window will only update after the last command. The delay defaults to 100 msec, but can be modified via the context-help-delay profile variable.

The Affected-By Sheet

When a command (such as committing an edit) incompatibly changes one or more Java definitions, all the fragments which might be affected by this change are automatically logged to the affected-by sheet. This is the code which needs to be examined to see if it depended on some aspect of the old interface, in which case it must be updated also.

The affected-by sheet is a special temporary sheet displayed in a TOC-only panel. When visible, it is found in the lower right corner of the screen. Each time an incompatible change is made, a new subsheets is created containing all of the affected fragments. The subsheet is given a description computed from the changed signatures.

Since the affected-by sheet is a sheet, all of the normal navigation capabilities are available. Usually what you do is find the change you are interested in the affected-by sheet, then double-click on that line. This will open the subsheet in its own full-size window where you can examine the code in detail.

Other Forms of Navigation

The Highlight string option in the Search menu will highlight a string (in red) anywhere it appears on the sheet (but only if the fragment is in the full view). Most queries will set the search string whenever a new sheet is created.

The Search and Replace option in the Search menu provides traditional string-based search and replace. This command will search only the current sheet.

Working With Sheets

Sheets provides several facilities to help you organize your code. The most important, of course, is the sheet. You can put fragments in any order, and you can even have the same fragment on multiple sheets.

Navigating Hierarchies of Sheets

Sheets are fragments, and thus may also be placed in sheets. This is used to create hierarchical organizations. When you first open a sheet, the contents of subsheets are normally not displayed. When a subsheet is not expanded, the TOC displays only the title of the sheet, and the edit panel (if any) displays only the description:

The main indication that this is a subsheet and not a text paragraph is that there is a plus sign (+) at the left of the fragment in both the TOC and the edit panel. If you click on the +, the contents are displayed, and the + changes to a -:

Clicking on the - hides the contents once more.

If you want to open the subsheet in a new top-level window displaying only that sheet, double-click on the TOC entry for the sheet. Double-clicking in the edit panel is another way to expand the sheet inline.

If there are highly nested sheets, you may want to recursively expand all subsheets of the current sheet. This can be done using Container/Recursively Open Containers (or right-click Recursively Open Containers from any container).

Cut and paste can be used to insert an existing sheet as a subsheet of another. One way to do this is Container/Copy Top Level Container then Fragment/Paste at the desired location. See also selection. A temporary sheet can be made permanent by inserting it as a subsheet of a permanent sheet.

Creating Sheets, Sheet Properties

To create a new sheet, go to the place where you want the sheet to appear and use Container/Insert New/Sheet or right-click Insert New/Sheet. This will bring up the sheet properties dialog:

The title of a sheet is a short name for the sheet which is displayed in the TOC. The description of a sheet is displayed in the gray area at the head of the sheet, and may be as much as a paragraph or so of text. You can leave the description blank in the properties dialog, then edit the description once the sheet has been created.

The most important setting in the properties dialog is the Kind button:

Normal, unexported sheet
Not clear how normal this is, but an unexported sheet's contents are not exported even if it does contain code. Unexported sheets are useful for library summaries. If a sheet doesn't contain code it is harmless to make it unexported.
Export to single file:
This option does not currently work.
Export each class to its own file
This is the setting to use on sheets containing code that you want to be exported.
Display sheet like menu
Normally, when a sheet is put in its own window, you will see both the edit panel and the table of contents panel. Sometimes, however, it's more convenient to hide one of the panels. You can do this on a temporary basis by using the Show Table of Contents and Show edit panel menu options in the Container menu. You can also hide the edit panel permanently by using the Container Properties dialog and setting the sheet's kind to Menu. For instance, you might want to do this to a sheet that contains only other sheets.
Root sheet
This is the kind of the projects sheet. You can't actually change to or from this kind.
You can change the properties of an already existing sheet using Container/Properties. However If you display the attributes of a sheet, you will see that title and description are also attributes. Were it not for historical reasons, all sheet properties would be attributes. right-click Show Attribute/Title is now the preferred way to set the title.

Attributes

In addition to its content text or fragments, all fragments also have attributes. Attributes are additional bits of information that have been attached to a fragment. If you do click-control-block Show Attribute/Package on a Java fragment, you'll see something like this:

Attributes are displayed at the end of the fragment that they belong to. Each displayed attribute is marked by an red-outlined icon in the left margin. Its value is indented to help set it off from the surrounding fragments.

More then one attribute can be displayed at once. If you display the documentation attribute on the SunDial class, you will see this:

There are two kinds of attributes: string and list. documentation is a list attribute. It contains fragments, and its value can be manipulated very much like a sheet. package is a string attribute; its value is a plain string, in this case, the name of the package that the SunDial class is defined in. List attributes are a form of typed hyperlink, whereas string attributes are a simple property of the fragment.

Defined Attributes

The set of attributes supported is fixed, though it can be fairly easily extended by modifying the Sheets source code. At present, defined attributes include:
see-also
A list attribute used to point to other fragments that are in some way related to this one. If fragment A adds B to its see-also, A is automatically added to B's see-also.
documentation
documentation-for
These list attributes are used to link code to its documentation. If A has B in its documentation then B automatically as A in its documentation-for.
project
This string attribute specifies the project of a fragment. Note that the project of Java fragments is specified once for each Java package, so can't be set directly.
package
Specifies the package of a Java fragment. In ordinary file-oriented java, the package is specified as part of the file; in Sheets there are no files and fragments must be usable out of context. For this reason, the package is specified independently for each fragment.
fragment-kind
archival-ID
These read-only string attributes display some information about how the fragment is represented in a project dump file.
exports
This list attribute contains all of the contents (methods, fields, etc.) for a Java class fragment. You may wish to explicitly manipulate it either to specify the order in which contents should be exported, or to insure that comments will be exported.
file-headers
file-header-for
These list attributes link Java comments and file-header fragments to Java classes. They specify precisely what should be written out at the top of the file when the class is exported.
There are a number of other attributes for more obscure fragment types such as sheets or images. They tend to be self explanatory, so you can simply experiment with them via click-control-block show attribute/show all.

Showing and Hiding Attributes

As we have already seen, individual attributes of single fragments can be shown via click-control-block Show Attribute and the attribute name:

On this menu each displayed attribute is has a checkmark displayed. If a displayed attribute is selected, it will be hidden. Show All shows all of the possible attributes, and Hide All will hide all displayed attributes.

Each displayed attribute has an icon, such as the book for documentation. If nobody has bothered to come up with a clever icon, a red X will be displayed, as for package. Clicking the icon for any displayed attribute will hide it. Furthermore if the attribute value is "interesting", then the attribute icon will always be displayed even if the attribute is hidden. In this case, you can display the attribute by clicking its icon.  (Typically an attribute is considered interesting if it is a non-empty list. However, other attributes such as errors may be defined as interesting.)

Editing Attributes

String and list attribute values are edited in very much the same way as text and sheet fragments. Simply select them and start typing. Typically, they will turn pink to show that you are editing them. Some attributes, however, are read-only.  In this case, Sheets will simply beep at you.

As with a text fragment, you must commit the edit when you are done. Each attribute is edited and commited indepdently from other attributes and from the parent fragment.

Constraints

In some cases, you will end up with a sheet whose entire purpose is to collect together fragments with a specific common property. For example, if you run a query for all references to method foo, you expect the sheet to only contain fragments which refer to foo. Ideally, it would also be automatically updated whenever you add new references to foo in other methods. Sheets accomplishes this by constraining the contents of the sheet. While the internal mechanism is fairly complicated, the results are fairly straightforward -- most sheets produced by queries are automatically updated so that they are consistent with the original intent of the query.

Occasionally, when you edit a fragment from within a constrained sheet, you will end up changing it in such a way that it no longer belongs on that sheet. Rather than magically removing the fragment as soon as it is committed, Sheets lets you choose how to handle the situation. You always have the option to either cancel the commit operation and keep editing or to commit anyway and let the fragment disappear. Sometimes you are also given the option of permanently removing the constraint from the sheet. If you choose this option, the sheet will go back to behaving normally, without doing any magic updates.

If you try to add a fragment to a constrained sheet which doesn't belong there, or to remove a fragment that does belong there, Sheets will simply beep at you without taking any action.

Several list-valued attributes also have constraints. For example, the exports attribute for a Java class fragment must contain all of the fields, methods, and constructors for that class. It is not allowed to contain fields, methods and constructors for other classes, nor any non-Java fragments. (Note that this leaves a middle ground -- comments are permitted but not required by the constraint.) Constraints on attributes cannot be deleted.

Some attributes have a special bi-directional constraint. Each fragment in a bi-directional attribute is connected back to the containing fragment via a corresponding attribute. For example, if fragment foo is contained in the documentation attribute of bar, then bar is contained in the documentation-for attribute of foo. Other bi-directional pairs include file-headers/file-header-for and see-also/see-also. Note that the connection goes both ways -- you can add documentation for a fragment by simply adding it to the documentation-for attribute of the doc fragment. 

The Sheets Database

All the code, documentation, etc. that is known to Sheets is stored in a database. A Sheets database is stored in a non-human-readable binary format in a file whose extension is .sdb. The database provides day-to-day persistence of hypercode so that you won't lose anything if you want to log out or if the machine crashes.

The database is only brought up to date at specific sync points, see sync-database. If Sheets never reaches a sync point due to a crash, then there will be dbname.sdb.log and dbname.sdb.lock files in addition to the .sdb file. You must delete the lock file (but not the log file) before sheets will let you resume using that database. Normally the lock file provides a way for Sheets to protect against the possibility that you might have an existing Sheets process using that same database.

You can have many database files, however we suggest that you only modify any given project in one particular database. Otherwise, you risk getting confused about which version is current. Also, Sheets may assign the same object ID to different objects in the different databases. 

When you create a new database, it will be initialized with the contents of prototype.sdb from the sheets home directory. This allows you to, for example, always include summaries of the standard Java libraries in any database you create, by simply executing

    sheets -exit prototype.sdb Java.sheets
from the sheets home directory.

Projects

Projects provide a way to break a hyperprogram up into manageable chunks that can be read into or written out from the database. Projects provide the framework for exporting hypercode from your database so that it can be imported into other databases.

In general the project of each fragment is specified by its project attribute. However with Java fragments, the project is specified once per package. The only Java fragments that it is possible to set the project of is a package header, or members of the magic default package (<default>). When you set the project of a normal package, the project of all fragments in that package changes too. However, since it is impossible to create a declaration for the <default> package, you must instead explicitly specify the project for each fragment in this package. (Note that this is a useful feature, not a limitation. It keeps anyone from trying to assert "ownership" of your small demo programs.)

With documentation fragments and sheets, the project is settable, and you must take some care to set it correctly.

The command Container/Change Project Of Everything Reachable is very useful for splitting an existing project into multiple projects or for fixing a sheet hierarchy where some of the fragments are in the wrong project. 

Creating Projects

The easiest way to create a new project is using the new-project wizard:

After you give the project name, choose one of these options to say what should be in the new project:

Import files
Import some already existing Java files into Sheets. See the import dialog.
New applet
Create a skeleton applet.
New application
Create a skeleton application.
Empty project
Put nothing in the project for now.

Importing Existing Java Code

File/Import... brings up the import dialog:

If code is exportable then Sheets will write out the latest version of the code before running the compiler. Normally you want to import Full source. The Summary Only option is used for creating summaries of Java libraries which you use but don't compile.

Unfortunately, this dialog doesn't allow you to specify the project, so unless brought up via the new-project wizard, it will simply inherit the project of whatever fragment is selected, and will be inserted immediately after the selected fragment. If you wish to create a new project with the imported code, you should probably use the new-project wizard instead.

Project Dump Files

A project dump file records the state of a project. Once it is written out by Sheets, it can be read into another Sheets database. Dump files are in a nominally human readable ASCII format in order to allow the use of standard tools.

The dump file format was specifically designed for use with version control systems, such as CVS. Not only can you check a dump file into CVS, other operations, like diff and merge, will also work. Though we've been doing it, can't really recommend using standard version control tools with sheets. There is an all-sheets version control solution in the works.

At any time you can save all loaded projects to their corresponding dump files with the File/Save Dump File... command. This brings up a dialog showing all of the loaded projects and the dump files that they will be saved do. You can change the file to be in a different directory, but the name of the dump file must be the project name. When you click OK, the dump file is rewritten to contain all of the fragments currently in the project.

You should periodically save out dump files and then back them up somewhere. This allows you to recover if there is a disk crash or if a Sheets bug somehow corrupts the database.

When working with multiple developers, it is frequently necessary to merge your work with others. Many version control systems provide some merge tool to do this semi-automatically; when the merge tool can't figure out how to merge properly, it will create merge conflict annotations in the result. If there are merge conflicts, you'll have to edit the dump file by hand to fix them Be careful not to insert empty lines in the middle of a fragment in the dump file. All fragment text (including apparently empty lines) should have a single leading space. 

Standard Summary Projects

In order to provide the maximum possible assistance to you, Sheets needs to know about the standard Java libraries that you are working with. To this end, we provide several optional distributions which contain prebuilt summaries of JDK1.1, Swing, and JDK1.2. These distributions contain both ".sheets" files which you can explicitly import into any database, and "prototype.sdb" files which you can install in order to automatically include the desired summaries in any database you build.

These projects are called summaries because they doesn't contain the actual code for all the stuff in Java -- they only contain the headers that are needed for context help. This saves memory and load time. You can create summary projects for any library for which you have source code. Simply use the "new project wizard" to import the source directories while specifying "Summary Only" and turning off the "Make exportable" checkbox.

Running the Compiler

Sheets is not a compiler, but it provides nice integration with off the shelf, file-based compilers. There are however various subtleties and novel concepts that you need to understand in order to successfully use Sheets' compilation facilities. Also, you may need to perform a bit of additional setup in your .sheetsrc file to use compilation; see the section on compilation variables for details.

Exporting Files from Sheets

Sheets code lives in the database, whereas standard compilers want text files as input. For this reason, Sheets exports any changed code (or documentation) to the appropriate file before running the compiler. Classes are exported to files in the current working directory or subdirectories of it determined by the Java package/directory naming rules.

Exporting is the process of taking Sheets hypercode, and turning it into files suitable for compilation by external file tools (such as Java compilers or documentation processors). Files can be exported by File/Export/All Fragments or File/Export/Dirty Fragments. It shouldn't be necessary to export all fragments, but it may help if something isn't being exported that should be. Exporting all code will also force a complete recompilation.

Export Dirty Fragments exports Java classes and documentation that have somehow changed since the last export. This not only includes classes whose members have changed, but classes that might have been affected by some incompatible interface change. Thus, it generally touches all of the files that need to be recompiled. Note that Files/Compile implicitly exports dirty fragments.

Limitations of Exporting

Since Java compilers expect each file to hold one class, exporting is done by class. A class is exportable if it is contained in some sheet which is exportable. When a class is exported, all members will be exported regardless of whether they appear on an exported sheet or not. The members of a class need not appear on any particular sheet resembling the .java file. This allows classes to be split across multiple subsheets or generally organized any way you please.

There is a potential problem inherent in the "free form" class organization described above. Although Sheets can determine all of the members in a class regardless of where they live, it doesn't necessarily know what order to write them in or whether any given comment belongs with the class. Therefore, all of the code which should be exported with a class are collected in the exports attribute of that class.  The attribute is constrained so that it can only contain members of the class or comments. The constraint also guarantees that all of the members will be included. You cannot delete class members from this attribute, although you can of course destroy them.

When you import a file into Sheets, the exports attribute will be initialized with all of the class contents, including comments, in the exact order that they occurred in the file. This helps ensure that if you later look at the file with some tool other than Sheets, you will see roughly what you had before. Depending upon how you create new members, they may not appear where you expect. You can, however, open the attribute and reorder the members however you wish.  Similarly, you can guarantee that comments will appear where you want them by explicitly adding or moving them in the exports attribute.  (Note that if you create new Java components by typing at the end of old ones, things will generally end up where you expect them. If you create brand new components or use the explicit extend command, members will more likely be tacked on to the end. This behavior may be changed in future versions of Sheets.)

You can also use attributes to control the exact form of the file header for a class file. The file-headers attribute can contain either arbitrary Java file header fragments (i.e. lists of import clauses with an optional package clause at the top) or Java comments.  These will be written out more or less verbatim, except that Sheets makes sure that the package name comes before any imports clauses.

If you don't have any file-headers specified, Sheets will try to find a Java file header with a package clause for the class's package and will use that as the file header. However, for the <default> package, you will have to explicitly specify any headers.

When you import a file into Sheets and then re-export it, there will be some unavoidable minor changes. The changes are as follows:

These changes should be small enough that you can easily switch back to using other tools if (for some inexplicable reason) you should decide that Sheets is not right for you. However, since Sheets will not detect changes made by other tools, you should not try to use other tools such as text editors at the same time you are using Sheets.

The Compile Dialog

File/Compile... brings up the compile dialog:

This dialog gives you the option of compiling files which have changed since the last invocation or recompiling your program's entire set of files. When you select either compile or recompile, Sheets will export all dirty fragments and invoke the compiler.

If compiling your program produces errors, Sheets will create a new sheet of all fragments that had errors in them. Each line that had an error is flagged by a red arrow in the left margin. At actual error message text is displayed in the Errors attribute at the bottom of the fragment. Since the error sheet is a sheet, you can edit the fragments right there in the error listing. If you want to see the fragment in context, use right-click Show In Context.

The default keybindings include use of the F5 key as the keyboard shortcut for compile-program, while control-F5 is the shortcut for recompile-program.

The Compilation Commands

What the compile dialog actually does is give you the choice of two different command lines to run, runs the command you select, and interprets the output as compiler error messages. It is only a convention that one command compiles changed classes and the other compiles all classes. If this is what you want, you must run the correct commands to actually cause this to happen.

Any changes to the compilation commands that you make using the dialog will only last as long as your current Sheets session. Usually you will set the default values for these commands in your .sheetsrc using the compile-command or recompile-command variables. (If you are running under a non-Windows platform, the program in the compilation commands must be specified as an absolute pathname; this is a limitation of Java. If you are running under Windows, Sheets will filter your command through a special program named "wrapper" which will (amongst other things) look things up in your PATH environment.)

If you are building a small java-only program, you can probably simply use the default compile command: "javac %java%". The string %java% is a substitution variable which is replaced with a list of all ".java" files which need to be recompiled. (This list will contain both classes which have been changed within sheets and classes which have been affected by interface changes.) The default recompile command is "javac %JAVA%". The new substitution variable refers to all Java files managed by Sheets. Note that there are other similar substitution variables: %class% and %CLASS% refer to the ".class" files for Java classes. For exported XML (DocSheet) fragments, you can use %xml%, %XML%, %html%, %HTML%.

Compiling Using Make

If you have a more complex program which contains a mixture of languages, or if you are dealing with a large number of classes, you will probably be better off using a make utility. This will give you much greater control over how the program will be compiled, as well as providing the flexibility to compile non-Java sources with the same command. For instance, the Sheets makefile runs the parser generator JavaCC before running the Java compiler. To compile using make, you must both configure the compilation commands and create a makefile.

Typical .sheetsrc configuration of compilations commands to run make under windows would look something like this:

  set compile-command "nmake "
  set recompile-command "nmake recompile"
(If you are on a non-Windows machine you should remember to specify an absolute pathname for your "make" utility.) When you export files, Sheets will create a file named export-map.mak, which is useful for including in makefiles. It defines two makefile variables, EXPORTED_SRCS and EXPORTED_OBJS. EXPORTED_SRCS is all filenames that were exported since Sheets was started. EXPORTED_OBJS is the name of the object files that correspond to the source files exported. For example, if you exported Foo.java and Bar.java, then export-map.mak would look like
    EXPORTED_SRCS = Foo.java Bar.java
    EXPORTED_OBJS = Foo.class Bar.class
The Makefile for building Sheets makes use of this facility. It does something like this:
    !if EXIST(export-map.mak)
    !message Using export map
    !include export-map.mak
    !else
    !message No export map available
    EXPORTED_SRCS =
    EXPORTED_OBJS =
    !endif

    compile : $(EXPORTED_OBJS)

    # How to turn .java files into .class files
    .SUFFIXES : .java .class
    .java.class : 
            $(JAVAC) -nowarn $*.java
(Note that this example will work only under Microsoft nmake, because it uses the nmake preprocessor and nmake inference rules to turn .java files into .class files.) This example assumes that all editing is done inside Sheets, and that before Sheets is launched, all .class files have been compiled. For a slightly more robust version, see the real Makefile distributed with the Sheets source code.

Command Line Usage

Sheets recognizes both switches and files in its command line. Files are interpreted according to their extension:
*.sheets
Load these project dump files into the database.
*.sdb
Specifies the file to use to hold the sheets database. If no sdb file is specified, the database is defaulted to sheets.sdb. If the sdb file doesn't exist, a new empty database is created (and the new-project wizard is run for you).
*.java
Import these Java files into the database. This doesn't set the project to anything useful, so we'd recommend that you use the File/Import File or File/New Project commands instead.
These switches are recognized:
-uusername
Specify the user name used to create unique object IDs in project dump files. Defaults to user name in the Java environment. Note that there is no space between the -u and the username.
-hsheets-home
Specify the location of the directory in which you installed the sheets distribution. This is used to look up various special files, such as the "beep" sound. Typically, you will modify sheets.bat to set this automatically.
-add-to-existing-db
Don't ask for confirmation when a database exists and .sheets or .java files are specified as inputs. Just add the stuff to the existing database.
-export
Export all the stuff in the database after it has been initialized.
-exit
After doing all the command line processing, exit instead of waiting for interactive input. This is useful in scripts.
The sheets.bat file in the distribution specifies various switches to the Sun Java VM and (redundantly) defaults -u to %USERNAME%. You will probably also wish to edit with an appropriate value for the -h switch.

Customization

Users may customize Sheets by creating a file named .sheetsrc. At startup time, Sheets will look for this file in the current directory, and then in the user's home directory (which under Windows is specified by setting the HOME environment variable), and then in the Sheets installation directory. If a .sheetsrc file can not be found, default user settings will be used.

In a .sheetsrc file, any line that begins with a pound sign (#) is considered a comment. .sheetsrc files are case sensitive. Mainly what you do in .sheetsrc is bind commands to keys and set variables. Sheets variables are used both for system configuration such as file locations and for user preferences. For more information, check the reference manuals for commands and variables.

The distribution contains several "typical" .sheetsrc files. default.sheetsrc is a nice neutral starting point, while nkramer.sheetsrc and rgs.sheetsrc represent the more idiosyncratic preferences of several Sheets developers. The former follows a minimalist Win32 style, while the latter reflects more of an Emacs flavor. 


[Go back to index]