The Orion 1.0 Shell page provided a basic mechanism for plugging custom commands into a shared command line environment. Shell page enhancements for Orion 2.0 have focused on formalizing new parameter types that provide access to files in the Orion workspace, and enabling commands to return result values beyond plaintext. Additionally, users can now redirect command output to workspace files. The implementation of zip and unzip commands will be used to demonstrate these new capabilities.
There are two new command parameter/return types:
- blob: A blob is just a JS blob object representing binary data (for example, an ArrayBuffer).
- file: A file represents a file or folder in the Orion workspace, and has the following shape:
{ path: (string, relative to CWD, no trailing '/'), isDirectory: (optional boolean, if absent implies false), blob: (optional blob, the file's content, absent if isDirectory is true) }
Command parameters of type file can be configured to give the desired file selection/autocompletion behaviors. The configurable attributes are booleans that default to false if absent, unless noted otherwise:
- directory: true if directories should be selectable
- file: true if files (files that are not directories) should be selectable
- multiple: true if multiple directories/files should be selectable (setting this to true enables the use of wildcards)
- recurse: true if directory descendents should be recursively selected (only relevant if directory and multiple are true)
- exist: true if a selected/typed directory or file must exist, or false if it must be non-existent. If undefined, the default behavior is to allow both existing and non-existing files to be selected/typed.
- content: true if content should be retrieved for selected existing files (only relevant if file is true)
Example: zip command
The following plug-in implementation of a zip command uses JSZip, a public JS library, to do the hard work.
<html> <head> <title>Shell 'zip/unzip' Plugin</title> <script src="/orion/plugin.js"></script> <script src="https://rawgithub.com/Stuk/jszip/v1.0.0/jszip.js"></script> <script src="https://rawgithub.com/Stuk/jszip/v1.0.0/jszip-load.js"></script> <script> window.onload = function() { var zipProperties = { name: "zip", description: "Archive files into a .zip", parameters: [{ name: "files", type: {name: "file", file: true, directory: true, exist: true, multiple: true, content: true, recurse: true}, description: "The files to add to the .zip" }], returnType: "blob" }; var zipImpl = { callback: function(args) { var zip = new JSZip(); var files = args.files; files.forEach(function(file) { var segments = file.path.split("/"); var current = zip; var dirSegmentCount = segments.length - (file.isDirectory ? 0 : 1); for (var i = 0; i < dirSegmentCount; i++) { var segment = segments[i]; if (segment !== ".") { current = current.folder(segment); } } if (!file.isDirectory) { current.file(segments[dirSegmentCount], file.blob); } }); return zip.generate({type:"blob"}); } }; var provider = new orion.PluginProvider(); provider.registerServiceProvider("orion.shell.command", zipImpl, zipProperties); provider.connect(); }; </script> </head> <body></body> </html>
Use of some new Shell page features can be seen in the zipProperties object. The “files” parameter specifies that workspace files and directories should be selected recursively, and the new returnType command attribute specifies that the returned value will be a blob (the return type defaults to string in the absence of this attribute). The details of zipImpl are not worth examining here, other than to notice that its returned value is a blob. Running command “zip ui” in the Shell page (where “ui” is the name of a directory in the cwd) gives the following output:
A destination was not specified for the resulting blob, so the default destination is the Shell page’s output area. However the Shell page will not attempt to write returned blob content to its output since it’s unlikely to be readable, hence the not-too-useful output of “(1 blobs)”. It’s up to the user to redirect a command’s output to a workspace location of their choice by providing a value for the optional output argument that is now added to all contributed commands. So running command “zip ui –output ui.zip” successfully writes the returned blob content to file ui.zip.
Example continued: unzip command
The unzip command can be added within the same plug-in file as the zip command above, so details like its <script> inclusions are omitted here.
var unzipProperties = { name: "unzip", description: "Extract the files from a .zip archive", parameters: [{ name: "file", type: {name: "file", file: true, exist: true, content: true}, description: "The .zip to extract from" }], returnType: "[file]" }; var unzipImpl = { callback: function(args) { var zip = new JSZip(args.file.blob); var files = zip.files; var newFiles = []; for (var name in files) { if (name.lastIndexOf("/") === (name.length - 1)) { /* represents a directory */ newFiles.push({path: name.substring(0, name.length - 1), isDirectory: true}); } else { var file = zip.file(name); newFiles.push({path: name, blob: file.asArrayBuffer()}); } }; return newFiles; } }; // ... provider.registerServiceProvider("orion.shell.command", unzipImpl, unzipProperties);
Since unzip’s return type is [file], the user-provided –output value will represent the directory that the resulting files are written to. Values of type file are always resolved in terms of a directory since a file has a path attribute, while values of type blob are always written to a file since a blob is just raw data.
String value processing
Another enhancement to command return values is the processing of string values for markdown-formatted links. Result strings written to the Shell page now have any contained URLs that are formatted as [link text here](link.address.here) replaced with live anchor elements. As an example, command return value “Visit [Eclipse](http://www.eclipse.org), or literally [](http://www.eclipse.org)” renders as shown below.
What’s Next?
The piece that’s missing from this updated picture is a means of chaining commands, or at least specifying a custom input source (analagous to the new –output parameter). The absence of these is the reason that the example unzip command takes a file as its input, rather than a blob, which would have made it symmetrical with the zip command. Blobs are currently only usable as command return values, not as parameters. The ability to chain commands is dependent on GCLI adding support for this functionality, but adding something like an –input parameter is an interim step that the Shell page could take for 3.0.
If there are more Shell page features that your plug-in needs then please log Enhancement Requests in Orion’s Bugzilla.
Lastly, thanks are due to Stuart Knightley for his JSZip utility that made implementing the zip/unzip example trivial.