Bug 701712 - GCLI help should be more, um helpful; r=dcamp,dao
authorJoe Walker <jwalker@mozilla.com>
Thu, 08 Dec 2011 12:08:35 +0000
changeset 82257 cab4a629450e8bcca2780114cba326acac2ba421
parent 82182 6785d3003414c46f93e3d79096bbe2de07772075
child 82258 71054aef1a3a3bd2a01440a47a4571831c8caee9
push id21589
push usertim.taubert@gmx.de
push dateFri, 09 Dec 2011 04:57:11 +0000
treeherdermozilla-central@9e7239c0f557 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp, dao
bugs701712
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 701712 - GCLI help should be more, um helpful; r=dcamp,dao
browser/devtools/webconsole/GcliCommands.jsm
browser/devtools/webconsole/HUDService.jsm
browser/devtools/webconsole/gcli.jsm
browser/devtools/webconsole/test/browser/browser_gcli_integrate.js
browser/locales/en-US/chrome/browser/devtools/gcli.properties
browser/themes/gnomestripe/devtools/gcli.css
browser/themes/pinstripe/devtools/gcli.css
browser/themes/winstripe/devtools/gcli.css
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -57,50 +57,16 @@ gcli.addCommand({
   ],
   returnType: "string",
   exec: function Command_echo(args, context) {
     return args.message;
   }
 });
 
 
-let canon = gcli._internal.require("gcli/canon");
-
-/**
- * 'help' command
- */
-gcli.addCommand({
-  name: "help",
-  returnType: "html",
-  description: gcli.lookup("helpDesc"),
-  exec: function Command_help(args, context) {
-    let output = [];
-
-    output.push("<strong>" + gcli.lookup("helpAvailable") + ":</strong><br/>");
-
-    let commandNames = canon.getCommandNames();
-    commandNames.sort();
-
-    output.push("<table>");
-    for (let i = 0; i < commandNames.length; i++) {
-      let command = canon.getCommand(commandNames[i]);
-      if (!command.hidden && command.description) {
-        output.push("<tr>");
-        output.push('<th class="gcli-help-right">' + command.name + "</th>");
-        output.push("<td>&#x2192; " + command.description + "</td>");
-        output.push("</tr>");
-      }
-    }
-    output.push("</table>");
-
-    return output.join("");
-  }
-});
-
-
 /**
  * 'console' command
  */
 gcli.addCommand({
   name: "console",
   description: gcli.lookup("consoleDesc"),
   manual: gcli.lookup("consoleManual")
 });
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -87,16 +87,22 @@ XPCOMUtils.defineLazyGetter(this, "CssRu
 });
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function () {
   var obj = {};
   Cu.import("resource://gre/modules/NetUtil.jsm", obj);
   return obj.NetUtil;
 });
 
