1 Tool support for #lang-based Languages
A variety of tools can use extra information specified by a language. These tools include DrRacket, expeditor, and more.
The simplest and best way to extend tools to support a new language is to implement the language via #lang (see Defining new #lang Languages for more details). Tools will then use read-language to find code and values that it uses to customize itself to the language.
If the call to read-language raises an error, DrRacket logs the error at the debug level to a logger with the name 'drracket-language (see Logging for more about how to follow specific loggers).
With the exception of the 'definitions-text-surrogate, if there is an error during a use of one of these extensions, DrRacket notices the error and removes all of the extensions for the language. It also shows the error at the bottom of the DrRacket frame (prefixed by #lang). Note that this applies only to errors that occur during the dynamic extent of a use of one of these extensions. If an extension were to, for example, create a new thread that (eventually) caused an error, DrRacket would not notice that error and would not remove the extensions.
When experimenting with changes to these extensions, use the Racket|Reload #lang Extensions menu item to cause DrRacket to remove the extensions and reload the implementations from the files on disk.
DrRacket calls the language’s read-language’s get-info procedure with the following key arguments. Other tools may use only a subset.
1.1 Syntax Coloring
When a language’s get-info procedure responds to 'color-lexer, it is expected to return a procedure suitable to pass as the get-token argument to start-colorer.
The recognized token styles (specified implicitly via start-colorer’s token-sym->style argument) are:
The precise colors for these identifiers are controlled by the preferences dialog in DrRacket, and by other customization mechanisms in other tools.
1.2 Indentation
When a language’s get-info procedure responds to 'drracket:indentation, it is expected to return a function with this contract:
(-> (is-a?/c color-textoid<%>) exact-nonnegative-integer? (or/c #f exact-nonnegative-integer?))
Although DrRacket might supply an object that implements racket:text<%>, if your language can limit itself to the smaller number of methods in the color-textoid<%> interface then it will work with more tools.
The function is used to indent lines. It is called with the position containing the line to be indented. It is expected to return the number of spaces that should appear at the beginning of the line or #f. If #f is returned, the tool uses the standard s-expression indentation rules.
Added in version 1.3 of package drracket.
When a language’s get-info procedure responds to 'drracket:range-indentation, it is expected to return a function with this contract:
(-> (is-a?/c color-textoid<%>) exact-nonnegative-integer? exact-nonnegative-integer? (or/c #f (listof (list/c exact-nonnegative-integer? string?))))
Although DrRacket might supply an object that implements racket:text<%>, if your language can limit itself to the smaller number of methods in the color-textoid<%> interface then it will work with more tools.
The function is used to indent a range that potentially spans multiple lines. It is called with the start and ending position of the range. The function is expected to return either #f or a list with an item for each line in the range. Returning #f falls back to iterating indentation over every line in the range (using 'drracket:indentation, if available). Returning a list indicates an update for each corresponding line, where a line update takes the form (list delete-amount insert-string): first delete delete-amount items from the start of the line, and then insert insert-string at the start of the line. If the returned list has fewer items then the range of lines to indent, the list is effectively padded with (list 0 "") no-op items. If the list has more items than the range of lines to indent, the extra items are ignored. Note that returning an empty list causes no lines to be updated, as opposed to returning #f to trigger a different indentation mechanism.
When both 'drracket:indentation and
'drracket:range-indentation are available, the function for
'drracket:range-indentation is called first—
Added in version 1.10 of package drracket.
These are used with the framework library’s color:text<%> object; they are supplied as the pairs argument to the start-colorer method. The default value is
'((|(| |)|) (|[| |]|) (|{| |}|))
They are also used to introduce keybindings that match the parentheses, via racket:map-pairs-keybinding-functions.
Added in version 1.12 of package drracket.
These characters are used to introduce keybindings via racket:map-pairs-keybinding-functions, where the open and close arguments are both the character.
The default value is (list #\" #\|).
Added in version 1.13 of package drracket.
1.3 Comments
When a language’s get-info procedure responds to 'drracket:comment-delimiters, it is expected to return a value with this contract:
(listof (or/c (list/c 'line (and/c string? (not/c #rx"[\r\n]")) (and/c string? (not/c #rx"[\r\n]"))) (list/c 'region (and/c string? (not/c #rx"[\r\n]")) (and/c string? (not/c #rx"[\r\n]")) (and/c string? (not/c #rx"[\r\n]")) (and/c string? (not/c #rx"[\r\n]")))))
The value is a list of comment styles. Each comment style is expressed as one of:
(list 'line start padding), where start plus padding starts a comment that is teriminated by the end of a line.
Lisp example: '(line ";;" " ").
C++ example: '(line "//" " ").
(list 'region start continue end padding), where:
start then padding opens a comment
continue then padding is added to the beginning of each line except the first one when a comment spans multiple lines
padding then end closes a comment
Racket example: '(region "#|" " " "|#" " ").
C++ example: '(region "/*" " *" "*/" " ").
When not specified by a lang, the default value is suitable for Racket s-expression langs:
'((line ";;" " ") (region "#|" " " "|#" " "))
An intended use for these values is by (un)comment commands, which
vary among tools. Some tools (un)comment entire lines, whereas others
may handle portions of a line. Generally this is orthogonal to using a
lang’s line vs. region style: A tool can wrap entire lines using
region comments. A tool can insert line breaks to make it possible to
use line comments on a portion of a line. The point of
'drracket:comment-delimiters is to enable a lang to tell a
tool about its comment delimiters —
When the list has multiple styles: Some tools may present the styles for the user to pick one. Other tools may default to using the first style in the list (allowing the user to configure another preference by other means). Therefore when a language supports multiple comment styles, it should list the most popular or preferred style first.
Added in version 1.15 of package drracket.
1.4 Keystrokes
When a language’s get-info procedure responds to 'drracket:keystrokes, it is expected to return a list of keybindings and callbacks matching this contract:
(listof (list/c string? (-> (is-a?/c text%) (is-a?/c event%) any)))
Each element of the list is a different keybinding, where the string indicates the keystroke (see the documentation for map-function for the precise contents of the string and how it maps to particular keystrokes) and the procedure is called when the user types that keystroke in the definitions window.
The procedure’s first argument will be the definitions text, the second will be the event object supplied from the GUI system and the result of the procedure is ignored.
When a language’s get-info procedure responds to 'drracket:grouping-position, it is expected to return a function that determines where positions relevant to the nesting structure of the program appear. This function is used for a number of motion and selection operations in the editor, as well as determining where to flash to for closing parentheses.
Specifically the result must be a function matching this contract:
(-> (is-a?/c color-textoid<%>) natural? natural? (or/c 'up 'down 'backward 'forward) (or/c #f #t natural?))
Although DrRacket might supply an object that implements racket:text<%>, if your language can limit itself to the smaller number of methods in the color-textoid<%> interface then it will work with more tools.
Consider first the first and third argument. The first argument indicates a position in the editor to start from and the third argument is a direction to look. The result return the position for the corresponding direction, where the nesting structure of the program is viewed as a tree. That is, if the third argument is 'up, the function should return the position that goes up one layer in the tree from the given position to the parent. Similarly 'down should return the position going on layer deeper into that tree, going down to the first child. The 'backward and 'forward arguments correspond to position where we stay at the same level in the tree, moving between siblings. The result should be #f when there is no corresponding position to move to, e.g., when the current position has no children, no parents, or no siblings in the corresponding direction.
The second argument is a limit. Positions smaller than the limit should be ignored, so if the corresponding position appears to be before the limit, return #f.
Finally, return #t to get the default behavior, namely motion in Racket-style s-expressions.
Added in version 1.11 of package drracket.
1.5 Filename Extensions
When a language’s get-info procedure responds to 'drracket:default-filters, it is expected to return (listof (list/c string? string?)).
These results are added as a prefix to finder:default-filters, extending the default that DrRacket normally uses, namely:
`(["Racket Sources" "*.rkt;*.scrbl;*.rktl;*.rktd;*.ss;*.scm"] ["Any" "*.*"])
Added in version 1.2 of package drracket.
When a language’s get-info procedure responds to 'drracket:default-extension, it is expected to return (and/c string? (not/c #rx"[.]")); the result is used as the default extension when saving files by setting finder:default-extension.
Added in version 1.2 of package drracket.
1.6 REPL Submit Predicate
When using the language declared in the source, DrRacket queries that language via read-language to determine if an expression in the interactions window is ready to be submitted to the evaluator (when the user types return). The info procedure is passed 'drracket:submit-predicate and should return a function matching this contract:
(-> input-port? boolean? boolean?)
This function’s first argument is a port that contains the interactions window’s data, starting just after the prompt and continuing to the end of the editor. The second argument is a boolean indicating if the insertion point is followed only by whitespace. The results should be a boolean indicating if the expression should be evaluated.
For backwards compatibility reasons, DrRacket also queries the result of module->language-info for 'drracket:submit-predicate. It does this during the evaluation of the definitions (so the Racket|Reload #lang extensions menu item does not trigger a re-load). If the submit predicate is specified both ways, then the predicate supplied via read-language takes precedence.
Changed in version 1.5 of package drracket: Look for drracket:submit-predicate via read-language.
1.7 Show Big “Definitions” and “Interactions” Labels
If the read-language predicate returns #t for 'drracket:show-big-defs/ints-labels, then DrRacket shows the words “Definitions” and “Interactions” in a large font in the corresponding windows. This is intended as a help for students who are reading instructions about where to type their programs but might not have internalized this particular bit of DrRacket terminology.
1.8 Opting Out of Standard Toolbar Buttons
Some of the built-in buttons in the DrRacket button bar at the top of the window can be disabled on a per-language basis. DrRacket will invoke the get-info proc returned by read-language with 'drracket:opt-out-toolbar-buttons (and 'drscheme:opt-out-toolbar-buttons for backwards compatibility).
If the result is a list of symbols, the listed symbols are opted out. If the result is #f, all buttons are opted out. The default is the empty list, meaning that all opt-out buttons appear.
The Check Syntax button uses the symbol 'drracket:syncheck; the debugger uses the symbol 'debug-tool and the macro stepper uses 'macro-stepper.
Plugins may add more opt-out buttons via drracket:module-language-tools:add-opt-out-toolbar-button.
1.9 Opting In to Language-Specific Toolbar Buttons
Like drracket:opt-out-toolbar-buttons, but for languages to opt in to buttons that are not enabled by default.
Plugins may add more opt-out buttons via drracket:module-language-tools:add-opt-in-toolbar-button.
Added in version 1.6 of package drracket.
1.10 Adding New Toolbar Buttons
DrRacket queries the result of read-language to determine if there are any new toolbar buttons to be used when editing files in this language.
Specifically, DrRacket will pass 'drracket:toolbar-buttons to the function and expect back a value matching this contract:
(or/c (listof (list/c string? (is-a?/c bitmap%) (-> (is-a?/c drracket:unit:frame<%>) any) (or/c real? #f))) #f)
which is then used to create new toolbar buttons, one for each element in the result list. The string is the label on the button; the bitmap is the icon (it should be 16x16 pixels); the function is called when the button is clicked; and the number is passed as the #:number argument to register-toolbar-button.
If the result is #f, then no toolbar buttons are created.
To implement functionality similar to the Run button, call the execute-callback method. You may also want to use the drracket:rep:after-expression parameter.
If 'drracket:toolbar-buttons is not recognized, DrRacket will also pass 'drscheme:toolbar-buttons; this is for backwards compatibility and new code should not use it. Similarly, if the fourth element from the list (the argument to #:number) is not present, then it is treated as #f.
1.11 Definition Popup-Menu Navigation
A popup menu in the DrRacket button bar jumps to definitions based on a heuristic search of the program text. DrRacket will invoke the get-info proc returned by read-language with 'drracket:define-popup to obtain a configuration for the menu.
The value must satisfy the contract
(non-empty-listof (or/c (list/c string? string? string?) (list/c string? string? string? (or/c #f (-> (is-a/c text%) string? exact-integer? (->* ((is-a/c text%) string? exact-integer?) (#:case-sensitive? any/c #:delimited? any/c) (or/c exact-integer? #f)) (or/c exact-integer? #f))) (or/c #f (-> (is-a/c text%) exact-integer? (-> (is-a/c text%) exact-integer? string?) string?)))))
where the first string in each nested list is literal text to search for (outside of comments and literal strings), the second string is a label to describe the category of matches, and the third string is a short form of the label. The labels from the first nested list are used for the definition-popup button itself, while all labels are used for the user to select which categories are enabled.
When a nested list contains fourth and fifth elements, they can supply replacements (when not #f) for the default functions that find a prefix and extract the subsequent name:
The prefix-finding function receives a text-editor object for the content to search, the prefix string to find, a position to start the search, and a default prefix-finding function. The result is a position in the text editor for the start of a found prefix, or #f if the prefix is not found.
The provided default finding function accepts two optional keyword arguments: a true value for #:case-sensitive? requires case-insensitive matching, and a true value for #:delimited? indicates that the matched text’s edges must coincide with forward and backward expression navigation.
The name-extracting function receives a text-editor object for the content to extract, a position after a found prefix string, and a default name-extracting function. The result must be a string for the extracted defined name.
Plugins can provide a default popup-menu configuration via drracket:language:register-capability using 'drscheme:define-popup.
Added in version 1.14 of package drracket.
1.12 Definitions Text Surrogate
Using a #lang-specific definitions text surrogate is a very powerful way to flexibly control DrRacket’s behavior when a new language is installed. It is also easy to cause DrRacket to completely misbehave with this form of extension. It is here only when one of the other forms of extension listed above are not sufficient for the kind of extension your language requires. And even in that case, it is preferable to add something to this list that is more easily controlled in the case of errors, using the definitions text surrogate only until that more easily controlled extension has been added to DrRacket.
DrRacket calls read-language’s get-info procedure with 'definitions-text-surrogate and expects it to return a value matching the contract (or/c #f module-path?), which is then passed to dynamic-require together with 'surrogate%. The result is expected to be a class implementing the interface racket:text-mode<%> (presumably derived from racket:text-mode%. That mode is installed into the definitions text, where it can change its behavior by changing how is responds to any of the methods in the mode.
One consequence of this power is that errors that happen during the dynamic extent of calls into the mode are not trapped (much as errors that occur on newly created threads are not trapped, as described in the introduction to this section).