Script Plugin for Dr  Racket
1 Introduction
2 Some demonstration videos
3 Installation
4 First simple example
5 Description
5.1 The .rkt file
5.2 The .rktd file
6 Scripts directory
7 Usage ideas and sample scripts
8 Updating the Script Plugin package
9 License
6.3.90.900

Script Plugin for DrRacket

Laurent Orseau <laurent dot orseauREMOVEME at gmail dot com>

1 Introduction

The Script Plugin’s purpose is to make it easy to extend DrRacket with small Racket scripts that can be used in the definition (or interaction) window, or to graphically interact with the user.

Creating a new script is as easy as a click on a menu item. Each script is automatically added as an item to the Scripts menu, without needing to restart DrRacket. A keyboard shortcut can be assigned to a script (via the menu item). By default, a script takes as input the currently selected text, and outputs the replacement text. There is also direct access to some elements of DrRacket for advanced (though simplified since there is no need to create a dedicated plugin) scripting, like DrRacket’s frame and the definition or interaction editor.

2 Some demonstration videos

3 Installation

To install, simply evaluate:

(require (planet orseau/script-plugin/tool))

Wait for the installation process to finish, and then restart DrRacket. You should now see a new Scripts menu.

4 First simple example

Click on the Scripts/Manage scripts/New script... menu item, and enter Reverse for the script name. 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 that just opened in DrRacket, modify the item-callback function to the following:
(define (item-callback str)
  (list->string (reverse (string->list str))))
and save the file. (Note: if you changed the properties file, you will need to reload the menu by clicking on Scripts/Manage scripts/Reload scripts menu).

Then go to a new tab, type some text, select it, and click on Scripts/Reverse, and voilà!

5 Description

The plugin adds a Scripts 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:
  • a .rkt file, the script itself (filled with a default script template),

  • a .rktd file, the metadata of the script with the default values.

These two files are automatically opened in DrRacket for edition.

The script menu is rebuilt each time the user activates it, so that changes are taken into account as soon as possible.

5.1 The .rkt file

This is the script file. It must provide the item-callback function, as in the sample code. It is meant to be executable by itself, as a normal module, to ease the testing process.

procedure

