racket/collects/scribblings/tools/tools.scrbl
Matthew Flatt 9453aaaccf assign authors to manuals
svn: r11287
2008-08-15 20:16:06 +00:00

499 lines
18 KiB
Racket

#lang scribble/doc
@(begin
(require scribble/manual
(for-label drscheme/tool-lib)
(for-label scheme/unit)
(for-label scheme/base)
(for-label framework/framework))
(define (File x) @tt[x])
(define (FileFirst x) @tt[x]) ;; indexing missing
(define (item/cap x . ys)
(apply item (bold (format "~a" x)) ": " ys)) ;; indexing missing
)
@title{@bold{Plugins}: Extending DrScheme}
@author["Robert Bruce Findler"]
@(defmodule drscheme/tool-lib)
This manual describes DrScheme's tools interface. It assumes
familiarity with
PLT Scheme, as described in
@(other-manual '(lib "scribblings/guide/guide.scrbl")),
DrScheme, as described in
@(other-manual '(lib "scribblings/drscheme/drscheme.scrbl")),
and the Framework, as described in
@(other-manual '(lib "scribblings/framework/framework.scrbl")).
@table-of-contents[]
@bold{Thanks}
Thanks especially to
Eli Barzilay,
John Clements,
Matthias Felleisen,
Cormac Flanagan,
Matthew Flatt,
Max Hailperin,
Philippe Meunier,
and
Christian Queinnec,
PLT at large, and many others for
their feedback and help.
@section[#:tag "implementing-tools"]{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.
When DrScheme starts up, it looks for tools by reading
fields in the @File{info.ss} file of each collection and the
newest version of each PLaneT package installed on the
system. (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 @File{info.ss} files). DrScheme checks for these
fields:
@itemize{
@item/cap['tools]{
@scheme[(listof (listof string[subcollection-name]))]
}
@item/cap['tool-names]{@scheme[(listof (union #f string))]}
@item/cap['tool-icons]{
@schemeblock[
(listof (union #f
string[relative-pathname]
(cons string[filename]
(listof string[collection-name]))))]
}
@item/cap['tool-urls]{
@scheme[(listof (union #f string[url]))]
}}
The @scheme[tools] field names a list of tools in this
collection. Each tool is specified as a collection path,
relative to the collection where the @File{info.ss} file
resides. As an example, if there is only one tool named
@File{tool.ss}, this suffices:
@schemeblock[
(define tools (list (list "tool.ss")))
]
If the @scheme[tool-icons] or @scheme[tool-names] fields are
present, they must be the same length as @scheme[tools]. The
@scheme[tool-icons] specifies the path to an icon for each
tool and the name of each tool. If it is @scheme[#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 @scheme[lib], inside
@scheme[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.
@index{phase1}
@index{phase2}
Each of @scheme[tools] files must contain a module that
@scheme[provide]s @scheme[tool@], which must be bound to a
@scheme[unit]. The unit
must import the @scheme[drscheme:tool^] signature, which is
provided by the @FileFirst{tool.ss} library in the
@scheme[drscheme] collection. The @scheme[drscheme:tool^]
signature contains all of the names listed in this manual.
The unit must export the @scheme[drscheme:tool-exports^]
signature.
The @scheme[drscheme:tool-exports^] signature contains two
names: @scheme[phase1] and @scheme[phase2]. These names must
be bound to thunks. After all of the tools are loaded, all of
the @tt{phase1} functions are called and then all of the
@tt{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
@scheme[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 @scheme[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:
@itemize{
@item{@scheme[drscheme:language:extend-language-interface]}
@item{@scheme[drscheme:unit:add-to-program-editor-mixin]}
}
Phase 2 functions:
@itemize{
@item{@scheme[drscheme:language-configuration:add-language]}
@item{@scheme[drscheme:language:get-default-mixin]}
@item{@scheme[drscheme:language:get-language-extensions]}
}
If the tool raises an error as it is loaded, invoked, or as
the @scheme[phase1] or @scheme[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 @File{info.ss} file in a collection
contains:
@schememod[
setup/infotab
(define name "Tool Name")
(define tools (list (list "tool.ss")))
]
then the same collection would be expected to contain a
@File{tool.ss} file. It might contain something like this:
@schemeblock[
(module tool mzscheme
(require (lib "tool.ss" "drscheme")
mred
mzlib/unit)
(provide tool@)
(define tool@
(unit
(import drscheme:tool^)
(export drscheme:tool-exports^)
(define (phase1) (message-box "tool example" "phase1"))
(define (phase2) (message-box "tool example" "phase2"))
(message-box "tool example" "unit invoked"))))
]
This tool just opens a few windows to indicate that it has
been loaded and that the @scheme[phase1] and @scheme[phase2]
functions have been called.
@section[#:tag "adding-languages"]{Adding Languages to DrScheme}
@index{adding languages to DrScheme}
@subsection{Adding Module-based Languages to DrScheme}
If a language can be implemented as a module
(see @scheme[module] for details)
and the standard language settings are
sufficient, simply create an
@File{info.ss} file in the collection
where the module is saved. Include these
definitions:
@(itemize
@item/cap['drscheme-language-modules]{
@index{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 @scheme[require], using the
@tt{lib} argument (but without the @tt{lib}). The
strings represent relative paths starting at the directory
containing the @File{info.ss} file. They are interpreted
like string arguments to @scheme[require].
}
@item/cap['drscheme-language-positions]{
@index{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.
}
@item/cap['drscheme-language-numbers]{
@index{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
@bold{drscheme-language-positions}. If absent, it defaults
to a list of zeros that has the same length as
@bold{drscheme-language-positions}. This will rarely be correct.
}
@item/cap['drscheme-language-one-line-summaries]{
@index{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.
}
@item/cap['drscheme-language-urls]{
@index{drscheme-language-urls}
This is
optional. If present, it must be a list whose elements are
either strings or @scheme{#f}.
Clicking the corresponding language's name in
the interactions window opens a web browser to the url.
}
@item/cap['drscheme-language-readers]{
@index{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 @scheme[require]). Each
specification must be a module that exports a function
named @scheme[read-syntax]. Each of these
@scheme[read-syntax] functions must match MzScheme's
@scheme[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 @File{info.ss} file. It is interpreted
like the string arguments to @scheme[require].
})
The lists must have the same length.
As an example, the @italic{Essentials of Programming Languages}
language specification's @File{info.ss} looks like this:
@schememod[
setup/infotab
(require 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 @File{info.ss} file indicates that there is a single
language in this collection. The module that implements the
language is the @File{eopl-lang.ss} file in the same directory as
the @File{info.ss} file. Additionally, the language dialog will contain
@tt{Essentials of Programming Languages} as a potential
language. The use of the string constant
@scheme[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:
@schemeblock[
'("My Text" "First Language")
]
and
@schemeblock[
'("My Text" "Second Language")
]
the two languages will be grouped together in the language
dialog.
@subsection{Adding Arbitrary Languages to DrScheme}
With some additional work, any language that can be compiled
to PLT Scheme is supported by the tools interface,
not just those that use standard configurations and
@scheme[module].
Each language is a class that implement the
@scheme[drscheme:language:language<%>] interface. DrScheme also
provides two simpler interfaces:
@scheme[drscheme:language:module-based-language<%>] and
@scheme[drscheme:language:simple-module-based-language<%>],
and
@scheme[mixins]
@scheme[drscheme:language:simple-module-based-language->module-based-language-mixin]
and
@scheme[drscheme:language:module-based-language->language-mixin]
that build implementations of @scheme[language^]s from these simpler interfaces.
Once you have an implementation of the
@scheme[drscheme:language:language^] interface, call
@scheme[drscheme:language-configuration:add-language] to add the language
to DrScheme.
Each language comes with its own type, called
@tt{settings}. This can be any type the language
designer chooses, but to aid documentation, we call it
@tt{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.
@subsection{Language Extensions}
Some tools may require additional functionality from the
@scheme[drscheme:language:language] interface. The
@scheme[drscheme:language:extend-language-interface]
function and the
@scheme[drscheme:language:get-default-mixin]
mixin make this possible.
For example, the MrFlow tool expands a program, 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
know which languages are available, it can call
@scheme[drscheme:language:extend-language-interface]
to extend the @scheme[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
@scheme[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.
In some cases, it is important for one tool to avoid
depending on another in the manner above. For example, if a
tool that provides a new language provides an implementation
for the MrFlow-specific method, that tool may fail to load
if MrFlow is not present (Indeed, with the tool manager,
this can happen to any tool that depends on another in this
manner.)
To avoid this problem, consider writing your tool to first
check to see if the base method is available before
extending it. For example, if the MrFlow tool provides the
@tt{render-value<%>} interface, then a tool that overrides
that method can first test to see if the superclass
implements that method before overriding it:
@schemeblock[
(define (my-language-mixin %)
(if (implementation? % mrflow:render-value<%>)
(class %
(define/override ...)
(super-new))
%))
]
To help test your tool, use the
@seclink["environment-variables" #:doc '(lib "scribblings/drscheme/drscheme.scrbl")]{@tt{PLTONLYTOOL}}
environment variable to load it in isolation.
@section{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 @scheme[drscheme:frame:basics-mixin].
Be sure to mix it into any new frame class that you add to
DrScheme.
@section{Extending the Existing DrScheme Classes}
Each of the names:
@(itemize
@item{@scheme[drscheme:get/extend:extend-interactions-text]}
@item{@scheme[drscheme:get/extend:extend-definitions-text]}
@item{@scheme[drscheme:get/extend:extend-interactions-canvas]}
@item{@scheme[drscheme:get/extend:extend-definitions-canvas]}
@item{@scheme[drscheme:get/extend:extend-unit-frame]}
@item{@scheme[drscheme:get/extend:extend-tab]})
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:
@schemeblock[
(drscheme:get/extend:extend-interactions-text
(lambda (super%)
(class super%
(public method1)
(define (method1 x) ...)
...)))
]
extends the interactions text class with a method named @tt{method1}.
@section[#:tag "Expanding and Breaking"]{Expanding the User's Program Text and Breaking}
@index{expanding user programs}
@index{breaking}
@index{break button}
Macro-expanding a program may involve arbitrary computation
and requires the setup of the correct language. To aid this,
DrScheme's tool interface provides
@scheme[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:
@method[drscheme:rep:context<%> enable-evaluation]
and
@method[drscheme:rep:context<%> disable-evaluation].
Since your tool will be expanding the program text, you
should be both overriding
@method[drscheme:rep:context<%> enable-evaluation]
and
@method[drscheme:rep:context<%> disable-evaluation]
to disable your tool and calling them
to ensure that only one expansion is happening
at a time.
Finally, DrScheme provides the
@method[drscheme:rep:context<%> set-breakables]
method. This method controls what behavior the Break button
has.
@section{Editor Modes}
@index{modes}
@index{scheme mode}
DrScheme provides support for multiple editor modes. Tools
register modes via
@scheme[drscheme:modes:add-mode]. Each mode is
visible in the @onscreen{Modes} submenu of the @onscreen{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
@File{.txt}, DrScheme uses text mode. Otherwise, DrScheme
uses Scheme mode.
@section{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.
@scheme[drscheme:language:register-capability]. Once
registered, a tool can query a language, via the
@method[drscheme:language:language<%> capability-value]
method. The result from this method controls whether or not
the tool shows this part of the GUI for DrScheme.
See @scheme[drscheme:language:register-capability]
for a list of the capabilities registered by default.
@include-section["get-slash-extend.scrbl"]
@include-section["unit.scrbl"]
@include-section["language.scrbl"]
@include-section["language-configuration.scrbl"]
@include-section["debug.scrbl"]
@include-section["rep.scrbl"]
@include-section["frame.scrbl"]
@include-section["help-desk.scrbl"]
@include-section["eval.scrbl"]
@include-section["modes.scrbl"]
@index-section[]