Implementing DrScheme Tools

Tools are designed for major extensions in DrScheme's functionality. To extend the appearance or the functionality the DrScheme window (say, to annotate programs in certain ways, to add buttons to the DrScheme frame or to add additional languages to DrScheme) use a tool. The Static Debugger, the Syntax Checker, the Stepper, and the teaching languages are all implemented as tools.

Libraries are for extensions of DrScheme that only want to add new functions and other values bound in the users namespace. See the DrScheme manual for more information on constructing libraries.

Tools rely heavily on MzScheme's units. See units for information on how to construct units. They also require understanding of libraries and collections.

When DrScheme starts up, it looks for tools by reading fields in the info.ss file of each collection (Technically, DrScheme looks in a cache of the info.ss files contents created by setup-plt. Be sure to re-run setup-plt if you change the contents of the info.ss files). DrScheme checks for these fields:

tools
(listof (listof string[subcollection-name]))
tool-names
(listof (union #f string))
tool-icons
(listof (union #f string[relative-pathname] (cons string[filename] (listof string[collection-name]))))
tool-urls
(listof (union #f string[url]))

The tools field names a list of tools in this collection. Each tool is specified as a collection path, relative to the collection where the info.ss file resides. As an example, if there is only one tool named tool.ss, this suffices:

(define tools (list (list "tool.ss")))

If the tool-icons or tool-names fields are present, they must be the same length as tools. The tool-icons specifies the path to an icon for each tool and the name of each tool. If it is #f, no tool is shown. If it is a relative pathname, it must refer to a bitmap and if it is a list of strings, it is treated the same as the arguments to lib, inside require.

This bitmap and the name show up in the about box, Help Desk's bug report form, and the splash screen as the tool is loaded at DrScheme's startup.

Each of tools files must contain a module that provides tool@, which must be bound to a unit/sig The unit must import the drscheme:tool^ signature, which is provided by the tool.ss library in the drscheme collection. The drscheme:tool^ signature contains all of the names listed in this manual. The unit must export the drscheme:tool-exports^ signature.

The drscheme:tool-exports^ signature contains two names: phase1 and phase2. These names must be bound to thunks. After all of the tools are loaded, all of the phase1 functions are called and then all of the phase2 functions are called. Certain primitives can only be called during the dynamic extent of those calls.

This mechanism is designed to support DrScheme's drscheme:language:language<%> extension capabilities. That is, this mechanism enables two tools to cooperate via new capabilities of languages. The first phase is used for adding functionality that each language must support and the second is used for creating instances of languages. As an example, a tool may require certain specialized language-specific information. It uses phase1 to extend the drscheme:language:language<%> interface and supply a default implementation of the interface extension. Then, other languages that are aware of the extension can supply non-default implementations of the additional functionality.

Phase 1 functions:

Phase 2 functions:

If the tools raises an error as it is loaded, invoked, or as the phase1 or phase2 thunks are called, DrScheme catches the error and displays a message box. Then, DrScheme continues to start up, without the tool.

For example, if the info.ss file in a collection contains:

(module info (lib "infotab.ss" "setup")
  (define name "Tool Name")
  (define tools (list (list "tool.ss"))))

then the same collection would be expected to contain a tool.ss file. It might contain something like this:

(module tool mzscheme
  (require (lib "tool.ss" "drscheme")
           (lib "mred.ss" "mred")
           (lib "unitsig.ss"))

  (provide tool@)

  (define tool@
    (unit/sig drscheme:tool-exports^
      (import drscheme:tool^)
      (define (phase1)
        (message-box "tool example" "phase1"))
      (define (phase2)
        (message-box "tool example" "phase2")))))

This tool just opens a window to indicate that it has been loaded.

2.1  Adding Languages to DrScheme

2.1.1  Adding module-based Languages to DrScheme

If a language can be implemented as a module (see module for details) and the standard language settings are sufficient, simply create an info.ss file in the collection where the module is saved. Include these definitions:

drscheme-language-modules
This must be bound to a list of collection path specifications or strings, one for each language in the collection. Each collection path specification is the quoted form of what might appear as an argument to require, using the lib argument (but without the lib). The strings represent relative paths starting at the directory containing the info.ss file. They are interpreted like string arguments to require.

drscheme-language-positions
This must be bound to a list of language positions. Each language position corresponds to the position of the language in language dialog. Each language position is a list of strings whose length must be at least two.

drscheme-language-numbers
This is optional. If present, it must be a list of a list of numbers. Each list corresponds to a single language from this collection. Each number indicates a sorting order in the language dialog for the corresponding string in drscheme-language-positions. If absent, it defaults to a list of zeros that has the same length as drscheme-language-positions. This will rarely be correct.

drscheme-language-one-line-summaries
This is optional. If present, it must be a list of strings. Each string is displayed at the bottom of the language dialog when the corresponding language is selected.

drscheme-language-urls
This is optional. If present, it must be a list whose elements are either strings or #f. Clicking the corresponding language's name in the interactions window opens a web browser to the url.

drscheme-language-readers
This is optional. If present, it must be bound to a quoted list of module specifications (that is, a quoted version of the argument to require). Each specification must be a module that exports a function named read-syntax. Each of these read-syntax functions must match MzScheme's read-syntax primitive's contract, but may read different concrete syntax.

If the module specification is a plain string, it represents a relative path starting at the directory containing the info.ss file. It is interpreted like the string arguments to require.

The lists must have the same length.

As an example, the Essentials of Programming Languages language specification's info.ss looks like this:

(module info (lib "infotab.ss" "setup")
  (require (lib "string-constant.ss" "string-constants"))
  (define name "EoPL Support")
  (define drscheme-language-modules
    (list "eopl-lang.ss"))
  (define drscheme-language-positions
    (list (list (string-constant teaching-languages)
                "Essentials of Programming Languages"))))

This info.ss file indicates that there is a single language in this collection. The module that implements the language is the eopl-lang.ss file in the same directory as the info.ss file. Additionally, the language dialog will contain Essentials of Programming Languages as a potential language. The use of the string constant teaching-languages ensures that EoPL's language is placed properly in foreign language versions of DrScheme.

For collections that define multiple (related) languages, if the language-positions contain multiple strings, the languages whose leading strings match are grouped together. That is, if two languages have strings:

  '("My Text" "First Language")

and

  '("My Text" "Second Language")

the two languages will be grouped together in the language dialog.

2.1.2  Adding Arbitrary Languages to DrScheme

With some additional work, any language that can be compiled to MzScheme's language is supported by the tools interface, not just those that use standard configurations and module.

Each language is a class that implement the drscheme:language:language<%> interface. DrScheme also provides two simpler interfaces: drscheme:language:module-based-language<%> and drscheme:language:simple-module-based-language<%>, and mixins drscheme:language:simple-module-based-language->module-based-language-mixin and drscheme:language:module-based-language->language-mixin that build implementations of language^s from these simpler interfaces.

Once you have an implementation of the drscheme:language:language<%> interface, call drscheme:language-configuration:add-language to add the language to DrScheme.

Each language comes with its own type, called settings. This can be any type the language designer chooses, but to aid documentation, we call it settings here. The settings type is expected to contain parameters of the language, such as case sensitivity, etc. The implementor of the language provides a GUI so the user can configure the settings and all of the language's operations accept a setting. DrScheme maintains the current settings for each language.

2.1.3  Language Extensions

Some tools may require additional functionality from the drscheme:language:language<%> interface. The drscheme:language:extend-language-interface function and the drscheme:language:get-default-mixin mixin make this possible.

For example, the MrFlow tool expands programs, analyzes it and then displays sets of values for each program point. These sets of values should be rendered in the syntax of the language that MrFlow analyzes. Since MrFlow doesn't apriori know which languages are available, it can call drscheme:language:extend-language-interface to extend the drscheme:language:language<%> interface with a method for rendering sets of values and provide a default implementation of that method. Tools that know about MrFlow can then override the value rendering method to provide a language-specific implementation of value rendering. Additionally, since the drscheme:language:get-default-mixin adds the default implementation for the value-set rendering method, all languages at least have some form of value-set rendering.

2.2  Creating New Kinds of DrScheme Frames

Each frame in DrScheme has certain menus and functionality, most of which is achieved by using the framework. Additionally, there is one mixin that DrScheme provides to augment that. It is drscheme:frame:basics-mixin. Be sure to mix it into any new frame class that you add to DrScheme.

2.3  Extending the Existing DrScheme Classes

Each of the names:

is bound to an extender function. In order to change the behavior of drscheme, you can derive new classes from the standard classes for the frame, texts, canvases. Each extender accepts a function as input. The function it accepts must take a class as it's argument and return a classes derived from that class as its result. For example:

(drscheme:get/extend:extend-interactions-text
  (lambda (super%)
    (class super%
      (public method1)
      (define (method1 x) ...)
      ...)))

extends the interactions text class with a method named
rawscmmethod1.

2.4  Expanding the User's Program Text and Breaking

Macro-expanding a program may involve arbitrary computation and requires the setup of the correct language. To aid this, DrScheme's tool interface provides drscheme:eval:expand-program to help. Use this method to extract the fully expanded program text in a particular language.

Because expanding the user's program may require DrScheme to evaluate arbitrary code that the user wrote, tools that expand the user's program should also allow the user to break the expansion. To help with this, the tools interfaces provides these methods: enable-evaluation and disable-evaluation. Since your tool will be expanding the program text, you should be both overriding enable-evaluation and disable-evaluation to disable your tool and calling them to ensure that only one expansion is happening at a time.

Finally, DrScheme provides the set-breakables, method. This method controls what behavior the Break button has.

2.5  Editor Modes

DrScheme provides support for multiple editor modes. Tools register modes via drscheme:modes:add-mode. Each mode is visible in the Modes submenu of the Edit menu. Initially, DrScheme only supports two modes: scheme mode and text mode.

DrScheme automatically selects a mode for each open file based on the file's extension. If the file ends with .txt, DrScheme uses text mode. Otherwise, DrScheme uses Scheme mode.

2.6  Language-specific capabilities

Drscheme's capability interface provides a mechanism for tools to allow languages to hide their GUI interface, if the tool does not apply to the language. Tools register capabilities keyed with symbols via. drscheme:language:register-capability. Once registered, a tool can query a language, via the capability-value method. The result from this method controls whether or not the tool shows this part of the GUI for DrScheme.

See drscheme:language:register-capability for a list of the capabilities registered by default.