(item-callback str)  (or/c string? (is-a?/c snip%) #f)

  str : string?
Returns the string meant to be inserted in place of the current selection, or at the cursor if there is no selection. If the returned value is not a string or a snip%, the selection is not modified (i.e., the file remains in a saved state if it was already saved).

This function signature can also be extended by (optional or mandatory) special keyword arguments:
  • #:file : (or/c path? #f)

    The path to the current file of the definition window, or #f if there is no such file (i.e., unsaved editor).

    Example:
    (define (item-callback str #:file f)
      (string-append "(in " (if f (path->string f) "no-file") ": " str))

    See also: file-name-from-path, filename-extension, path->string, split-path.

  • #:definitions : text%

    The text% editor of the current definition window.

    Example: "insert-lambda.rkt"

    See text% for more details.

  • #:interactions : text%

    The text% editor of the current interaction window. Similar to #:definitions.

  • #:editor : text%

    The text% current editor, either the definition or the interaction editor. Similar to #:definitions.

  • #:frame : drracket:unit:frame<%>

    DrRacket’s frame. For advanced scripting.

    Example:
    (define (item-callback str #:frame fr)
      (send fr create-new-tab)
      #f)

The name of the function can also be changed, but this requires to change it also in the functions entry of the .rktd file (see below), and the function must be provided.

5.2 The .rktd file

This is the metadata file. It contains an association list dictionary that defines the configuration of the script.

Note: This file being a data file, it should almost never contain quotes. The quotes in the following definitions must thus not appear in the file.

Most entries (label, shortcut, shortcut-prefix, help-string) are the same as for the menu-item% constructor. In particular, a keyboard shortcut can be assigned to an item.

Additionally, if the label is 'separator, then a separator is added in the menu.

If an entry does not appear in the dictionary, it takes its default value.

There are some additional entries:
  • functions : (or/c symbol? (listof (list/c symbol? string?))) = item-callback

    If a symbol, it is the name of the function to call (which must be provided), and it must follow item-callback’s signature (with potential extensions).

    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 label option is used as the parent menu name.

    Note that a sub-menu can be shared among scripts.

    Example:

    The following .rktd file creates a sub-menu named My Functions (with the letter F for keyboard access), containing 3 items, one for each function and its associated letter-accessor.

    "my-functions.rktd"

    ((label . "My &Functions")
     (functions . ((my-function1 "My Function #&1")
                   (my-function2 "My Function #&2")
                   (my-function3 "My Function #&3")
                   ))
     (output-to . message-box)
     (active . #f)
     )

    And the associated .rkt example file:

    "my-functions.rkt"

    #lang racket/base
     
    (provide my-function1)
    (define (my-function1 str) "1")
     
    (provide my-function2)
    (define (my-function2 str) "2")
     
    (provide my-function3)
    (define (my-function3 str) "3")
     
     

    Note: The label can also be 'separator.

  • output-to : (one-of/c 'selection 'new-tab 'message-box #f) = 'selection

    If 'selection, the output of the item-callback function replaces the selection in the current tab, or insert at the cursor if there is no selection. If 'new-tab, a new tab is created and the output of the function is written to it. If 'message-box, the output is displayed in a message-box. If #f, no output is generated.

  • persistent : boolean? = #f

    If not persistent, each time a script is invoked, it is done so in a fresh namespace (so that less memory is used, at the expense of a slight time overhead). In particular, all variables are reset to their initial state.

    On the contrary, if a script is persistent, a fresh namespace is created only the first time it is invoked, and the same namespace is re-used for the subsequent invocations.

    Consider the following script:
    #lang racket/base
    (define count 0)
    (define (item-callback str)
      (set! count (+ 1 count))
      (number->string count))

    If the script is persistent, the counter increases at each invocation of the script via the menu, whereas it always displays 1 if the script is not persistent.

    Note: Persistent scripts can be "unloaded" by clicking on the Scripts/Manage scripts/Unload persistent scripts menu item. In the previous example, this will reset the counter.

  • active : boolean? = #t

    If set to #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 scripts, each one with its own .rktd file and its single dictionary.

If any change is made to a .rktd file, the Scripts menu will probably need to be reloaded: Click on Scripts/Manage scripts/Reload scripts menu.

6 Scripts directory

The default location of the user’s scripts is in a sub-folder of (find-system-path 'pref-dir). The directory of the user’s scripts can be changed through DrRacket’s preferences (in Edit/Preferences/Scripts). Important: The user’s script directory must have write-access for the user (which should be the case for the default settings).

The Import bundled script item is useful to either restore a bundled script to its initial contents if you have made changes, or import new scripts after an update of the plugin.

Note: Bundled scripts are automatically copied from the plugin directory to the user script directory on installation. To force the recopy of all bundled scripts, just delete the user script directory (itself, not only its contents) and restart DrRacket.

7 Usage ideas and sample scripts

Remark: Code snippets should probably be of rare usage, as one should better take advantage of Racket’s wonderful macro system. In some cases however, snippets might be useful, e.g., to require your common module where all your usual macros and functions are defined, or for automatic comments.

8 Updating the Script Plugin package

To update the Script Plugin once already installed, just require the newest version of the PLaneT package. The user’s scripts will not be modified in the process. There may be new bundled scripts or new versions of some bundled scripts in the new package; they won’t be (re)installed by default. To (re)install them, import them with the Import bundled script menu item. To import all bundled scripts at once, delete or rename the user script directory and (re)start DrRacket; the directory will be recreated with all bundled scripts (then move your own scripts from the renamed folder to this new one).

Some scripts are persistent (like the "def-signatures" one) and need either DrRacket to be restarted or simpler to click on the Unload persistent scripts menu item.

9 License

Copyright (c) 2012 by Laurent Orseau <laurent.orseauREMOVEME@gmail.com>.

This package is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You can find a copy of the GNU Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.html.