The Joyride API consists of:
- The Joyride Extension API
exports- Extension commands
whenclause context
- The Joyride Scripting API
- Scripting lifecycle management
- Included clojure library namespaces
Give the joyride_api.cljs example a spin, why don't ya!
Please note that Joyride's Extension API is also available to Joyride scripts.
Joyride's Extension API has these parts:
- The
exportsmap/object on the activated extension instance. - The Joyride extension commands.
whenclauses contexts
When the Joyride extension has activated it has the following API:
joyride.runCode- Evaluates the the string provided and returns a promise with the result.
startNReplServer [project-root-path]- Returns a promise resolving to the port where the nREPL server started
project-root-pathis optional, defaulting tovscode/workspace.rootPath
getContextValue context-key- Returns the value of a Joyride
whenclause context - Returns undefined for non-existing
context-keys
- Returns the value of a Joyride
You reach the API through the exports field on the Joyride extension:
const joyrideExtension = vscode.extensions.getExtension("betterthantomorrow.joyride");
const joyride = joyrideExtension.exports;Note that runCode will return the ClojureScript results. And if an error occurs it will be a ClojureScript error. This means that if you are consuming the API from JavaScript/TypeScript you will need to convert the results, as well as any error. You can use Joyride's ClojureScript (SCI) interpreter for this:
const toJS = await joyride.runCode("clj->js");
const exData = await joyride.runCode("ex-data");
const r = joyride.runCode("{:a (some-fn)}")
.catch(e => vscode.window.showErrorMessage(JSON.stringify(toJS(exData(e)))));
if (r) {
const js_r = await toJS(r);
vscode.window.showInformationMessage(js_r);
}Select Joyride from the VS Code Extension pane to see which commands it provides. The commands you'll probably use the most are:
joyride.runCodejoyride.evaluatSelectionjoyride.runUserScriptjoyride.runWorkspaceScript
The same note about ClojureScript applies for the joyride.runCode command as for the corresponding API export, mentioned above. Fetching the clj->js function looks more like so in this case:
const toJS = await vscode.commands.executeCommand('joyride.runCode', "clj->js");The following contexts are available for users of Joyride when binding commands to keyboard shortcuts:
joyride.isActive,boolean- Whether the joyRide extension is active or notjoyride.isNReplServerRunning,boolean- Whether the Joyride nREPL server is running or not
If your script needs access to these contexts, use the getContextValue function from the Joyride exports API.
Joyride has a fixed ”classpath” (the paths where it looks for roots to your Joyride code source files). It searches these directories in this order:
<workspace-root>/.joyride/src<workspace-root>/.joyride/scripts<user-home>/.config/joyride/src<user-home>/.config/joyride/scripts
The first file found will be used.
You can make some code run when Joyride activates. The activation scripts will be run in the order:
<User scripts directory>/user_activate.cljs<Workspace scripts directory>/workspace_activate.cljs
Look at the Activation example script for a way to use this, and for a way to make the script re-runnable.
To use npm modules these need to be installed in the path somewhere in the path from where the script using it resides, to the root of the filesystem. Consider using <user-home>/.config/joyride and <ws-root>/.joyride. (yarn or npm i both work, Joyride doesn't care, it looks for stuff in node_modules).
See examples/.joyride/scripts/html_to_hiccup.cljs for an example.
The modules you use need to be in CommonJS format.
You can reload an updated npm module by adding :reload to the require form requiring it.
You can require JavaScript files (CommonJS) from your scripts by using absolute or relative paths to the JS files. Like in actual JavaScript, relative paths need to start with the current or parent directory to separate them from node module requires. If you provide the :reload option to the require form, the code in these files are reloaded when you re-require them from Clojure code. See Examples Requiring JavaScript Files for an example.
Joyride exposes its vscode module for scripts to consume. You require it like so:
(ns joyride-api
(:require ...
["vscode" :as vscode]
...))VS Code Extensions that export an API can be required using the ext:// prefix followed by the extension identifier. For instance, to require Calva's Extension API use "ext://betterthantomorrow.calva". Optionally you can specify any submodules in the exported API by suffixing the namespace with a $ followed by the dotted path of the submodule. You can also refer objects in the API and submodules. Like so:
(ns calva-api
(:require ...
["ext://betterthantomorrow.calva$v0" :as calva :refer [repl ranges]])
...)
(def current-form-text (second (ranges.currentForm)))This does not work in Joyride SCI:
(deftype Foo []
Object
(bar [x y] ...))
Do this instead:
#js {:bar (fn [x y] ...)}
The latter should not be instantiated. Just be used wherever an instance is expected.
See examples/.joyride/src/problem_hover.cljs for an example. (Used from the user_activate.cljs template)
In addition to clojure.core, clojure.set, clojure.edn, clojure.string,
clojure.walk, clojure.data, cljs.test, and clojure.zip, Joyride makes the following libraries available:
- Promesa (partly, see below
- rewrite-clj
Lacking some particular library? Please consider contributing to babashka/sci.configs!
In addition to these there is also joyride.core:
*file*: dynamic var holding the absolute path of file where the current evaluation is taking placeinvoked-script: function returning the absolute path of the invoked script when running as a script. Otherwise returnsnil. Together with*file*this can be used to create a guard that avoids running certain code when you load a file in the REPL:(when (= (joyride/invoked-script) joyride/*file*) (main))
extension-context: function returning the Joyride ExtensionContext instanceoutput-terminal: function returning the Joyride Output terminal instancejs-properties: a function returning a sequence of the full JS API of the provided JS object/instance. For use instead ofcljs.core/js-keyswhen it doesn't return the full API.user-joyride-dir: a string path with the user/global joyride directory.slurp: similar to Clojureslurp, but is asynchronous and returns a promise. Resolves relative paths using the workspace root.load-file: similar to Clojureload-file, but is asynchronous and returns a promise. Resolves relative paths using the workspace root.
Here's a snippet from the joyride_api.cljs example.
(ns your-awesome-script
(:require [joyride.core :as joyride]
...))
(.show (joyride/output-terminal))
(print "Writing to the ")
(println "Joyride output channel.")
(println "Joyride extension path: " (-> (joyride/extension-context)
.-extension
.-extensionPath))
(println "joyride/*file*: " joyride/*file*)
(println "Invoked script: " (joyride/invoked-script))
(println "🎉")NB: Currently, using bare *file* works. But it will probably stop working soon. Always use it from joyride.core.
WebView panel and sidebar view creation convenience.
close!: Close a flare by key. Returnstrueif a flare was closed,falseif the key was not found.post-message!+: Post a message to a flare by key. Returns thepostMessagepromise.close-all!: Close all active flares. Returns the count of flares that were closed.ls: List all active flares as a mapkey → flare.get-flare: Get a flare by its key, returning only the view and optional message handler when active.flare!+: Create or update a flare. The argument is an options map with::html- HTML content string OR Hiccup data structure:url- URL to display in iframe:file- Path to an HTML or EDN file containing Hiccup. Can be:- Absolute path string
- Relative path string, resolves to a path in the workspace root (the first folder, if it's a multi-folder workspace)
:title- Panel/view title (default: 'Flare'):key- Identifier for reusing panels- Special keys are
:sidebar-1→:sidebar-5, displays the flare in sidebar vs separate panel
- Special keys are
:icon- Icon for panel tab. String (path/URL) or map{:light "..." :dark "..."}:column- vscode ViewColumn (default:js/undefined):reveal?- Whether to reveal the panel (default:true).falseworks differently between regular editor area flares and sidebar flares:- editor area: Will reveal newly created flares, will respect the
falsesetting when updating flares - sidebar: Will always respect
false
- editor area: Will reveal newly created flares, will respect the
:preserve-focus?- Whether to preserve focus when revealing the panel (default:true):message-handler- Function to handle messages from webview. Receives message object.:webview-options- A map with vscode WebviewPanelOptions & WebviewOptions for the webview (default:{:enableScripts true, :localResourceRoots [workspace-folders, extension-dir, joyride-user-dir]})
Path Templates in HTML/Hiccup Content:
In HTML/Hiccup content, file paths in attributes (like src, href) support template placeholders for portable resource references:
{joyride/user-dir}→ Expands to your Joyride user directory (~/.config/joyride){joyride/extension-dir}→ Expands to the Joyride extension installation directory{joyride/workspace-dir}→ Expands to the current workspace root directory (but consider using relative paths instead for this)
Example:
(flare/flare!+
{:html [:div
[:img {:src "{joyride/user-dir}/shared/logo.png"}]
[:script {:src "{joyride/extension-dir}/vendor/lib.js"}]]
:key :my-flare})Relative paths without templates resolve relative to the workspace root.
Icon Options:
- String - Path or URL to icon file
- Map -
{:light "path-or-url" :dark "path-or-url"}for themed icons
See also the examples README.
See promesa docs for how to use it.
NB: All of Promesa is not available in Joyride. Exactly how much is supported depends on which version of
sci-configs Joyride is built with. At the time of this writing, we were using commit 3cd48a595bace287554b1735bb378bad1d22b931.
To check what you can use from Promesa you can check sci-config for the given commit, like so:
You'll find the commit id to use for latest Joyride in deps.edn of this repo.