Difference between revisions of "Orion/Documentation/Developer Guide/Plugging into the shell"

From Eclipsepedia

Jump to: navigation, search
m (fix typo)
(fix spec for 'file' return object)
 
(28 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
= Overview of contributing services to the Shell =
 
= Overview of contributing services to the Shell =
  
The <b>Shell</b> page provides a view through which users can interact with Orion via a command-line interface.  The page provides some basic commands for navigating Orion's workspace, and defines a service that allows plug-ins to contribute additional commands.
+
The <b>Shell</b> page provides a view through which users can interact with Orion via a command-line interface.  The page provides some basic commands for navigating Orion's workspace, and defines services that allow plug-ins to contribute additional commands and command parameter types.
  
 
= orion.shell.command =
 
= orion.shell.command =
  
The <tt>orion.shell.command</tt> service is used to contribute commands to the Shell page. When the service is executed, an object containing the user-supplied argument values is passed to the service's <tt>callback</tt> method. The command then optionally returns a response (a return value) to be displayed in the Shell.
+
The <tt>orion.shell.command</tt> service is used to contribute commands to the Shell page. When the service is executed, an object containing the user-supplied argument values is passed to the service's <tt>callback</tt> method. The command then optionally returns a response (a return value) to be displayed in the Shell page.
  
 
== Service methods ==
 
== Service methods ==
  
Implementations of <tt>orion.shell.command</tt> may define the following function:
+
Implementations of <tt>orion.shell.command</tt> may define the following method:
  
;callback(args)
+
; callback(args, context)
: Takes the user-supplied command-line arguments (if any) as an argument, and optionally returns a response string in plain text (no HTML is allowed).
+
: Takes the user-supplied command-line arguments and optionally returns a response value. The arguments are as follows:
 +
: '''args'''
 +
:: An object with the user-supplied command-line arguments (if any)
 +
: '''context'''
 +
:: An object with property {cwd: string}, indicating the user's current working directory in the Shell page
  
The only context where a contributed command would not define a service method is to assist with the contribution of sub-commands.  For example, to contribute commands "tar create" and "tar extract", a parent command "tar" without a service method must first be contributed.
+
The basic return value types of '''callback()''' are:
 +
:<b>string</b> (note that occurrences of <tt>[link text](link address)</tt> are converted to links when output)
 +
:<b>file object</b>: <tt>{path: string (relative to CWD), isDirectory: boolean (default: false), blob: aBlob (optional)}</tt>
 +
:<b>blob object</b>: <tt>{blob: aBlob}</tt>
 +
 
 +
Implementations of '''callback()''' can return either a value with one of these basic types, or an <tt>orion.Deferred</tt>. Returning an <tt>orion.Deferred</tt> facilitates the asynchronous return of a result value (by invoking <tt>Deferred.resolve()</tt>), the incremental display of progress (by invoking <tt>Deferred.progress(string)</tt>), and the interim use of a delegated UI that is shown in a frame provided by the Shell page.
 +
 
 +
To make use of a delegated UI, invoke <tt>Deferred.progress({uriTemplate: url, width: string, height: string})</tt>, where '''url''' at the content to show in the provided frame ([[Orion/Documentation/Developer Guide/Plugging into the shell#Example of Using a Delegated UI|example]]). This content is subsequently responsible for sending the command's result value to the Shell page by posting the following message:
 +
:<code>window.parent.postMessage(JSON.stringify({pageService: "orion.page.delegatedUI", source: theCommandName, result: theResultValue}), "*");</code>
 +
 
 +
The only context where a contributed command would not define this service method is to assist with the contribution of sub-commands.  For example, to contribute commands "tar create" and "tar extract", a parent command "tar" without a service method must first be contributed.
  
 
== Service attributes ==
 
== Service attributes ==
Line 29: Line 43:
 
:(Optional) An array of the parameters that the command accepts
 
:(Optional) An array of the parameters that the command accepts
  
The Shell page currently uses [https://wiki.mozilla.org/DevTools/Features/GCLI GCLI] as its underlying shell widget, and consequently has adopted its syntax for parameter specification.  For details on this syntax begin reading at the "## Default argument values" header in the [https://raw.github.com/mozilla/gcli/master/docs/writing-commands.md GCLI Writing Commands doc].  The basic parameter object attributes are:
+
The Shell page currently uses [https://wiki.mozilla.org/DevTools/Features/GCLI GCLI] as its underlying shell widget, and consequently has adopted its syntax for parameter specification.  The basic parameter object attributes are:
  
::;name
+
: '''name'''
:::The parameter's identifier
+
:: The parameter's identifier
::;type
+
: '''type'''
:::One of { string | boolean | number | array | selection | a custom orion.shell.type}
+
:: One of { string | boolean | number | array | selection | file | blob | a custom orion.shell.type}
::;description
+
: '''description'''
:::(Recommended) A brief description of the parameter
+
:: (Recommended) A brief description of the parameter
::;defaultValue
+
: '''defaultValue'''
:::(Optional) The value assumed by the parameter if the user does not supply a value for it, makes the parameter optional
+
:: (Optional) The value assumed by the parameter if the user does not supply a value for it.  Specifying this makes the parameter optional.
 +
 
 +
== Example of Using a Delegated UI ==
 +
 
 +
The following "login" command implementation delegates to accompanying file "authenticationPrompter.html":
 +
 
 +
<source lang="javascript" enclose="div" >
 +
callback: function(args, context) {
 +
var result = new orion.Deferred();
 +
var url = window.location.href;
 +
url = url.substring(0, url.lastIndexOf('/')); //$NON-NLS-0$
 +
setTimeout(function() {
 +
  result.progress({uriTemplate: url + "/authenticationPrompter.html", width: "400px", height: "100px"});
 +
});
 +
return result;
 +
}
 +
</source>
 +
 
 +
authenticationPrompter.html shows simple username/password fields in the frame that has been provided to it by the Shell page.  When the page's Submit button is pressed it performs the authentication and posts the result value.  The frame showing this page is subsequently removed by the Shell page.
 +
 
 +
<source lang="javascript" enclose="div" >
 +
<head>
 +
<script>
 +
  function authenticate() {
 +
    /* do authentication here, assume is fine, so post result back to Shell page */
 +
    var result = "Authenticated user: " + document.getElementById("username").value;
 +
    window.parent.postMessage(JSON.stringify({pageService: "orion.page.delegatedUI", source: "login", result: result}), "*");
 +
    return true;
 +
  }
 +
</script>
 +
</head>
 +
<body>
 +
User name:<input type="text" id="username"><br>
 +
Password:<input type="password" id="password">
 +
<button type="button" onclick="authenticate();" value="Submit">Submit</button>
 +
</body>
 +
</source>
  
 
= orion.shell.type =
 
= orion.shell.type =
Line 45: Line 95:
  
 
== Service methods ==
 
== Service methods ==
 +
Implementations of <tt>orion.shell.type</tt> define the following methods:
  
Implementations of <tt>orion.shell.type</tt> define the following functions:
+
; parse(arg, typeSpec, context)
 +
: Takes the currently-typed user string and returns an object with completion suggestions. The arguments to this method are:
 +
:: '''arg'''
 +
:: An object with properties {prefix: string, suffix: string, text: string}, contains the user's currently-typed string for an instance of this parameter
 +
:: '''typeSpec'''
 +
:: This parameter's typeSpec as defined by the command that is using it
 +
:: '''context'''
 +
:: An object with property {lastParseTimestamp: number}, which can aid in determining when the completion suggestions being returned should be recomputed
  
;parse(arg, typeSpec, context)
+
The return value for this method must be either an <tt>orion.Deferred</tt> (if computation of the result object has to be done asynchronously), or an object with the following properties:
: Takes the currently-typed user string and returns an object with completion suggestions.  The arguments to this function are:
+
:;arg
+
::An object with properties {prefix: string, suffix: string, text: string}, contains the user's currently-typed string for an instance of this parameter
+
:;typeSpec
+
::This parameter's typeSpec as defined by the command that is using it
+
:;context
+
::An object with additional information; currently its only property is {lastParseTimestamp: number}, which can aid in determining when the completion suggestions being returned should be recomputed
+
  
The return value for this service must be either an orion.Deferred (if computation of the result object has to be done asynchronously), or an object with the following properties:
+
: '''status'''
 
+
: One of the following numbers:
:;status
+
:: <code>0</code>: (MATCH) the currently-typed string matches a valid value (ie.- the typing of this parameter value can now be considered complete)
::One of the following numbers:
+
:: <code>1</code>: (PARTIAL) the currently-typed string partially matches a valid value (ie.- the typing of a valid value in progress)
:::0: (MATCH) the currently-typed string matches a valid value (ie.- the typing of this parameter value can now be considered complete)
+
:: <code>2</code>: (ERROR) the currently-typed string does not match a subset of the initial characters of any valid values
:::1: (PARTIAL) the currently-typed string partially matches a valid value (ie.- the typing of a valid value in progress)
+
: '''message'''
:::2: (ERROR) the currently-typed string does not match a subset of the initial characters of any valid values
+
:: (Optional) An error message to display in the Shell page
:;message
+
: '''predictions'''
::(Optional) An error message to display in the Shell page
+
:: An array of {name: string, value: object} objects that are valid completions for the currently-typed string (name is the string to display in the suggestions list)
:;predictions
+
: '''value'''
::An array of {name: string, value: object} objects that are valid completions for the currently-typed string (name is the string to display in the suggestions list)
+
:: If the currently-typed string is a match for a valid value then set this property to that {name: string, value: object} value, otherwise set its value to undefined
:;value
+
::If the currently-typed string is a match for a valid value then set this property to that {name: string, value: object} value, otherwise set its value to undefined
+
  
 
;stringify(object, typeSpec)
 
;stringify(object, typeSpec)
: (Optional) Returns an object's string representation that is appropriate for display in the Shell page's output area.  An example case where this string may not be the same as the "name" that was specified for it by the parse service is if a typed relative path should be resolved to an absolute path.  If this service is not implemented then the object's "name" that was specified for it by the parse service implementation is used as its display string. The arguments to this function are:
+
: (Optional) Returns an object's string representation that is appropriate for display in the Shell page's output area.  An example case where this string may not be the same as the "name" that was provided for it by the parse service is if a typed relative path should be resolved to an absolute path.  If this service is not implemented then the object's "name" that was provided for it by the parse service implementation is used as its display string.<br>The arguments to this method are:
:;object
+
:: '''object'''
::The object to stringify
+
::: The object to stringify
:;typeSpec
+
:: '''typeSpec'''
::This parameter's typeSpec as defined by the command that is using it
+
::: This parameter's typeSpec as defined by the command that is using it
  
 
== Service attributes ==
 
== Service attributes ==
Line 88: Line 137:
  
 
The following sample plug-in contributes a "lab" type and "printLab" command to the Shell:
 
The following sample plug-in contributes a "lab" type and "printLab" command to the Shell:
<pre>
+
<source lang="javascript" enclose="div" >
 
<html>
 
<html>
 
<head>
 
<head>
Line 165: Line 214:
 
  /* contribute a command that uses the custom type */
 
  /* contribute a command that uses the custom type */
  
  var printLabProperties = {  
+
  var printLabProperties = {
 
   name: "printLab",
 
   name: "printLab",
 
   description: "Print a lab location",
 
   description: "Print a lab location",
Line 175: Line 224:
 
   returnType: "string"
 
   returnType: "string"
 
  };
 
  };
  var printLabImpl = {
+
var printLabImpl = {
 
   callback: function(args) {
 
   callback: function(args) {
 
     var result = "Lab name: " + args.lab.name + "\nLocation: " + args.lab.address;
 
     var result = "Lab name: " + args.lab.name + "\nLocation: " + args.lab.address;
Line 192: Line 241:
 
</head>
 
</head>
 
</html>
 
</html>
</pre>
+
</source>
 
+
  
 
When this plug-in is installed the user can use the "printLab" command in the Shell.  The first image below shows a user in the process of entering a printLab command, and is shown a list of valid argument values.
 
When this plug-in is installed the user can use the "printLab" command in the Shell.  The first image below shows a user in the process of entering a printLab command, and is shown a list of valid argument values.
  
 
[[Image:orion-shell-command-example1.jpg]]
 
[[Image:orion-shell-command-example1.jpg]]
 
  
 
In the following image the printLab command has executed and its result is shown in the output area.
 
In the following image the printLab command has executed and its result is shown in the output area.
  
 
[[Image:orion-shell-command-example2.jpg]]
 
[[Image:orion-shell-command-example2.jpg]]

Latest revision as of 16:13, 7 November 2013

Contents

[edit] Overview of contributing services to the Shell

The Shell page provides a view through which users can interact with Orion via a command-line interface. The page provides some basic commands for navigating Orion's workspace, and defines services that allow plug-ins to contribute additional commands and command parameter types.

[edit] orion.shell.command

The orion.shell.command service is used to contribute commands to the Shell page. When the service is executed, an object containing the user-supplied argument values is passed to the service's callback method. The command then optionally returns a response (a return value) to be displayed in the Shell page.

[edit] Service methods

Implementations of orion.shell.command may define the following method:

callback(args, context)
Takes the user-supplied command-line arguments and optionally returns a response value. The arguments are as follows:
args
An object with the user-supplied command-line arguments (if any)
context
An object with property {cwd: string}, indicating the user's current working directory in the Shell page

The basic return value types of callback() are:

string (note that occurrences of [link text](link address) are converted to links when output)
file object: {path: string (relative to CWD), isDirectory: boolean (default: false), blob: aBlob (optional)}
blob object: {blob: aBlob}

Implementations of callback() can return either a value with one of these basic types, or an orion.Deferred. Returning an orion.Deferred facilitates the asynchronous return of a result value (by invoking Deferred.resolve()), the incremental display of progress (by invoking Deferred.progress(string)), and the interim use of a delegated UI that is shown in a frame provided by the Shell page.

To make use of a delegated UI, invoke Deferred.progress({uriTemplate: url, width: string, height: string}), where url at the content to show in the provided frame (example). This content is subsequently responsible for sending the command's result value to the Shell page by posting the following message:

window.parent.postMessage(JSON.stringify({pageService: "orion.page.delegatedUI", source: theCommandName, result: theResultValue}), "*");

The only context where a contributed command would not define this service method is to assist with the contribution of sub-commands. For example, to contribute commands "tar create" and "tar extract", a parent command "tar" without a service method must first be contributed.

[edit] Service attributes

Implementations of orion.shell.command define the following attributes:

name
The name that is typed at the command line to invoke the command
description
(Recommended) A brief description of the command
manual
(Optional) A longer description of the command
parameters
(Optional) An array of the parameters that the command accepts

The Shell page currently uses GCLI as its underlying shell widget, and consequently has adopted its syntax for parameter specification. The basic parameter object attributes are:

name
The parameter's identifier
type
One of { string | boolean | number | array | selection | file | blob | a custom orion.shell.type}
description
(Recommended) A brief description of the parameter
defaultValue
(Optional) The value assumed by the parameter if the user does not supply a value for it. Specifying this makes the parameter optional.

[edit] Example of Using a Delegated UI

The following "login" command implementation delegates to accompanying file "authenticationPrompter.html":

callback: function(args, context) {
 var result = new orion.Deferred();
 var url = window.location.href;
 url = url.substring(0, url.lastIndexOf('/')); //$NON-NLS-0$
 setTimeout(function() {
   result.progress({uriTemplate: url + "/authenticationPrompter.html", width: "400px", height: "100px"});
 });
 return result;
}

authenticationPrompter.html shows simple username/password fields in the frame that has been provided to it by the Shell page. When the page's Submit button is pressed it performs the authentication and posts the result value. The frame showing this page is subsequently removed by the Shell page.

<head>
<script>
  function authenticate() {
    /* do authentication here, assume is fine, so post result back to Shell page */
    var result = "Authenticated user: " + document.getElementById("username").value;
    window.parent.postMessage(JSON.stringify({pageService: "orion.page.delegatedUI", source: "login", result: result}), "*");
    return true;
  }
</script>
</head>
<body>
 User name:<input type="text" id="username"><br>
 Password:<input type="password" id="password">
 <button type="button" onclick="authenticate();" value="Submit">Submit</button>
</body>

[edit] orion.shell.type

The orion.shell.type service is used to contribute parameter types to the Shell page. These enable contributed commands to have custom parameters with dynamically-computed completion suggestions. The service is executed when the Shell page needs either completion suggestion values to display, or the string representation for a given value.

[edit] Service methods

Implementations of orion.shell.type define the following methods:

parse(arg, typeSpec, context)
Takes the currently-typed user string and returns an object with completion suggestions. The arguments to this method are:
arg
An object with properties {prefix: string, suffix: string, text: string}, contains the user's currently-typed string for an instance of this parameter
typeSpec
This parameter's typeSpec as defined by the command that is using it
context
An object with property {lastParseTimestamp: number}, which can aid in determining when the completion suggestions being returned should be recomputed

The return value for this method must be either an orion.Deferred (if computation of the result object has to be done asynchronously), or an object with the following properties:

status
One of the following numbers:
0: (MATCH) the currently-typed string matches a valid value (ie.- the typing of this parameter value can now be considered complete)
1: (PARTIAL) the currently-typed string partially matches a valid value (ie.- the typing of a valid value in progress)
2: (ERROR) the currently-typed string does not match a subset of the initial characters of any valid values
message
(Optional) An error message to display in the Shell page
predictions
An array of {name: string, value: object} objects that are valid completions for the currently-typed string (name is the string to display in the suggestions list)
value
If the currently-typed string is a match for a valid value then set this property to that {name: string, value: object} value, otherwise set its value to undefined
stringify(object, typeSpec)
(Optional) Returns an object's string representation that is appropriate for display in the Shell page's output area. An example case where this string may not be the same as the "name" that was provided for it by the parse service is if a typed relative path should be resolved to an absolute path. If this service is not implemented then the object's "name" that was provided for it by the parse service implementation is used as its display string.
The arguments to this method are:
object
The object to stringify
typeSpec
This parameter's typeSpec as defined by the command that is using it

[edit] Service attributes

Implementations of orion.shell.type define the following attribute:

name
The name of the parameter type (used in the plug-in definition of contributed commands)

[edit] Example

The following sample plug-in contributes a "lab" type and "printLab" command to the Shell:

<html>
<head>
<script src="/orion/plugin.js"></script>
<script src="/orion/Deferred.js"></script>
<script>

 /* first, contribute a custom type that will be used by the contributed command */

 var CompletionStatus = {
   MATCH: 0,
   PARTIAL: 1,
   ERROR: 2
 };
 var labTypeProperties = {
   name: "lab"
 };
 var labTypeImpl = {
   parse: function(arg, typeSpec, context) {
     /*
      * Compute all potential prediction objects here, can be done asynchronously
      * if needed (eg.- if need to wait on an XHR).
      */

     var ottawaValue = {name: 'Ottawa', address: 'Kanata, actually', climate: 'colder'};
     var rtpValue = {name: 'RTP', address: 'Raleigh', climate: 'warmer'};
     var potentialPredictions = [
       {
         name: 'Ottawa', /* the string to be typed */
         value: ottawaValue /* the corresponding object */
       },
       {
         name: 'RTP', /* the string to be typed */
         value: rtpValue /* the corresponding object */
       }
     ];

     var value; /* undefined until a valid value is fully typed */
     var status; /* one of the CompletionStatus values above */
     var predictions = []; /* an [] of {name: typedString, value: object} */

     for (var i = 0; i < potentialPredictions.length; i++) {
       if (potentialPredictions[i].name.indexOf(arg.text) === 0) {
         predictions.push(potentialPredictions[i]);
         if (potentialPredictions[i].name === arg.text) {
           value = potentialPredictions[i].value;
         }
       }
     }

     status = CompletionStatus.ERROR;
     if (predictions.length > 0) {
       status = value ? CompletionStatus.MATCH : CompletionStatus.PARTIAL;
     }
     var result = {
       value: value,
       message: (status === CompletionStatus.ERROR ? ("'" + arg.text + "' is not valid") : undefined),
       status: status,
       predictions: predictions
     };

     /*
      * If all of the above can be computed synchronously then just return result directly.
      * If the above cannot be done synchronously (eg.- waiting on an XHR) then return
      * a promise as demonstrated below and resolve it when the result becomes ready.
      */

     var promise = new orion.Deferred();
     setTimeout(
       function() {
         promise.resolve(result); /* result has become ready some time later */
       }
     );
     return promise;
   }
 };

 /* contribute a command that uses the custom type */

 var printLabProperties = {
   name: "printLab",
   description: "Print a lab location",
   parameters: [{
     name: "lab",
     type: {name: "lab", showClimateToo: true},
     description: "The name of the lab to print info for"
   }],
   returnType: "string"
 };
 var printLabImpl = {
   callback: function(args) {
     var result = "Lab name: " + args.lab.name + "\nLocation: " + args.lab.address;
     if (args.lab.climate) {
       result += "\nClimate: " + args.lab.climate;
     }
     return result;
   }
 };

 var provider = new orion.PluginProvider();
 provider.registerServiceProvider("orion.shell.type", labTypeImpl, labTypeProperties);
 provider.registerServiceProvider("orion.shell.command", printLabImpl, printLabProperties);
 provider.connect();
</script>
</head>
</html>

When this plug-in is installed the user can use the "printLab" command in the Shell. The first image below shows a user in the process of entering a printLab command, and is shown a list of valid argument values.

Orion-shell-command-example1.jpg

In the following image the printLab command has executed and its result is shown in the output area.

Orion-shell-command-example2.jpg