+XPCOMUtils.defineLazyGetter(this, "Templater", function () {
+  var obj = {};
+  Cu.import("resource:///modules/devtools/Templater.jsm", obj);
+  return obj.Templater;
+});
+
 XPCOMUtils.defineLazyGetter(this, "PropertyPanel", function () {
   var obj = {};
   try {
     Cu.import("resource:///modules/PropertyPanel.jsm", obj);
   } catch (err) {
     Cu.reportError(err);
   }
   return obj.PropertyPanel;
@@ -6849,24 +6855,46 @@ GcliTerm.prototype = {
     this.writeOutput(aEvent.output.typed, { category: CATEGORY_INPUT });
 
     if (aEvent.output.output == null) {
       return;
     }
 
     let output = aEvent.output.output;
     if (aEvent.output.command.returnType == "html" && typeof output == "string") {
-      let frag = this.document.createRange().createContextualFragment(
+      output = this.document.createRange().createContextualFragment(
           '<div xmlns="' + HTML_NS + '" xmlns:xul="' + XUL_NS + '">' +
-          output + '</div>');
-
-      output = this.document.createElementNS(HTML_NS, "div");
-      output.appendChild(frag);
-    }
-    this.writeOutput(output);
+          output + '</div>').firstChild;
+    }
+
+    let element = this.document.createRange().createContextualFragment(
+      '<richlistitem xmlns="' + XUL_NS + '" clipboardText="${clipboardText}"' +
+      '    timestamp="${timestamp}" id="${id}" class="hud-msg-node">' +
+      '  <label class="webconsole-timestamp" value="${timestampString}"/>' +
+      '  <vbox class="webconsole-msg-icon-container" style="${iconContainerStyle}">' +
+      '    <image class="webconsole-msg-icon"/>' +
+      '    <spacer flex="1"/>' +
+      '  </vbox>' +
+      '  <hbox flex="1" class="gcliterm-msg-body">${output}</hbox>' +
+      '  <hbox align="start"><label value="1" class="webconsole-msg-repeat"/></hbox>' +
+      '</richlistitem>').firstChild;
+
+    let hud = HUDService.getHudReferenceById(this.hudId);
+    let timestamp = ConsoleUtils.timestamp();
+    new Templater().processNode(element, {
+      iconContainerStyle: "margin-left=" + (hud.groupDepth * GROUP_INDENT) + "px",
+      output: output,
+      timestamp: timestamp,
+      timestampString: ConsoleUtils.timestampString(timestamp),
+      clipboardText: output.innerText,
+      id: "console-msg-" + HUDService.sequenceId()
+    });
+
+    ConsoleUtils.setMessageType(element, CATEGORY_OUTPUT, SEVERITY_LOG);
+    ConsoleUtils.outputMessageNode(element, this.hudId);
   },
 
   /**
    * Setup the eval sandbox, should be called whenever we are attached.
    */
   createSandbox: function Gcli_createSandbox()
   {
     let win = this.context.get().QueryInterface(Ci.nsIDOMWindow);
--- a/browser/devtools/webconsole/gcli.jsm
+++ b/browser/devtools/webconsole/gcli.jsm
@@ -681,29 +681,30 @@ var mozl10n = {};
     }
     catch (ex) {
       throw new Error("Failure in lookupFormat('" + name + "')");
     }
   };
 
 })(mozl10n);
 
-define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/ui/display'], function(require, exports, module) {
+define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/commands/help', 'gcli/ui/display'], function(require, exports, module) {
 
   // The API for use by command authors
   exports.addCommand = require('gcli/canon').addCommand;
   exports.removeCommand = require('gcli/canon').removeCommand;
   exports.lookup = mozl10n.lookup;
   exports.lookupFormat = mozl10n.lookupFormat;
 
   // Internal startup process. Not exported
   require('gcli/types/basic').startup();
   require('gcli/types/javascript').startup();
   require('gcli/types/node').startup();
   require('gcli/cli').startup();
+  require('gcli/commands/help').startup();
 
   var Requisition = require('gcli/cli').Requisition;
   var Display = require('gcli/ui/display').Display;
 
   var cli = require('gcli/cli');
   var jstype = require('gcli/types/javascript');
   var nodetype = require('gcli/types/node');
 
