Bug 701712 - GCLI help should be more, um helpful; r=dcamp,dao

This commit is contained in:
Joe Walker 2011-12-08 12:08:35 +00:00
Родитель 02a73a8b0f
Коммит 38302e28fe
8 изменённых файлов: 554 добавлений и 73 удалений

Просмотреть файл

@ -62,40 +62,6 @@ gcli.addCommand({
});
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
*/

Просмотреть файл

@ -92,6 +92,12 @@ XPCOMUtils.defineLazyGetter(this, "NetUtil", function () {
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 {
@ -6854,14 +6860,36 @@ GcliTerm.prototype = {
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);
output + '</div>').firstChild;
}
this.writeOutput(output);
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);
},
/**

Просмотреть файл

@ -686,7 +686,7 @@ var mozl10n = {};
})(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;
@ -699,6 +699,7 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types
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;
@ -1029,7 +1030,8 @@ canon.removeCommand = function removeCommand(commandOrName) {
* @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;
};
/**
@ -1190,8 +1192,16 @@ exports.createEvent = function(name) {
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
@ -1249,7 +1259,7 @@ dom.setInnerHtml = function(elem, html) {
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);
}
@ -4527,7 +4537,7 @@ Requisition.prototype.exec = function(input) {
}).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) {
@ -5012,9 +5022,10 @@ 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() {
@ -5041,6 +5052,269 @@ define('gcli/promise', ['require', 'exports', 'module' ], function(require, expo
* 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;
@ -6984,18 +7258,6 @@ CommandMenu.prototype.onCommandChange = function(ev) {
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" +

Просмотреть файл

@ -86,9 +86,9 @@ function testCallCommands() {
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();

Просмотреть файл

@ -94,3 +94,24 @@ nodeParseMultiple=Too many matches (%S)
# 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.

Просмотреть файл

@ -109,10 +109,6 @@
border-bottom: 1px solid threedshadow;
}
.gcli-help-right {
text-align: right;
}
.gcliterm-menu {
display: -moz-box;
-moz-box-flex: 1;
@ -122,6 +118,32 @@
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)')
@ -151,6 +173,10 @@
/* 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 {
@ -284,3 +310,45 @@
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;
}

Просмотреть файл

@ -109,10 +109,6 @@
border-bottom: 1px solid threedshadow;
}
.gcli-help-right {
text-align: right;
}
.gcliterm-menu {
display: -moz-box;
-moz-box-flex: 1;
@ -122,6 +118,32 @@
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)')
@ -155,6 +177,10 @@
padding-top: 6px !important;
}
.gcli-out-shortcut {
font-family: Menlo, Monaco, monospace;
}
/* From: $GCLI/lib/gcli/ui/arg_fetch.css */
.gcli-argfetch {
@ -288,3 +314,45 @@
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;
}

Просмотреть файл

@ -109,10 +109,6 @@
border-bottom: 1px solid threedshadow;
}
.gcli-help-right {
text-align: right;
}
.gcliterm-menu {
display: -moz-box;
-moz-box-flex: 1;
@ -122,6 +118,32 @@
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)')
@ -151,6 +173,10 @@
/* 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 {
@ -284,3 +310,45 @@
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;
}