Quickscript, a scripting plugin for Dr  Racket
1 Introduction
2 Installation
3 Make your own script:   First simple example
4 Into more details
4.1 The script’s procedure
4.2 The script’s properties
5 Script library
6 Shadow scripts
7 Updating the quickscript package
8 License
6.12

Quickscript, a scripting plugin for DrRacket

Laurent Orseau <laurent dot orseauREMOVEME at gmail dot com>

1 Introduction

Quickscript’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 GUI for advanced scripting, like DrRacket’s frame and the definition or interaction editor.

2 Installation

To install, either look for quickscript in the DrRacket menu File>Package Manager, or run the raco command:
  raco pkg install quickscript

You need to restart DrRacket. Now you should have a new item Scripts in the menu bar.

3 Make your own script: 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 file reverse.rkt in the user’s scripts directory. Also, a new item automatically appears in the Scripts menu.

In the .rkt file that just opened in DrRacket, modify the define-script definition to the following:
(define-script reverse
  #:label "Reverse"
  (λ(selection)
    (list->string (reverse (string->list selection)))))
and save the file. (Note: if you later change the label property, you will need to reload the menu by clicking on Scripts>Manage scripts>Reload scripts menu after saving the file).

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

4 Into more details

Quickscript adds a Scripts menu to the main DrRacket window. This menu has several items, followed by the list of scripts.

The New script item asks for a script name and creates a corresponding .rkt file in the user’s script directory, and opens it in DrRacket.

Each scripts is defined with define-script, which among other things adds an entry in DrRacket’s Scripts menu. A single script file can contain several calls to define-script.

By default, the new script is reduced to its simplest form. However, scripts can be extended with several optional properties and arguments. When all of them are used, a script can look like this:
(define-script a-complete-script
  ; Properties:
  #:label "Full script"
  #:help-string "A complete script showing all properties and arguments"
  #:menu-path ("Submenu" "Subsubmenu")
  #:shortcut #\a
  #:shortcut-prefix (ctl shift)
  #:output-to selection
  #:persistent
  #:os-types (unix macos windows)
  ; Procedure with its arguments:
  (λ(selection #:editor ed #:frame fr #:interactions ints #:file f)
    "Hello world!"))

Below we detail first the procedure and its arguments and then the script’s properties.

4.1 The script’s procedure

When clicking on a script label in the Scripts menu in DrRacket, its corresponding procedure is called. The procedure takes at least the selection argument, which is the string that is currently selected in the current editor. The procedure must returns either #f or a string?. If it returns #f, no change is applied to the current editor, but if it returns a string, then the current selection is replace with the return value.

If some of the above keywords are specified in the procedure, the Script Plugin detects them and passes the corresponding values, so the procedure can take various forms:
(λ(selection)....)
(λ(selection #:frame fr)....)
(λ(selection #:file f)....)
(λ(selection #:editor ed #:file f)....)
....

Here is the meaning of the 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-script current-file-example
      #:label "Current file example"
      #:output-to message-box
      (λ(selection #:file f)
        (string-append "File: " (if f (path->string f) "no-file")
                       "\nSelection: " selection)))

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

  • #:definitions : text%

    The text% editor of the current definition window. 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:
    (require racket/class)
    (define-script number-tabs
      #:label "Number of tabs"
      #:output-to message-box
      (λ(selection #:frame fr)
        (format "Number of tabs in DrRacket: ~a"
                (send fr get-tab-count))))

4.2 The script’s properties

The properties are mere data and cannot contain expressions.

Most properties (#: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.

If a property does not appear in the dictionary, it takes its default value.

There are some additional properties:
  • #:menu-path : (listof string?) = () This is the list of submenus in which the script’s label will be placed, under the Script menu.

    Note that different scripts in different files can share the same submenus.

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

    If selection, the output of the procedure replaces the selection in the current editor (definitions or interactions), or insert the output at the cursor if there is no selection. If new-tab, the return value is written in a new tab. If message-box, the return value (if a string) is displayed in a message-box. If clipboard, the return value (if a string) is copied to the clipboard. If #f, the return value is not used.

  • #:persistent

    If they keyword #:persistent is not provided, each invocation of the script is done in a fresh namespace that is discarded when the procedure finishes.

    But if #:persistent is provided, a fresh namespace is created only the first time it is invoked, and the same namespace is re-used for the subsequent invocations. Note that a single namespace is kept per file, so if different scripts in the same file are marked as persistent, they will all share the same namespace (and, thus, variables). Also note that a script marked as non-persistent will not share the same namespace as the other scripts of the same file marked as persistent.

    Consider the following script:
    (define count 0)
     
    (define-script persistent-counter
      #:label "Persistent counter"
      #:persistent
      #:output-to message-box
      (λ(selection)
        (set! count (+ count 1))
        (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.

  • #:os-types (listof (one-of/c unix macos windows))

    This keyword must be followed by a list of supported os-types. Defaults to all types, i.e. (unix macos windows).

If changes are made to these properties, the Scripts menu will probably need to be reloaded by clicking on Scripts>Manage scripts>Reload scripts menu.

5 Script library

When the user creates a new script, the latter is placed into a sub-directory of (find-system-path 'pref-dir). A direct access to this folder is provided via the Scripts>Manage scripts>Open script... menu entry.

Additional directories to look for scripts can be added via the Scripts>Manage scripts>Library menu entry. When a directory is added to the library, all its .rkt files (non-recursively) are considered as scripts. Specific files can be excluded from the library.

6 Shadow scripts

todo...

7 Updating the quickscript package

To update Quickscript once already installed, either do so through the File>Package Manager menu in DrRacket, or run raco pkg update quickscript.

The user’s scripts will not be modified in the process.

8 License

MIT License

Copyright (c) 2012 by Laurent Orseau <[email protected]>.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.