@@ -1024,17 +1025,18 @@ canon.removeCommand = function removeCom
   canon.canonChange();
 };
 
 /**
  * Retrieve a command by name
  * @param name The name of the command to retrieve
  */
 canon.getCommand = function getCommand(name) {
-  return commands[name];
+  // '|| undefined' is to silence 'reference to undefined property' warnings
+  return commands[name] || undefined;
 };
 
 /**
  * Get an array of all the registered commands.
  */
 canon.getCommands = function getCommands() {
   // return Object.values(commands);
   return Object.keys(commands).map(function(name) {
@@ -1185,19 +1187,27 @@ exports.createEvent = function(name) {
   return event;
 };
 
 
 //------------------------------------------------------------------------------
 
 var dom = {};
 
+/**
+ * XHTML namespace
+ */
 dom.NS_XHTML = 'http://www.w3.org/1999/xhtml';
 
 /**
+ * XUL namespace
+ */
+dom.NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+
+/**
  * Create an HTML or XHTML element depending on whether the document is HTML
  * or XML based. Where HTML/XHTML elements are distinguished by whether they
  * are created using doc.createElementNS('http://www.w3.org/1999/xhtml', tag)
  * or doc.createElement(tag)
  * If you want to create a XUL element then you don't have a problem knowing
  * what namespace you want.
  * @param doc The document in which to create the element
  * @param tag The name of the tag to create
@@ -1244,17 +1254,17 @@ dom.importCss = function(cssText, doc) {
  * There are problems with innerHTML on XML documents, so we need to do a dance
  * using document.createRange().createContextualFragment() when in XML mode
  */
 dom.setInnerHtml = function(elem, html) {
   if (dom.isXmlDocument(elem.ownerDocument)) {
     dom.clearElement(elem);
     html = '<div xmlns="' + dom.NS_XHTML + '">' + html + '</div>';
     var range = elem.ownerDocument.createRange();
-    var child = range.createContextualFragment(html).childNodes[0];
+    var child = range.createContextualFragment(html).firstChild;
     while (child.hasChildNodes()) {
       elem.appendChild(child.firstChild);
     }
   }
   else {
     elem.innerHTML = html;
   }
 };
@@ -4522,17 +4532,17 @@ Requisition.prototype.exec = function(in
       outputObject.error = error;
       outputObject.output = output;
       outputObject.completed = true;
       this.commandOutputManager.sendCommandOutput(outputObject);
     }
   }).bind(this);
 
   try {
-    var context = new ExecutionContext(this.environment, this.document);
+    var context = new ExecutionContext(this);
     var reply = command.exec(args, context);
 
     if (reply != null && reply.isPromise) {
       reply.then(
         function(data) { onComplete(data, false); },
         function(error) { onComplete(error, true); });
 
       // Add progress to our promise and add a handler for it here
@@ -5007,19 +5017,20 @@ Requisition.prototype._assign = function
 };
 
 exports.Requisition = Requisition;
 
 
 /**
  * Functions and data related to the execution of a command
  */
-function ExecutionContext(environment, document) {
-  this.environment = environment;
-  this.document = document;
+function ExecutionContext(requisition) {
+  this.requisition = requisition;
+  this.environment = requisition.environment;
+  this.document = requisition.document;
 }
 
 ExecutionContext.prototype.createPromise = function() {
   return new Promise();
 };
 
 
 });
@@ -5036,16 +5047,279 @@ define('gcli/promise', ['require', 'expo
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
+define('gcli/commands/help', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/l10n', 'gcli/ui/domtemplate', 'text!gcli/commands/help.css', 'text!gcli/commands/help_intro.html', 'text!gcli/commands/help_list.html', 'text!gcli/commands/help_man.html'], function(require, exports, module) {
+var help = exports;
+
+
+var canon = require('gcli/canon');
+var util = require('gcli/util');
+var l10n = require('gcli/l10n');
+
+var Templater = require('gcli/ui/domtemplate').Templater;
+
+var helpCss = require('text!gcli/commands/help.css');
+var helpStyle = undefined;
+var helpIntroHtml = require('text!gcli/commands/help_intro.html');
+var helpIntroTemplate = undefined;
+var helpListHtml = require('text!gcli/commands/help_list.html');
+var helpListTemplate = undefined;
+var helpManHtml = require('text!gcli/commands/help_man.html');
+var helpManTemplate = undefined;
+
+/**
+ * 'help' command
+ * We delay definition of helpCommandSpec until help.startup() to ensure that
+ * the l10n strings have been loaded
+ */
+var helpCommandSpec;
+
+/**
+ * Registration and de-registration.
+ */
+help.startup = function() {
+
+  helpCommandSpec = {
+    name: 'help',
+    description: l10n.lookup('helpDesc'),
+    manual: l10n.lookup('helpManual'),
+    params: [
+      {
+        name: 'search',
+        type: 'string',
+        description: l10n.lookup('helpSearchDesc'),
+        manual: l10n.lookup('helpSearchManual'),
+        defaultValue: null
+      }
+    ],
+    returnType: 'html',
+
+    exec: function(args, context) {
+      help.onFirstUseStartup(context.document);
+
+      var match = canon.getCommand(args.search);
+      if (match) {
+        var clone = helpManTemplate.cloneNode(true);
+        new Templater().processNode(clone, getManTemplateData(match, context));
+        return clone;
+      }
+
+      var parent = util.dom.createElement(context.document, 'div');
+      if (!args.search) {
+        parent.appendChild(helpIntroTemplate.cloneNode(true));
+      }
+      parent.appendChild(helpListTemplate.cloneNode(true));
+      new Templater().processNode(parent, getListTemplateData(args, context));
+      return parent;
+    }
+  };
+
+  canon.addCommand(helpCommandSpec);
+};
+
+help.shutdown = function() {
+  canon.removeCommand(helpCommandSpec);
+
+  helpListTemplate = undefined;
+  helpStyle.parentElement.removeChild(helpStyle);
+  helpStyle = undefined;
+};
+
+/**
+ * Called when the command is executed
+ */
+help.onFirstUseStartup = function(document) {
+  if (!helpIntroTemplate) {
+    helpIntroTemplate = util.dom.createElement(document, 'div');
+    util.dom.setInnerHtml(helpIntroTemplate, helpIntroHtml);
+  }
+  if (!helpListTemplate) {
+    helpListTemplate = util.dom.createElement(document, 'div');
+    util.dom.setInnerHtml(helpListTemplate, helpListHtml);
+  }
+  if (!helpManTemplate) {
+    helpManTemplate = util.dom.createElement(document, 'div');
+    util.dom.setInnerHtml(helpManTemplate, helpManHtml);
+  }
+  if (!helpStyle && helpCss != null) {
+    helpStyle = util.dom.importCss(helpCss, document);
+  }
+};
+
+/**
+ * Find an element within the passed element with the class gcli-help-command
+ * and update the requisition to contain this text.
+ */
+function updateCommand(element, context) {
+  context.requisition.update({
+    typed: element.querySelector('.gcli-help-command').textContent
+  });
+}
+
+/**
+ * Find an element within the passed element with the class gcli-help-command
+ * and execute this text.
+ */
+function executeCommand(element, context) {
+  context.requisition.exec({
+    visible: true,
+    typed: element.querySelector('.gcli-help-command').textContent
+  });
+}
+
+/**
+ * Create a block of data suitable to be passed to the help_list.html template
+ */
+function getListTemplateData(args, context) {
+  return {
+    onclick: function(ev) {
+      updateCommand(ev.currentTarget, context);
+    },
+
+    ondblclick: function(ev) {
+      executeCommand(ev.currentTarget, context);
+    },
+
+    getHeading: function() {
+      return args.search == null ?
+              'Available Commands:' :
+              'Commands starting with \'' + args.search + '\':';
+    },
+
+    getMatchingCommands: function() {
+      var matching = canon.getCommands().filter(function(command) {
+        if (args.search && command.name.indexOf(args.search) !== 0) {
+          // Filtered out because they don't match the search
+          return false;
+        }
+        if (!args.search && command.name.indexOf(' ') != -1) {
+          // We don't show sub commands with plain 'help'
+          return false;
+        }
+        return true;
+      });
+      matching.sort();
+      return matching;
+    }
+  };
+}
+
+/**
+ * Create a block of data suitable to be passed to the help_man.html template
+ */
+function getManTemplateData(command, context) {
+  return {
+    command: command,
+
+    onclick: function(ev) {
+      updateCommand(ev.currentTarget, context);
+    },
+
+    getTypeDescription: function(param) {
+      var input = '';
+      if (param.defaultValue === undefined) {
+        input = 'required';
+      }
+      else if (param.defaultValue === null) {
+        input = 'optional';
+      }
+      else {
+        input = param.defaultValue;
+      }
+      return '(' + param.type.name + ', ' + input + ')';
+    }
+  };
+}
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+  Components.utils.import("resource:///modules/devtools/Templater.jsm");
+  exports.Templater = Templater;
+
+});
+define("text!gcli/commands/help.css", [], void 0);
+define("text!gcli/commands/help_intro.html", [], "\n" +
+  "<h2>Welcome to GCLI</h2>\n" +
+  "\n" +
+  "<p>GCLI is an experiment to create a highly usable JavaScript command line for developers.</p>\n" +
+  "\n" +
+  "<p>\n" +
+  "  Useful links:\n" +
+  "  <a target='_blank' href='https://github.com/joewalker/gcli'>source</a> (BSD),\n" +
+  "  <a target='_blank' href='https://github.com/joewalker/gcli/blob/master/docs/index.md'>documentation</a> (for users/embedders),\n" +
+  "  <a target='_blank' href='https://wiki.mozilla.org/DevTools/Features/GCLI'>Mozilla feature page</a> (for GCLI in the web console).\n" +
+  "</p>\n" +
+  "");
+
+define("text!gcli/commands/help_list.html", [], "\n" +
+  "<h3>${getHeading()}</h3>\n" +
+  "\n" +
+  "<table>\n" +
+  "  <tr foreach=\"command in ${getMatchingCommands()}\"\n" +
+  "      onclick=\"${onclick}\" ondblclick=\"${ondblclick}\">\n" +
+  "    <th class=\"gcli-help-name\">${command.name}</th>\n" +
+  "    <td class=\"gcli-help-arrow\">&#x2192;</td>\n" +
+  "    <td>\n" +
+  "      ${command.description}\n" +
+  "      <span class=\"gcli-out-shortcut gcli-help-command\">help ${command.name}</span>\n" +
+  "    </td>\n" +
+  "  </tr>\n" +
+  "</table>\n" +
+  "");
+
+define("text!gcli/commands/help_man.html", [], "\n" +
+  "<h3>${command.name}</h3>\n" +
+  "\n" +
+  "<h4 class=\"gcli-help-header\">\n" +
+  "  Synopsis:\n" +
+  "  <span class=\"gcli-help-synopsis\" onclick=\"${onclick}\">\n" +
+  "    <span class=\"gcli-help-command\">${command.name}</span>\n" +
+  "    <span foreach=\"param in ${command.params}\">\n" +
+  "      ${param.defaultValue !== undefined ? '[' + param.name + ']' : param.name}\n" +
+  "    </span>\n" +
+  "  </span>\n" +
+  "</h4>\n" +
+  "\n" +
+  "<h4 class=\"gcli-help-header\">Description:</h4>\n" +
+  "\n" +
+  "<p class=\"gcli-help-description\">\n" +
+  "  ${command.manual || command.description}\n" +
+  "</p>\n" +
+  "\n" +
+  "<h4 class=\"gcli-help-header\">Parameters:</h4>\n" +
+  "\n" +
+  "<ul class=\"gcli-help-parameter\">\n" +
+  "  <li if=\"${command.params.length === 0}\">None</li>\n" +
+  "  <li foreach=\"param in ${command.params}\">\n" +
+  "    <tt>${param.name}</tt> ${getTypeDescription(param)}\n" +
+  "    <br/>\n" +
+  "    ${param.manual || param.description}\n" +
+  "  </li>\n" +
+  "</ul>\n" +
+  "");
+
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
 define('gcli/ui/display', ['require', 'exports', 'module' , 'gcli/ui/inputter', 'gcli/ui/arg_fetch', 'gcli/ui/menu', 'gcli/ui/focus'], function(require, exports, module) {
 
 var Inputter = require('gcli/ui/inputter').Inputter;
 var ArgFetcher = require('gcli/ui/arg_fetch').ArgFetcher;
 var CommandMenu = require('gcli/ui/menu').CommandMenu;
 var FocusManager = require('gcli/ui/focus').FocusManager;
 
 /**
@@ -6980,28 +7254,16 @@ CommandMenu.prototype.onCommandChange = 
     this.hide();
   }
 };
 
 exports.CommandMenu = CommandMenu;
 
 
 });
-/*
- * Copyright 2009-2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE.txt or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-
-define('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
-
-  Components.utils.import("resource:///modules/devtools/Templater.jsm");
-  exports.Templater = Templater;
-
-});
 define("text!gcli/ui/menu.css", [], void 0);
 define("text!gcli/ui/menu.html", [], "\n" +
   "<table class=\"gcli-menu-template\" aria-live=\"polite\">\n" +
   "  <tr class=\"gcli-menu-option\" foreach=\"item in ${items}\"\n" +
   "      onclick=\"${onItemClick}\" title=\"${item.manual || ''}\">\n" +
   "    <td class=\"gcli-menu-name\">${item.name}</td>\n" +
   "    <td class=\"gcli-menu-desc\">${item.description}</td>\n" +
   "  </tr>\n" +
--- a/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js
+++ b/browser/devtools/webconsole/test/browser/browser_gcli_integrate.js
@@ -81,19 +81,19 @@ function testCallCommands() {
   gcliterm.inputNode.focus();
   EventUtils.synthesizeKey("d", {});
   is(gcliterm.completeNode.textContent, " ecd", "Completion for \"ecd\"");
 
   // Test a normal command's life cycle
   gcliterm.opts.display.inputter.setInput("echo hello world");
   gcliterm.opts.requisition.exec();
 
-  let nodes = hud.outputNode.querySelectorAll("description");
+  let nodes = hud.outputNode.querySelectorAll(".gcliterm-msg-body");
 
-  is(nodes.length, 2, "Right number of output nodes");
+  is(nodes.length, 1, "Right number of output nodes");
   ok(/hello world/.test(nodes[0].textContent), "the command's output is correct.");
 
   gcliterm.clearOutput();
 }
 
 function testRemoveCommands() {
   let gcli = require("gcli/index");
   gcli.removeCommand(tselarr);
--- a/browser/locales/en-US/chrome/browser/devtools/gcli.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
@@ -89,8 +89,29 @@ nodeParseSyntax=Syntax error in CSS quer
 # error message is displayed.
 nodeParseMultiple=Too many matches (%S)
 
 # LOCALIZATION NOTE (nodeParseNone): When the command line is expecting a CSS
 # string that matches a single node, but no nodes match, this error message is
 # displayed.
 nodeParseNone=No matches
 
+# LOCALIZATION NOTE (helpDesc): A very short description of the 'help'
+# command. This string is designed to be shown in a menu alongside the command
+# name, which is why it should be as short as possible. See helpManual for a
+# fuller description of what it does.
+helpDesc=Get help on the available commands
+
+# LOCALIZATION NOTE (helpManual): A fuller description of the 'help' command.
+# Displayed when the user asks for help on what it does.
+helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found).
+
+# LOCALIZATION NOTE (helpSearchDesc): A very short description of the 'search'
+# parameter to the 'help' command. See helpSearchManual for a fuller
+# description of what it does. This string is designed to be shown in a dialog
+# with restricted space, which is why it should be as short as possible.
+helpSearchDesc=Search string
+
+# LOCALIZATION NOTE (helpSearchManual): A fuller description of the 'search'
+# parameter to the 'help' command. Displayed when the user asks for help on
+# what it does.
+helpSearchManual=A search string to use in narrowing down the list of commands that are displayed to the user. Any part of the string can match, regular expressions are not supported.
+
--- a/browser/themes/gnomestripe/devtools/gcli.css
+++ b/browser/themes/gnomestripe/devtools/gcli.css
@@ -104,29 +104,51 @@
 
 .gcliterm-hint-parent {
   width: 300px;
   padding: 10px 10px 0;
   border-top: 1px solid threedshadow;
   border-bottom: 1px solid threedshadow;
 }
 
-.gcli-help-right {
-  text-align: right;
-}
-
 .gcliterm-menu {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 .gcliterm-hint-nospace {
   display: none;
 }
 
+.gcliterm-msg-body {
+  margin-top: 0;
+  margin-bottom: 3px;
+  -moz-margin-start: 3px;
+  -moz-margin-end: 6px;
+}
+
+/* Extract from display.css, we only want these 2 rules */
+
+.gcli-out-shortcut {
+  border: 1px solid #999;
+  border-radius: 3px;
+  padding: 0 4px;
+  margin: 0 4px;
+  font-size: 70%;
+  color: #666;
+  cursor: pointer;
+  vertical-align: bottom;
+}
+
+.gcli-out-shortcut:before {
+  color: #66F;
+  content: '\bb';
+  padding: 0 2px;
+}
+
 /*
  * The language of a console is not en_US or any other common language
  * (i.e we don't attempt to translate 'console.log(x)')
  * So we fix .gcliterm-input-node/.gcliterm-complete-node elements to be ltr.
  * As a result we also want the hints to pop up on the left (above the prompt)
  */
 .gcliterm-input-node,
 .gcliterm-complete-node,
@@ -146,16 +168,20 @@
  */
 .gcliterm-hint-parent:-moz-locale-dir(rtl),
 .hud-output-node:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
 /* From: $GCLI/mozilla/gcli/ui/gcliterm-gnomestripe.css */
 
+.gcli-out-shortcut {
+  font-family: "DejaVu Sans Mono", monospace;
+}
+
 /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
 
 .gcli-argfetch {
   width: 100%;
   box-sizing: border-box;
   -moz-box-sizing: border-box;
 }
 
@@ -279,8 +305,50 @@
 .gcli-in-closebrace {
   color: #999;
 }
 
 .gcli-prompt {
   color: #66F;
   font-weight: bold;
 }
+
+/* From: $GCLI/lib/gcli/commands/help.css */
+
+.gcli-help-name {
+  text-align: end;
+}
+
+.gcli-help-arrow {
+  font-size: 70%;
+  color: #AAA;
+}
+
+.gcli-help-synopsis {
+  font-family: monospace;
+  font-weight: normal;
+  padding: 0 3px;
+  margin: 0 10px;
+  border: 1px solid #999;
+  border-radius: 3px;
+  color: #666;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.gcli-help-synopsis:before {
+  color: #66F;
+  content: '\bb';
+}
+
+.gcli-help-description {
+  margin: 0 20px;
+  padding: 0;
+}
+
+.gcli-help-parameter {
+  margin: 0 30px;
+  padding: 0;
+}
+
+.gcli-help-header {
+  margin: 10px 0 6px;
+}
--- a/browser/themes/pinstripe/devtools/gcli.css
+++ b/browser/themes/pinstripe/devtools/gcli.css
@@ -104,29 +104,51 @@
 
 .gcliterm-hint-parent {
   width: 300px;
   padding: 10px 10px 0;
   border-top: 1px solid threedshadow;
   border-bottom: 1px solid threedshadow;
 }
 
-.gcli-help-right {
-  text-align: right;
-}
-
 .gcliterm-menu {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 .gcliterm-hint-nospace {
   display: none;
 }
 
+.gcliterm-msg-body {
+  margin-top: 0;
+  margin-bottom: 3px;
+  -moz-margin-start: 3px;
+  -moz-margin-end: 6px;
+}
+
+/* Extract from display.css, we only want these 2 rules */
+
+.gcli-out-shortcut {
+  border: 1px solid #999;
+  border-radius: 3px;
+  padding: 0 4px;
+  margin: 0 4px;
+  font-size: 70%;
+  color: #666;
+  cursor: pointer;
+  vertical-align: bottom;
+}
+
+.gcli-out-shortcut:before {
+  color: #66F;
+  content: '\bb';
+  padding: 0 2px;
+}
+
 /*
  * The language of a console is not en_US or any other common language
  * (i.e we don't attempt to translate 'console.log(x)')
  * So we fix .gcliterm-input-node/.gcliterm-complete-node elements to be ltr.
  * As a result we also want the hints to pop up on the left (above the prompt)
  */
 .gcliterm-input-node,
 .gcliterm-complete-node,
@@ -150,16 +172,20 @@
 }
 
 /* From: $GCLI/mozilla/gcli/ui/gcliterm-pinstripe.css */
 
 .gcliterm-complete-node {
   padding-top: 6px !important;
 }
 
+.gcli-out-shortcut {
+  font-family: Menlo, Monaco, monospace;
+}
+
 /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
 
 .gcli-argfetch {
   width: 100%;
   box-sizing: border-box;
   -moz-box-sizing: border-box;
 }
 
@@ -283,8 +309,50 @@
 .gcli-in-closebrace {
   color: #999;
 }
 
 .gcli-prompt {
   color: #66F;
   font-weight: bold;
 }
+
+/* From: $GCLI/lib/gcli/commands/help.css */
+
+.gcli-help-name {
+  text-align: end;
+}
+
+.gcli-help-arrow {
+  font-size: 70%;
+  color: #AAA;
+}
+
+.gcli-help-synopsis {
+  font-family: monospace;
+  font-weight: normal;
+  padding: 0 3px;
+  margin: 0 10px;
+  border: 1px solid #999;
+  border-radius: 3px;
+  color: #666;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.gcli-help-synopsis:before {
+  color: #66F;
+  content: '\bb';
+}
+
+.gcli-help-description {
+  margin: 0 20px;
+  padding: 0;
+}
+
+.gcli-help-parameter {
+  margin: 0 30px;
+  padding: 0;
+}
+
+.gcli-help-header {
+  margin: 10px 0 6px;
+}
--- a/browser/themes/winstripe/devtools/gcli.css
+++ b/browser/themes/winstripe/devtools/gcli.css
@@ -104,29 +104,51 @@
 
 .gcliterm-hint-parent {
   width: 300px;
   padding: 10px 10px 0;
   border-top: 1px solid threedshadow;
   border-bottom: 1px solid threedshadow;
 }
 
-.gcli-help-right {
-  text-align: right;
-}
-
 .gcliterm-menu {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 .gcliterm-hint-nospace {
   display: none;
 }
 
+.gcliterm-msg-body {
+  margin-top: 0;
+  margin-bottom: 3px;
+  -moz-margin-start: 3px;
+  -moz-margin-end: 6px;
+}
+
+/* Extract from display.css, we only want these 2 rules */
+
+.gcli-out-shortcut {
+  border: 1px solid #999;
+  border-radius: 3px;
+  padding: 0 4px;
+  margin: 0 4px;
+  font-size: 70%;
+  color: #666;
+  cursor: pointer;
+  vertical-align: bottom;
+}
+
+.gcli-out-shortcut:before {
+  color: #66F;
+  content: '\bb';
+  padding: 0 2px;
+}
+
 /*
  * The language of a console is not en_US or any other common language
  * (i.e we don't attempt to translate 'console.log(x)')
  * So we fix .gcliterm-input-node/.gcliterm-complete-node elements to be ltr.
  * As a result we also want the hints to pop up on the left (above the prompt)
  */
 .gcliterm-input-node,
 .gcliterm-complete-node,
@@ -146,16 +168,20 @@
  */
 .gcliterm-hint-parent:-moz-locale-dir(rtl),
 .hud-output-node:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
 /* From: $GCLI/mozilla/gcli/ui/gcliterm-winstripe.css */
 
+.gcli-out-shortcut {
+  font-family: Consolas, Inconsolata, "Courier New", monospace;
+}
+
 /* From: $GCLI/lib/gcli/ui/arg_fetch.css */
 
 .gcli-argfetch {
   width: 100%;
   box-sizing: border-box;
   -moz-box-sizing: border-box;
 }
 
@@ -279,8 +305,50 @@
 .gcli-in-closebrace {
   color: #999;
 }
 
 .gcli-prompt {
   color: #66F;
   font-weight: bold;
 }
+
+/* From: $GCLI/lib/gcli/commands/help.css */
+
+.gcli-help-name {
+  text-align: end;
+}
+
+.gcli-help-arrow {
+  font-size: 70%;
+  color: #AAA;
+}
+
+.gcli-help-synopsis {
+  font-family: monospace;
+  font-weight: normal;
+  padding: 0 3px;
+  margin: 0 10px;
+  border: 1px solid #999;
+  border-radius: 3px;
+  color: #666;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.gcli-help-synopsis:before {
+  color: #66F;
+  content: '\bb';
+}
+
+.gcli-help-description {
+  margin: 0 20px;
+  padding: 0;
+}
+
+.gcli-help-parameter {
+  margin: 0 30px;
+  padding: 0;
+}
+
+.gcli-help-header {
+  margin: 10px 0 6px;
+}