#lang scribble/manual
@(require planet/scribble)
@title{Script Plugin for DrRacket}
@section{Introduction}
The Script Plugin is a plugin for DrRacket that is similar to GEdit's External Tools plugin.
Its purpose is to make it easy to extend DrRacket with small scripts that can be used in the definition window,
on the selected text.
@section{Example}
Click on Scripts/New Script, and enter @italic{Reverse}.
This creates and opens the files reverse.rkt and reverse.rktd in the script directory.
Also, a new item automatically appears in the Scripts menu.
In the .rkt file, modify the @racket[transform-input-string] function to the following:
@codeblock|{
(define (transform-input-string str)
(list->string (reverse (string->list str))))
}|
Then go to a new tab, type some text, select it, and click on Scripts/Reverse.
@section{Description}
This DrRacket plugin adds a Script menu to the main window.
This menu has several items, followed by the (initially empty) list of active
scripts.
The New Script item asks for a script name and creates 2 files:
@itemlist[
@item{a .rkt file, the script itself (with a sample program)}
@item{a .rktd file, the metadata of the script with the default values}
]
These two files are automatically opened.
The script menu is rebuilt each time the user activates it, so that changes
are taken into account as soon as possible.
The default location of the scripts is in a sub-folder of
@racket[(find-system-path 'home-path)].
The directory of the user scripts can be change through DrRacket's preferences
(in Edit/Preferences/Scripts).
@bold{Important:} The scripts directory must have write-access for the user.
@subsection{The .rkt file}
This is the script file.
It must provide the @racket[transform-input-string] (that's not a very good name)
function, as in the sample code.
It is meant to be executable by itself, to ease the testing process.
@defproc[(transform-input-string [str string?]) string?]{}
This function also accepts (optional or mandatory) special keyword arguments
(the signature is automatically checked with @racket[procedure-keywords]):
@itemlist[
@item{@racket[#:file-string]
If present, the full filename of the current file in the definition window
is given to the function when called, or @racket[#f] if there is no such file.}
@item{@racket[#:file-path]
The path to the current file of the definition window.}
@item{@racket[#:editor]
The @racket[text%] editor of the current definition window.
For advanced scripting.}
@item{More keywords are to come.}
]
For example:
@codeblock|{
(define (transform-input-string str #:filename f)
(string-append "(in " (or f "no-file") ": " str))
}|
The name of the function can also be changed,
but this requires to change it also in the @racket[functions]
entry of the .rktd file, and the function must be @racket[provide]d.
@subsection{The .rktd file}
This is the metadata file.
It contains an association list that defines the configuration of the script.
Most options (@racket[label], @racket[shortcut], @racket[shortcut-prefix], @racket[help-string]) are the same as
for the @racket[menu-item%] constructor.
In particular, a keyboard shortcut can be assigned to an item.
There are some additional options:
@itemlist[
@item{@racket[functions : (or symbol? (list-of (list symbol? string?))) = transform-input-string]
If a symbol, the name of the function to call (which must be provided),
and must follow @racket[transform-input-string]'s signature.
If a list, each symbol is the name of a function, and each string is a label for that function.
In this case, a sub-menu holding all these functions is created, and the @racket[label] option is used as the parent menu name.
Note that a sub-menu can be shared among scripts.}
@item{@racket[output-to : (one-of selection new-tab message-box) = selection]
If @racket[selection], the output of the transform function replaces the
selection in the current tab (or insert at the cursor if there is no
selection).
If @racket[new-tab], a new tab is created and the output of the script is written to it.
If @racket[message-box], the output is displayed in a message-box.
}
@item{@racket[active : boolean? = #t]
If set to @racket[#f], no menu item is generated for this dictionary.}
]
Finally, one .rktd file can contain several such dictionaries (one after the other),
which allows for multiple sub-menus and menu items and in a single script.
This would have roughly the same effect as splitting such a script into several script,
each one with its own .rktd file and its dictionary.
@section{Usage ideas}
@itemlist[@item{Snippets to add a @racket[require] line for each planet package you usually use.
Modify the .rktd file to put them all in a "PLaneT" subsection.}
@item{ASCII frames and styling (upper-case, camel-case, etc.) for titles, sections, etc.}
@item{Code snippets scripts, with keyboard shortcuts.}
@item{Automatic comments, or even contracts from signatures and conventions.}
@item{Automatic reformatting.}
@item{Count the number of words/lines/characters/whatever and display it in a message-box.}
@item{Open a new tab with a template code.}
@item{...}
]
Note that @racket[racket/gui] can be used to ask the user for more information and more.