Bug 1032789 - Pull together the various GCLI plugin points; r=mratcliffe

This commit is contained in:
Joe Walker 2014-07-10 14:37:09 +01:00
parent c5a0038de8
commit a671a0a954
58 changed files with 643 additions and 698 deletions

View File

@ -7,8 +7,6 @@ let prefBranch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService).getBranch(null)
.QueryInterface(Ci.nsIPrefBranch2);
let settings = require("gcli/settings");
const TEST_URI = "data:text/html;charset=utf-8,gcli-pref1";
function test() {
@ -137,7 +135,9 @@ function spawnTest() {
setup: 'pref show devtools.tilt.enabled',
check: {
args: {
setting: { value: settings.getSetting("devtools.tilt.enabled") }
setting: {
value: options.requisition.system.settings.get("devtools.tilt.enabled")
}
},
},
exec: {

View File

@ -7,8 +7,6 @@ let prefBranch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService).getBranch(null)
.QueryInterface(Ci.nsIPrefBranch2);
let settings = require("gcli/settings");
const TEST_URI = "data:text/html;charset=utf-8,gcli-pref2";
function test() {
@ -27,7 +25,9 @@ function spawnTest() {
setup: 'pref show devtools.editor.tabsize',
check: {
args: {
setting: { value: settings.getSetting("devtools.editor.tabsize") }
setting: {
value: options.requisition.system.settings.get("devtools.editor.tabsize")
}
},
},
exec: {
@ -38,7 +38,9 @@ function spawnTest() {
setup: 'pref set devtools.editor.tabsize 20',
check: {
args: {
setting: { value: settings.getSetting("devtools.editor.tabsize") },
setting: {
value: options.requisition.system.settings.get("devtools.editor.tabsize")
},
value: { value: 20 }
},
},
@ -54,7 +56,9 @@ function spawnTest() {
setup: 'pref show devtools.editor.tabsize',
check: {
args: {
setting: { value: settings.getSetting("devtools.editor.tabsize") }
setting: {
value: options.requisition.system.settings.get("devtools.editor.tabsize")
}
},
},
exec: {
@ -65,7 +69,9 @@ function spawnTest() {
setup: 'pref set devtools.editor.tabsize 1',
check: {
args: {
setting: { value: settings.getSetting("devtools.editor.tabsize") },
setting: {
value: options.requisition.system.settings.get("devtools.editor.tabsize")
},
value: { value: 1 }
},
},
@ -77,7 +83,9 @@ function spawnTest() {
setup: 'pref show devtools.editor.tabsize',
check: {
args: {
setting: { value: settings.getSetting("devtools.editor.tabsize") }
setting: {
value: options.requisition.system.settings.get("devtools.editor.tabsize")
}
},
},
exec: {

View File

@ -10,8 +10,6 @@ let prefBranch = Cc["@mozilla.org/preferences-service;1"]
let supportsString = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
let settings = require("gcli/settings");
const TEST_URI = "data:text/html;charset=utf-8,gcli-pref3";
function test() {
@ -31,7 +29,9 @@ function spawnTest() {
setup: 'pref show devtools.debugger.remote-host',
check: {
args: {
setting: { value: settings.getSetting("devtools.debugger.remote-host") }
setting: {
value: options.requisition.system.settings.get("devtools.debugger.remote-host")
}
},
},
exec: {
@ -42,7 +42,9 @@ function spawnTest() {
setup: 'pref set devtools.debugger.remote-host e.com',
check: {
args: {
setting: { value: settings.getSetting("devtools.debugger.remote-host") },
setting: {
value: options.requisition.system.settings.get("devtools.debugger.remote-host")
},
value: { value: "e.com" }
},
},
@ -54,7 +56,9 @@ function spawnTest() {
setup: 'pref show devtools.debugger.remote-host',
check: {
args: {
setting: { value: settings.getSetting("devtools.debugger.remote-host") }
setting: {
value: options.requisition.system.settings.get("devtools.debugger.remote-host")
}
},
},
exec: {
@ -70,7 +74,9 @@ function spawnTest() {
setup: 'pref set devtools.debugger.remote-host moz.foo',
check: {
args: {
setting: { value: settings.getSetting("devtools.debugger.remote-host") },
setting: {
value: options.requisition.system.settings.get("devtools.debugger.remote-host")
},
value: { value: "moz.foo" }
},
},
@ -82,7 +88,9 @@ function spawnTest() {
setup: 'pref show devtools.debugger.remote-host',
check: {
args: {
setting: { value: settings.getSetting("devtools.debugger.remote-host") }
setting: {
value: options.requisition.system.settings.get("devtools.debugger.remote-host")
}
},
},
exec: {

View File

@ -10,8 +10,6 @@ let prefBranch = Cc["@mozilla.org/preferences-service;1"]
let supportsString = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
let settings = require("gcli/settings");
const TEST_URI = "data:text/html;charset=utf-8,gcli-settings";
function test() {
@ -22,9 +20,14 @@ function spawnTest() {
// Setup
let options = yield helpers.openTab(TEST_URI);
let tiltEnabled = settings.getSetting("devtools.tilt.enabled");
let tabSize = settings.getSetting("devtools.editor.tabsize");
let remoteHost = settings.getSetting("devtools.debugger.remote-host");
require("devtools/commandline/commands-index");
let gcli = require("gcli/index");
yield gcli.load();
let settings = gcli.settings;
let tiltEnabled = settings.get("devtools.tilt.enabled");
let tabSize = settings.get("devtools.editor.tabsize");
let remoteHost = settings.get("devtools.debugger.remote-host");
let tiltEnabledOrig = prefBranch.getBoolPref("devtools.tilt.enabled");
let tabSizeOrig = prefBranch.getIntPref("devtools.editor.tabsize");

View File

@ -42,17 +42,17 @@ function test() {
// var assert = require('../testharness/assert');
// var helpers = require('./helpers');
var Canon = require('gcli/commands/commands').Canon;
var Commands = require('gcli/commands/commands').Commands;
var startCount;
var events;
var canonChange = function(ev) {
var commandsChange = function(ev) {
events++;
};
exports.setup = function(options) {
startCount = options.requisition.canon.getCommands().length;
startCount = options.requisition.system.commands.getAll().length;
events = 0;
};
@ -62,22 +62,22 @@ exports.shutdown = function(options) {
};
exports.testAddRemove1 = function(options) {
var canon = options.requisition.canon;
var commands = options.requisition.system.commands;
return helpers.audit(options, [
{
name: 'testadd add',
setup: function() {
canon.onCanonChange.add(canonChange);
commands.onCommandsChange.add(commandsChange);
canon.addCommand({
commands.add({
name: 'testadd',
exec: function() {
return 1;
}
});
assert.is(canon.getCommands().length,
assert.is(commands.getAll().length,
startCount + 1,
'add command success');
assert.is(events, 1, 'add event');
@ -102,14 +102,14 @@ exports.testAddRemove1 = function(options) {
{
name: 'testadd alter',
setup: function() {
canon.addCommand({
commands.add({
name: 'testadd',
exec: function() {
return 2;
}
});
assert.is(canon.getCommands().length,
assert.is(commands.getAll().length,
startCount + 1,
'read command success');
assert.is(events, 2, 'read event');
@ -128,9 +128,9 @@ exports.testAddRemove1 = function(options) {
{
name: 'testadd remove',
setup: function() {
canon.removeCommand('testadd');
commands.remove('testadd');
assert.is(canon.getCommands().length,
assert.is(commands.getAll().length,
startCount,
'remove command success');
assert.is(events, 3, 'remove event');
@ -149,16 +149,16 @@ exports.testAddRemove1 = function(options) {
};
exports.testAddRemove2 = function(options) {
var canon = options.requisition.canon;
var commands = options.requisition.system.commands;
canon.addCommand({
commands.add({
name: 'testadd',
exec: function() {
return 3;
}
});
assert.is(canon.getCommands().length,
assert.is(commands.getAll().length,
startCount + 1,
'rereadd command success');
assert.is(events, 4, 'rereadd event');
@ -170,11 +170,11 @@ exports.testAddRemove2 = function(options) {
output: /^3$/
},
post: function() {
canon.removeCommand({
commands.remove({
name: 'testadd'
});
assert.is(canon.getCommands().length,
assert.is(commands.getAll().length,
startCount,
'reremove command success');
assert.is(events, 5, 'reremove event');
@ -191,26 +191,26 @@ exports.testAddRemove2 = function(options) {
};
exports.testAddRemove3 = function(options) {
var canon = options.requisition.canon;
var commands = options.requisition.system.commands;
canon.removeCommand({ name: 'nonexistant' });
assert.is(canon.getCommands().length,
commands.remove({ name: 'nonexistant' });
assert.is(commands.getAll().length,
startCount,
'nonexistant1 command success');
assert.is(events, 5, 'nonexistant1 event');
canon.removeCommand('nonexistant');
assert.is(canon.getCommands().length,
commands.remove('nonexistant');
assert.is(commands.getAll().length,
startCount,
'nonexistant2 command success');
assert.is(events, 5, 'nonexistant2 event');
canon.onCanonChange.remove(canonChange);
commands.onCommandsChange.remove(commandsChange);
};
exports.testAltCanon = function(options) {
var canon = options.requisition.canon;
var altCanon = new Canon();
exports.testAltCommands = function(options) {
var commands = options.requisition.system.commands;
var altCommands = new Commands(options.requisition.system.types);
var tss = {
name: 'tss',
@ -224,9 +224,9 @@ exports.testAltCanon = function(options) {
args.str + ':' + args.num + ':' + args.opt;
}
};
altCanon.addCommand(tss);
altCommands.add(tss);
var commandSpecs = altCanon.getCommandSpecs();
var commandSpecs = altCommands.getCommandSpecs();
assert.is(JSON.stringify(commandSpecs),
'[{"item":"command","name":"tss","params":[' +
'{"name":"str","type":"string"},' +
@ -238,16 +238,16 @@ exports.testAltCanon = function(options) {
var remoter = function(args, context) {
assert.is(context.commandName, 'tss', 'commandName is tss');
var cmd = altCanon.getCommand(context.commandName);
var cmd = altCommands.get(context.commandName);
return cmd.exec(args, context);
};
canon.addProxyCommands(commandSpecs, remoter, 'proxy', 'test');
commands.addProxyCommands(commandSpecs, remoter, 'proxy', 'test');
var parent = canon.getCommand('proxy');
var parent = commands.get('proxy');
assert.is(parent.name, 'proxy', 'Parent command called proxy');
var child = canon.getCommand('proxy tss');
var child = commands.get('proxy tss');
assert.is(child.name, 'proxy tss', 'child command called proxy tss');
return helpers.audit(options, [
@ -269,11 +269,11 @@ exports.testAltCanon = function(options) {
output: 'tss:foo:6:3'
},
post: function() {
canon.removeCommand('proxy');
canon.removeCommand('proxy tss');
commands.remove('proxy');
commands.remove('proxy tss');
assert.is(canon.getCommand('proxy'), undefined, 'remove proxy');
assert.is(canon.getCommand('proxy tss'), undefined, 'remove proxy tss');
assert.is(commands.get('proxy'), undefined, 'remove proxy');
assert.is(commands.get('proxy tss'), undefined, 'remove proxy tss');
}
}
]);

View File

@ -100,7 +100,7 @@ exports.testContext = function(options) {
args: {
command: { name: 'context' },
prefix: {
value: options.requisition.canon.getCommand('tsn'),
value: options.requisition.system.commands.get('tsn'),
status: 'VALID',
message: ''
}
@ -195,7 +195,7 @@ exports.testContext = function(options) {
args: {
command: { name: 'context' },
prefix: {
value: options.requisition.canon.getCommand('tsn ext'),
value: options.requisition.system.commands.get('tsn ext'),
status: 'VALID',
message: ''
}
@ -220,7 +220,7 @@ exports.testContext = function(options) {
args: {
command: { name: 'context' },
prefix: {
value: options.requisition.canon.getCommand('tsn deep'),
value: options.requisition.system.commands.get('tsn deep'),
status: 'VALID',
message: ''
}

View File

@ -46,7 +46,7 @@ function test() {
var Status = require('gcli/types/types').Status;
exports.testParse = function(options) {
var date = options.requisition.types.createType('date');
var date = options.requisition.system.types.createType('date');
return date.parseString('now').then(function(conversion) {
// Date comparison - these 2 dates may not be the same, but how close is
// close enough? If this test takes more than 30secs to run the it will
@ -62,7 +62,7 @@ exports.testParse = function(options) {
exports.testMaxMin = function(options) {
var max = new Date();
var min = new Date();
var types = options.requisition.types;
var types = options.requisition.system.types;
var date = types.createType({ name: 'date', max: max, min: min });
assert.is(date.getMax(), max, 'max setup');
@ -71,7 +71,7 @@ exports.testMaxMin = function(options) {
};
exports.testIncrement = function(options) {
var date = options.requisition.types.createType('date');
var date = options.requisition.system.types.createType('date');
return date.parseString('now').then(function(conversion) {
var plusOne = date.increment(conversion.value);
var minusOne = date.decrement(plusOne);

View File

@ -65,7 +65,7 @@ var mockDoc = {
};
exports.testParamGroup = function(options) {
var tsg = options.requisition.canon.getCommand('tsg');
var tsg = options.requisition.system.commands.get('tsg');
assert.is(tsg.params[0].groupName, null, 'tsg param 0 group null');
assert.is(tsg.params[1].groupName, 'First', 'tsg param 1 group First');
@ -423,7 +423,7 @@ exports.testExecNode = function(options) {
return helpers.audit(options, [
{
skipIf: options.isRemote, // No DOM on server
skipIf: options.isNoDom,
setup: 'tse :root',
check: {
input: 'tse :root',

View File

@ -46,7 +46,7 @@ exports.testIntroStatus = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: function commandIntroMissing() {
return options.requisition.canon.getCommand('intro') == null;
return options.requisition.system.commands.get('intro') == null;
},
setup: 'intro',
check: {

View File

@ -75,7 +75,7 @@ exports.shutdown = function(options) {
function jsTestAllowed(options) {
return options.isRemote || options.isNoDom ||
options.requisition.canon.getCommand('{') == null;
options.requisition.system.commands.get('{') == null;
}
exports.testBasic = function(options) {

View File

@ -76,14 +76,14 @@ exports.testScript = function(options) {
return helpers.audit(options, [
{
skipIf: function commandJsMissing() {
return options.requisition.canon.getCommand('{') == null;
return options.requisition.system.commands.get('{') == null;
},
setup: '{ wind<TAB>',
check: { input: '{ window' }
},
{
skipIf: function commandJsMissing() {
return options.requisition.canon.getCommand('{') == null;
return options.requisition.system.commands.get('{') == null;
},
setup: '{ window.docum<TAB>',
check: { input: '{ window.document' }
@ -95,7 +95,7 @@ exports.testJsdom = function(options) {
return helpers.audit(options, [
{
skipIf: function jsDomOrCommandJsMissing() {
return options.requisition.canon.getCommand('{') == null;
return options.requisition.system.commands.get('{') == null;
},
setup: '{ window.document.titl<TAB>',
check: { input: '{ window.document.title ' }

View File

@ -57,6 +57,7 @@ exports.shutdown = function(options) {
exports.testNode = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: options.isNoDom,
setup: 'tse ',
check: {
input: 'tse ',

View File

@ -45,7 +45,7 @@ function test() {
exports.testPrefShowStatus = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: options.requisition.canon.getCommand('pref') == null,
skipRemainingIf: options.requisition.system.commands.get('pref') == null,
setup: 'pref s',
check: {
typed: 'pref s',
@ -114,7 +114,7 @@ exports.testPrefShowStatus = function(options) {
exports.testPrefSetStatus = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: options.requisition.canon.getCommand('pref') == null,
skipRemainingIf: options.requisition.system.commands.get('pref') == null,
setup: 'pref s',
check: {
typed: 'pref s',

View File

@ -43,10 +43,9 @@ function test() {
// var assert = require('../testharness/assert');
// var helpers = require('./helpers');
var mockSettings = require('./mockSettings');
var settings = require('gcli/settings');
exports.testPrefExec = function(options) {
if (options.requisition.canon.getCommand('pref') == null) {
if (options.requisition.system.commands.get('pref') == null) {
assert.log('Skipping test; missing pref command.');
return;
}
@ -135,7 +134,7 @@ exports.testPrefExec = function(options) {
},
{
skipRemainingIf: function commandPrefListMissing() {
return options.requisition.canon.getCommand('pref list') == null;
return options.requisition.system.commands.get('pref list') == null;
},
setup: 'pref list tempNum',
check: {

View File

@ -310,7 +310,7 @@ exports.testRemoteWebsocket = function(options) {
},
exec: {
output: [
/^GCLI is an experiment/,
/GCLI is an experiment/,
/F1\/Escape/
],
type: 'intro',

View File

@ -310,7 +310,7 @@ exports.testRemoteXhr = function(options) {
},
exec: {
output: [
/^GCLI is an experiment/,
/GCLI is an experiment/,
/F1\/Escape/
],
type: 'intro',

View File

@ -68,7 +68,7 @@ exports.testAllPredictions1 = function(options) {
return;
}
var resource = options.requisition.types.createType('resource');
var resource = options.requisition.system.types.createType('resource');
return resource.getLookup().then(function(opts) {
assert.ok(opts.length > 1, 'have all resources');
@ -84,7 +84,7 @@ exports.testScriptPredictions = function(options) {
return;
}
var types = options.requisition.types;
var types = options.requisition.system.types;
var resource = types.createType({ name: 'resource', include: 'text/javascript' });
return resource.getLookup().then(function(opts) {
assert.ok(opts.length > 1, 'have js resources');
@ -101,7 +101,7 @@ exports.testStylePredictions = function(options) {
return;
}
var types = options.requisition.types;
var types = options.requisition.system.types;
var resource = types.createType({ name: 'resource', include: 'text/css' });
return resource.getLookup().then(function(opts) {
assert.ok(opts.length >= 1, 'have css resources');
@ -117,7 +117,7 @@ exports.testAllPredictions2 = function(options) {
assert.log('Skipping checks due to nodom document.stylsheets support.');
return;
}
var types = options.requisition.types;
var types = options.requisition.system.types;
var scriptRes = types.createType({ name: 'resource', include: 'text/javascript' });
return scriptRes.getLookup().then(function(scriptOptions) {
@ -139,7 +139,7 @@ exports.testAllPredictions3 = function(options) {
return;
}
var types = options.requisition.types;
var types = options.requisition.system.types;
var res1 = types.createType({ name: 'resource' });
return res1.getLookup().then(function(options1) {
var res2 = types.createType('resource');

View File

@ -66,7 +66,7 @@ exports.testFlatCommand = function(options) {
};
exports.testJavascript = function(options) {
if (!options.requisition.canon.getCommand('{')) {
if (!options.requisition.system.commands.get('{')) {
assert.log('Skipping testJavascript because { is not registered');
return;
}

View File

@ -56,7 +56,7 @@ exports.shutdown = function(options) {
};
function forEachType(options, typeSpec, callback) {
var types = options.requisition.types;
var types = options.requisition.system.types;
return util.promiseEach(types.getTypeNames(), function(name) {
typeSpec.name = name;
typeSpec.requisition = options.requisition;
@ -147,7 +147,7 @@ exports.testGetSpec = function(options) {
var str = JSON.stringify(spec);
assert.ok(str != null, 'serializable spec for ' + type.name);
var example = options.requisition.types.createType(spec);
var example = options.requisition.system.types.createType(spec);
assert.ok(example != null, 'creatable spec for ' + type.name);
});
};

View File

@ -489,9 +489,9 @@ helpers._actual = {
},
unassigned: function(options) {
return options.requisition._unassigned.map(assignment => {
return options.requisition._unassigned.map(function(assignment) {
return assignment.arg.toString();
});
}.bind(this));
},
outputState: function(options) {
@ -540,7 +540,7 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return Promise.all(hintsPromise, predictionsPromise).then((values) => {
return Promise.all([ hintsPromise, predictionsPromise ]).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
@ -610,7 +610,7 @@ helpers._createDebugCheck = function(options) {
output += ']);';
return output;
}, util.errorHandler);
}.bind(this), util.errorHandler);
};
/**
@ -933,7 +933,7 @@ helpers._exec = function(options, name, expected) {
}
try {
return requisition.exec({ hidden: true }).then((output) => {
return requisition.exec({ hidden: true }).then(function(output) {
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -993,7 +993,7 @@ helpers._exec = function(options, name, expected) {
}
return { output: output, text: textOutput };
});
}).then(function(data) {
}.bind(this)).then(function(data) {
if (expected.error) {
cli.logErrors = origLogErrors;
}
@ -1229,6 +1229,9 @@ helpers.audit = function(options, audits) {
});
}).then(function() {
return options.automator.setInput('');
}, function(ex) {
options.automator.setInput('');
throw ex;
});
};

View File

@ -24,7 +24,6 @@
var Promise = require('gcli/util/promise').Promise;
var converters = require('gcli/converters/converters');
var mockCommands = {};
// We use an alias for exports here because this module is used in Firefox
@ -34,37 +33,11 @@ var mockCommands = {};
* Registration and de-registration.
*/
mockCommands.setup = function(requisition) {
mockCommands.items.forEach(function(item) {
if (item.item === 'command') {
requisition.canon.addCommand(item);
}
else if (item.item === 'type') {
requisition.types.addType(item);
}
else if (item.item === 'converter') {
converters.addConverter(item);
}
else {
console.error('Ignoring item ', item);
}
});
requisition.system.addItems(mockCommands.items);
};
mockCommands.shutdown = function(requisition) {
mockCommands.items.forEach(function(item) {
if (item.item === 'command') {
requisition.canon.removeCommand(item);
}
else if (item.item === 'type') {
requisition.types.removeType(item);
}
else if (item.item === 'converter') {
converters.removeConverter(item);
}
else {
console.error('Ignoring item ', item);
}
});
requisition.system.removeItems(mockCommands.items);
};
function createExec(name) {

View File

@ -11,7 +11,12 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
XPCOMUtils.defineLazyModuleGetter(this, "promise",
"resource://gre/modules/Promise.jsm", "Promise");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
@ -430,7 +435,7 @@ let gDevToolsBrowser = {
focusEl.setAttribute("disabled", "true");
}
if (devToolbarEnabled && Services.prefs.getBoolPref("devtools.toolbar.visible")) {
win.DeveloperToolbar.show(false);
win.DeveloperToolbar.show(false).catch(console.error);
}
// Enable WebIDE?

View File

@ -73,8 +73,7 @@ let CommandUtils = {
*/
createRequisition: function(environment) {
return gcli.load().then(() => {
let Requisition = require("gcli/cli").Requisition
return new Requisition({ environment: environment });
return gcli.createRequisition({ environment: environment });
});
},
@ -319,9 +318,9 @@ Object.defineProperty(DeveloperToolbar.prototype, 'sequenceId', {
*/
DeveloperToolbar.prototype.toggle = function() {
if (this.visible) {
return this.hide();
return this.hide().catch(console.error);
} else {
return this.show(true);
return this.show(true).catch(console.error);
}
};

View File

@ -104,7 +104,7 @@ There are 3 basic steps in using GCLI in your system.
is needed to get started.
require([ 'gcli/index' ], function(gcli) {
gcli.addCommand(...); // Register custom commands
gcli.add(...); // Register custom commands/types/etc
gcli.createTerminal(); // Create a user interface
});

View File

@ -54,7 +54,7 @@ the future.
This is how to create a basic ``greet`` command:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
description: 'Show a greeting',
params: [
@ -68,7 +68,7 @@ This is how to create a basic ``greet`` command:
exec: function(args, context) {
return 'Hello, ' + args.name;
}
});
}]);
This command is used as follows:
@ -98,18 +98,18 @@ consider starting a new JSM.
Your command will then look something like this:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
description: gcli.lookup("greetDesc")
...
});
}]);
### Web Commands
There are 2 ways to provide translated strings for web use. The first is to
supply the translated strings in the description:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
description: {
'root': 'Show a greeting',
@ -119,7 +119,7 @@ supply the translated strings in the description:
...
}
...
});
}]);
Each description should contain at least a 'root' entry which is the
default if no better match is found. This method has the benefit of being
@ -130,11 +130,11 @@ the majority of which will never be used.
More efficient is to supply a lookup key and ask GCLI to lookup the key from an
appropriate localized strings file:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
description: { 'key': 'demoGreetingDesc' }
...
});
}]);
For web usage, the central store of localized strings is
``lib/gcli/nls/strings.js``. Other string files can be added using the
@ -151,7 +151,7 @@ The ``greet`` command requires the entry of the ``name`` parameter. This
parameter can be made optional with the addition of a ``defaultValue`` to the
parameter:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
description: 'Show a message to someone',
params: [
@ -166,7 +166,7 @@ parameter:
exec: function(args, context) {
return "Hello, " + args.name;
}
});
}]);
Now we can also use the ``greet`` command as follows:
@ -190,7 +190,7 @@ For example, we can also invoke the greet command as follows:
GCLI allows you to specify a 'short' character for any parameter:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{
@ -201,7 +201,7 @@ GCLI allows you to specify a 'short' character for any parameter:
}
],
...
});
}]);
This is used as follows:
@ -236,14 +236,14 @@ more information.
The following examples assume the following definition of the ```greet```
command:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'name', type: 'string' },
{ name: 'repeat', type: 'number' }
],
...
});
}]);
Parameters can be specified either with named arguments:
@ -284,14 +284,14 @@ to achieve this is to use the ```option: true``` property.
For example, using:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'name', type: 'string' },
{ name: 'repeat', type: 'number', option: true, defaultValue: 1 }
],
...
});
}]);
Would mean that this is an error
@ -318,7 +318,7 @@ There is currently no way to make parameters mutually exclusive.
Parameters can have a type of ``selection``. For example:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'name', ... },
@ -330,7 +330,7 @@ Parameters can have a type of ``selection``. For example:
}
],
...
});
}]);
GCLI will enforce that the value of ``arg.lang`` was one of the values
specified. Alternatively ``data`` can be a function which returns an array of
@ -357,7 +357,7 @@ Similarly, ``lookup`` can be a function returning the data of this type.
Number types are mostly self explanatory, they have one special property which
is the ability to specify upper and lower bounds for the number:
gcli.addCommand({
gcli.addItems([{
name: 'volume',
params: [
{
@ -367,7 +367,7 @@ is the ability to specify upper and lower bounds for the number:
}
],
...
});
}]);
You can also specify a ``step`` property which specifies by what amount we
should increment and decrement the values. The ``min``, ``max``, and ``step``
@ -385,7 +385,7 @@ of another parameter. For example:
We can achieve this as follows:
gcli.addCommand({
gcli.addItems([{
name: 'set',
params: [
{
@ -401,7 +401,7 @@ We can achieve this as follows:
}
],
...
});
}]);
Several details are left out of this example, like how the delegateType()
function knows what the current setting is. See the ``pref`` command for an
@ -412,7 +412,7 @@ example.
Parameters can have a type of ``array``. For example:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{
@ -426,7 +426,7 @@ Parameters can have a type of ``array``. For example:
exec: function(args, context) {
return "Hello, " + args.names.join(', ') + '.';
}
});
}]);
This would be used as follows:
@ -453,22 +453,24 @@ examples of commands that should be structured as in a sub-command style -
Groups of commands are specified with the top level command not having an
exec function:
gcli.addCommand({
name: 'tar',
description: 'Commands to manipulate archives',
});
gcli.addCommand({
name: 'tar create',
description: 'Create a new archive',
exec: function(args, context) { ... },
...
});
gcli.addCommand({
name: 'tar extract',
description: 'Extract from an archive',
exec: function(args, context) { ... },
...
});
gcli.addItems([
{
name: 'tar',
description: 'Commands to manipulate archives',
},
{
name: 'tar create',
description: 'Create a new archive',
exec: function(args, context) { ... },
...
},
{
name: 'tar extract',
description: 'Extract from an archive',
exec: function(args, context) { ... },
...
}
]);
## Parameter groups
@ -480,31 +482,31 @@ There are 3 ways to assign a parameter to a group.
The simplest uses ```option: true``` to put a parameter into the default
'Options' group:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'repeat', type: 'number', option: true }
],
...
});
}]);
The ```option``` property can also take a string to use an alternative parameter
group:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'repeat', type: 'number', option: 'Advanced' }
],
...
});
}]);
An example of how this can be useful is 'git' which categorizes parameters into
'porcelain' and 'plumbing'.
Finally, parameters can be grouped together as follows:
gcli.addCommand({
gcli.addItems([{
name: 'greet',
params: [
{ name: 'name', type: 'string', description: 'The name to greet' },
@ -517,7 +519,7 @@ Finally, parameters can be grouped together as follows:
}
],
...
});
}]);
This could be used as follows:
@ -588,7 +590,7 @@ The parameters to the exec function are designed to be useful when you have a
large number of parameters, and to give direct access to the environment (if
used).
gcli.addCommand({
gcli.addItems([{
name: 'echo',
description: 'The message to display.',
params: [
@ -602,7 +604,7 @@ used).
exec: function(args, context) {
return args.message;
}
});
}]);
The ``args`` object contains the values specified on the params section and
provided on the command line. In this example it would contain the message for

View File

@ -54,7 +54,7 @@ contains default implementations:
* increment(value)
* decrement(value)
Type, Conversion and Status are all declared by canon.js.
Type, Conversion and Status are all declared by commands.js.
The values produced by the parse function can be of any type, but if you are
producing your own, you are strongly encouraged to include properties called

View File

@ -17,75 +17,45 @@
'use strict';
var Promise = require('./util/promise').Promise;
var centralCanon = require('./commands/commands').centralCanon;
var connectors = require('./connectors/connectors');
var converters = require('./converters/converters');
var fields = require('./fields/fields');
var languages = require('./languages/languages');
var settings = require('./settings');
var centralTypes = require('./types/types').centralTypes;
var Commands = require('./commands/commands').Commands;
var Connectors = require('./connectors/connectors').Connectors;
var Converters = require('./converters/converters').Converters;
var Fields = require('./fields/fields').Fields;
var Languages = require('./languages/languages').Languages;
var Settings = require('./settings').Settings;
var Types = require('./types/types').Types;
/**
* This is the heart of the API that we expose to the outside
*/
exports.getApi = function() {
var canon = centralCanon;
var types = centralTypes;
exports.createSystem = function() {
settings.startup(types);
var components = {
connector: new Connectors(),
converter: new Converters(),
field: new Fields(),
language: new Languages(),
type: new Types()
};
components.setting = new Settings(components.type);
components.command = new Commands(components.type);
var addItem = function(item) {
var getItemType = function(item) {
if (item.item) {
return item.item;
}
// Some items are registered using the constructor so we need to check
// the prototype for the the type of the item
var type = item.item;
if (type == null && item.prototype) {
type = item.prototype.item;
}
if (type === 'connector') {
connectors.addConnector(item);
}
else if (type === 'converter') {
converters.addConverter(item);
}
else if (type === 'field') {
fields.addField(item);
}
else if (type === 'language') {
languages.addLanguage(item);
}
else if (type === 'setting') {
settings.addSetting(item);
}
else if (type === 'type') {
types.addType(item);
}
else {
canon.addCommand(item);
}
return (item.prototype && item.prototype.item) ?
item.prototype.item : 'command';
};
var addItem = function(item) {
components[getItemType(item)].add(item);
};
var removeItem = function(item) {
if (item.item === 'connector') {
connectors.removeConnector(item);
}
else if (item.item === 'converter') {
converters.removeConverter(item);
}
else if (item.item === 'field') {
fields.removeField(item);
}
else if (item.item === 'language') {
languages.removeLanguage(item);
}
else if (item.item === 'settings') {
settings.removeSetting(types, item);
}
else if (item.item === 'type') {
types.removeType(item);
}
else {
canon.removeCommand(item);
}
components[getItemType(item)].remove(item);
};
/**
@ -145,17 +115,6 @@ exports.getApi = function() {
var pendingChanges = false;
var api = {
addCommand: function(item) { return canon.addCommand(item); },
removeCommand: function(item) { return canon.removeCommand(item); },
addConnector: connectors.addConnector,
removeConnector: connectors.removeConnector,
addConverter: converters.addConverter,
removeConverter: converters.removeConverter,
addLanguage: languages.addLanguage,
removeLanguage: languages.removeLanguage,
addType: function(item) { return types.addType(item); },
removeType: function(item) { return types.removeType(item); },
addItems: function(items) {
items.forEach(addItem);
},
@ -213,31 +172,46 @@ exports.getApi = function() {
}
};
Object.defineProperty(api, 'canon', {
get: function() { return canon; },
set: function(c) { canon = c; },
Object.defineProperty(api, 'commands', {
get: function() { return components.command; },
set: function(commands) { components.command = commands; },
enumerable: true
});
Object.defineProperty(api, 'connectors', {
get: function() { return components.connector; },
enumerable: true
});
Object.defineProperty(api, 'converters', {
get: function() { return components.converter; },
enumerable: true
});
Object.defineProperty(api, 'fields', {
get: function() { return components.field; },
enumerable: true
});
Object.defineProperty(api, 'languages', {
get: function() { return components.language; },
enumerable: true
});
Object.defineProperty(api, 'settings', {
get: function() { return components.setting; },
enumerable: true
});
Object.defineProperty(api, 'types', {
get: function() { return types; },
set: function(t) {
types = t;
settings.startup(types);
get: function() { return components.type; },
set: function(types) {
components.type = types;
components.command.types = types;
components.setting.types = types;
},
enumerable: true
});
return api;
};
/**
* api.getApi() is clean, but generally we want to add the functions to the
* 'exports' object. So this is a quick helper.
*/
exports.populateApi = function(obj) {
var exportable = exports.getApi();
Object.keys(exportable).forEach(function(key) {
obj[key] = exportable[key];
});
};

View File

@ -22,12 +22,9 @@ var host = require('./util/host');
var l10n = require('./util/l10n');
var view = require('./ui/view');
var converters = require('./converters/converters');
var centralCanon = require('./commands/commands').centralCanon;
var Parameter = require('./commands/commands').Parameter;
var CommandOutputManager = require('./commands/commands').CommandOutputManager;
var centralTypes = require('./types/types').centralTypes;
var Status = require('./types/types').Status;
var Conversion = require('./types/types').Conversion;
var commandModule = require('./types/command');
@ -106,9 +103,9 @@ var removeMapping = function(requisition) {
/**
* Some manual intervention is needed in parsing the { command.
*/
function getEvalCommand(canon) {
function getEvalCommand(commands) {
if (getEvalCommand._cmd == null) {
getEvalCommand._cmd = canon.getCommand(evalCmd.name);
getEvalCommand._cmd = commands.get(evalCmd.name);
}
return getEvalCommand._cmd;
}
@ -347,7 +344,7 @@ function CommandAssignment(requisition) {
},
enumerable: true
});
this.param = new Parameter(requisition.types, commandParamMetadata);
this.param = new Parameter(requisition.system.types, commandParamMetadata);
}
CommandAssignment.prototype = Object.create(Assignment.prototype);
@ -368,7 +365,7 @@ exports.CommandAssignment = CommandAssignment;
*/
function UnassignedAssignment(requisition, arg) {
var isIncompleteName = (arg.text.charAt(0) === '-');
this.param = new Parameter(requisition.types, {
this.param = new Parameter(requisition.system.types, {
name: '__unassigned',
description: l10n.lookup('cliOptions'),
type: {
@ -426,19 +423,19 @@ Object.defineProperty(exports, 'logErrors', {
* assignments of values to parameters, each handled by an instance of
* Assignment.
*
* @param system Allows access to the various plug-in points in GCLI. At a
* minimum it must contain commands and types objects.
* @param options A set of options to customize how GCLI is used. Includes:
* - environment An optional opaque object passed to commands in the
* Execution Context.
* - document A DOM Document passed to commands using the Execution Context in
* order to allow creation of DOM nodes. If missing Requisition will use the
* global 'document'.
* global 'document', or leave undefined.
* - commandOutputManager A custom commandOutputManager to which output should
* be sent
* - canon An instance of Canon that specifies the commands that are allowed in
* this Requisition
* @constructor
*/
function Requisition(options) {
function Requisition(system, options) {
options = options || {};
this.environment = options.environment || {};
@ -453,8 +450,7 @@ function Requisition(options) {
}
this.commandOutputManager = options.commandOutputManager || new CommandOutputManager();
this.canon = options.canon || centralCanon;
this.types = options.types || centralTypes;
this.system = system;
this.shell = {
cwd: '/', // Where we store the current working directory
@ -582,7 +578,11 @@ Object.defineProperty(Requisition.prototype, 'executionContext', {
});
Object.defineProperty(this._executionContext, 'shell', {
get: function() { return requisition.shell; },
enumerable : true
enumerable: true
});
Object.defineProperty(this._executionContext, 'system', {
get: function() { return requisition.system; },
enumerable: true
});
if (legacy) {
@ -631,6 +631,10 @@ Object.defineProperty(Requisition.prototype, 'conversionContext', {
get: function() { return requisition.environment; },
enumerable: true
});
Object.defineProperty(this._conversionContext, 'system', {
get: function() { return requisition.system; },
enumerable: true
});
}
return this._conversionContext;
@ -1771,7 +1775,7 @@ function isSimple(typed) {
}
/**
* Looks in the canon for a command extension that matches what has been
* Looks in the commands for a command extension that matches what has been
* typed at the command line.
*/
Requisition.prototype._split = function(args) {
@ -1782,7 +1786,8 @@ Requisition.prototype._split = function(args) {
if (args[0].type === 'ScriptArgument') {
// Special case: if the user enters { console.log('foo'); } then we need to
// use the hidden 'eval' command
conversion = new Conversion(getEvalCommand(this.canon), new ScriptArgument());
conversion = new Conversion(getEvalCommand(this.system.commands),
new ScriptArgument());
this._setAssignmentInternal(this.commandAssignment, conversion);
return;
}
@ -2019,7 +2024,7 @@ Requisition.prototype.exec = function(options) {
if (options.command != null) {
// Fast track by looking up the command directly since passed args
// means there is no command line to parse.
command = this.canon.getCommand(options.command);
command = this.system.commands.get(options.command);
if (!command) {
console.error('Command not found: ' + options.command);
}
@ -2040,7 +2045,7 @@ Requisition.prototype.exec = function(options) {
typed = typed.replace(/\s*}\s*$/, '');
}
var output = new Output({
var output = new Output(this.conversionContext, {
command: command,
args: args,
typed: typed,
@ -2061,7 +2066,7 @@ Requisition.prototype.exec = function(options) {
util.errorHandler(ex);
}
else {
console.log(data);
console.error(data);
}
}
@ -2129,13 +2134,14 @@ exports.Requisition = Requisition;
/**
* A simple object to hold information about the output of a command
*/
function Output(options) {
function Output(context, options) {
options = options || {};
this.command = options.command || '';
this.args = options.args || {};
this.typed = options.typed || '';
this.canonical = options.canonical || '';
this.hidden = options.hidden === true ? true : false;
this.converters = context.system.converters;
this.type = undefined;
this.data = undefined;
@ -2180,7 +2186,7 @@ Output.prototype.complete = function(data, error) {
* Call converters.convert using the data in this Output object
*/
Output.prototype.convert = function(type, conversionContext) {
return converters.convert(this.data, this.type, type, conversionContext);
return this.converters.convert(this.data, this.type, type, conversionContext);
};
Output.prototype.toJson = function() {

View File

@ -19,8 +19,6 @@
var util = require('../util/util');
var l10n = require('../util/l10n');
var centralTypes = require('../types/types').centralTypes;
/**
* Implement the localization algorithm for any documentation objects (i.e.
* description and manual) in a command.
@ -68,7 +66,7 @@ function lookup(data, onUndefined) {
/**
* The command object is mostly just setup around a commandSpec (as passed to
* #addCommand()).
* Commands.add()).
*/
function Command(types, commandSpec) {
Object.keys(commandSpec).forEach(function(key) {
@ -328,11 +326,10 @@ exports.Parameter = Parameter;
/**
* A canon is a store for a list of commands
* A store for a list of commands
*/
function Canon(options) {
options = options || {};
this.types = options.types || centralTypes;
function Commands(types) {
this.types = types;
// A lookup hash of our registered commands
this._commands = {};
@ -342,19 +339,19 @@ function Canon(options) {
this._commandSpecs = {};
// Enable people to be notified of changes to the list of commands
this.onCanonChange = util.createEvent('canon.onCanonChange');
this.onCommandsChange = util.createEvent('commands.onCommandsChange');
}
/**
* Add a command to the canon of known commands.
* Add a command to the list of known commands.
* This function is exposed to the outside world (via gcli/index). It is
* documented in docs/index.md for all the world to see.
* @param commandSpec The command and its metadata.
* @return The new command
*/
Canon.prototype.addCommand = function(commandSpec) {
Commands.prototype.add = function(commandSpec) {
if (this._commands[commandSpec.name] != null) {
// Roughly canon.removeCommand() without the event call, which we do later
// Roughly commands.remove() without the event call, which we do later
delete this._commands[commandSpec.name];
this._commandNames = this._commandNames.filter(function(test) {
return test !== commandSpec.name;
@ -368,17 +365,17 @@ Canon.prototype.addCommand = function(commandSpec) {
this._commandSpecs[commandSpec.name] = commandSpec;
this.onCanonChange();
this.onCommandsChange();
return command;
};
/**
* Remove an individual command. The opposite of #addCommand().
* Remove an individual command. The opposite of Commands.add().
* Removing a non-existent command is a no-op.
* @param commandOrName Either a command name or the command itself.
* @return true if a command was removed, false otherwise.
*/
Canon.prototype.removeCommand = function(commandOrName) {
Commands.prototype.remove = function(commandOrName) {
var name = typeof commandOrName === 'string' ?
commandOrName :
commandOrName.name;
@ -387,14 +384,14 @@ Canon.prototype.removeCommand = function(commandOrName) {
return false;
}
// See start of canon.addCommand if changing this code
// See start of commands.add if changing this code
delete this._commands[name];
delete this._commandSpecs[name];
this._commandNames = this._commandNames.filter(function(test) {
return test !== name;
});
this.onCanonChange();
this.onCommandsChange();
return true;
};
@ -402,7 +399,7 @@ Canon.prototype.removeCommand = function(commandOrName) {
* Retrieve a command by name
* @param name The name of the command to retrieve
*/
Canon.prototype.getCommand = function(name) {
Commands.prototype.get = function(name) {
// '|| undefined' is to silence 'reference to undefined property' warnings
return this._commands[name] || undefined;
};
@ -410,24 +407,17 @@ Canon.prototype.getCommand = function(name) {
/**
* Get an array of all the registered commands.
*/
Canon.prototype.getCommands = function() {
Commands.prototype.getAll = function() {
return Object.keys(this._commands).map(function(name) {
return this._commands[name];
}, this);
};
/**
* Get an array containing the names of the registered commands.
*/
Canon.prototype.getCommandNames = function() {
return this._commandNames.slice(0);
};
/**
* Get access to the stored commandMetaDatas (i.e. before they were made into
* instances of Command/Parameters) so we can remote them.
*/
Canon.prototype.getCommandSpecs = function() {
Commands.prototype.getCommandSpecs = function() {
var commandSpecs = [];
Object.keys(this._commands).forEach(function(name) {
@ -452,7 +442,7 @@ Canon.prototype.getCommandSpecs = function() {
* @param to URL-like string that describes where the commands are executed.
* This is to complete the parent command description.
*/
Canon.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
Commands.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
if (prefix != null) {
if (this._commands[prefix] != null) {
throw new Error(l10n.lookupFormat('canonProxyExists', [ prefix ]));
@ -460,7 +450,7 @@ Canon.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
// We need to add the parent command so all the commands from the other
// system have a parent
this.addCommand({
this.add({
name: prefix,
isProxy: true,
description: l10n.lookupFormat('canonProxyDesc', [ to ]),
@ -481,7 +471,7 @@ Canon.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
commandSpec.name = prefix + ' ' + commandSpec.name;
}
commandSpec.isProxy = true;
this.addCommand(commandSpec);
this.add(commandSpec);
}.bind(this));
};
@ -489,7 +479,7 @@ Canon.prototype.addProxyCommands = function(commandSpecs, remoter, prefix, to) {
* Remove a set of commands added with addProxyCommands.
* @param prefix The name prefix that we assign to all command names
*/
Canon.prototype.removeProxyCommands = function(prefix) {
Commands.prototype.removeProxyCommands = function(prefix) {
var toRemove = [];
Object.keys(this._commandSpecs).forEach(function(name) {
if (name.indexOf(prefix) === 0) {
@ -499,9 +489,9 @@ Canon.prototype.removeProxyCommands = function(prefix) {
var removed = [];
toRemove.forEach(function(name) {
var command = this.getCommand(name);
var command = this.get(name);
if (command.isProxy) {
this.removeCommand(name);
this.remove(name);
removed.push(name);
}
else {
@ -513,9 +503,7 @@ Canon.prototype.removeProxyCommands = function(prefix) {
return removed;
};
exports.Canon = Canon;
exports.centralCanon = new Canon();
exports.Commands = Commands;
/**
* CommandOutputManager stores the output objects generated by executed

View File

@ -17,7 +17,6 @@
'use strict';
var l10n = require('../util/l10n');
var connectors = require('../connectors/connectors');
var cli = require('../cli');
/**
@ -46,8 +45,9 @@ var connector = {
item: 'type',
name: 'connector',
parent: 'selection',
lookup: function() {
return connectors.getConnectors().map(function(connector) {
lookup: function(context) {
var connectors = context.system.connectors;
return connectors.getAll().map(function(connector) {
return { name: connector.name, value: connector };
});
}
@ -72,7 +72,7 @@ var connect = {
short: 'm',
type: 'connector',
description: l10n.lookup('connectMethodDesc'),
defaultValue: undefined, // set to connectors.get('xhr') below
defaultValue: null,
option: true
},
{
@ -91,15 +91,17 @@ var connect = {
throw new Error(l10n.lookupFormat('connectDupReply', [ args.prefix ]));
}
return args.method.connect(args.url).then(function(connection) {
var connector = args.method || context.system.connectors.get('xhr');
return connector.connect(args.url).then(function(connection) {
// Nasty: stash the prefix on the connection to help us tidy up
connection.prefix = args.prefix;
connections[args.prefix] = connection;
return connection.call('specs').then(function(specs) {
var remoter = this.createRemoter(args.prefix, connection);
var canon = cli.getMapping(context).requisition.canon;
canon.addProxyCommands(specs, remoter, args.prefix, args.url);
var commands = cli.getMapping(context).requisition.system.commands;
commands.addProxyCommands(specs, remoter, args.prefix, args.url);
// TODO: We should add type proxies here too
@ -111,8 +113,8 @@ var connect = {
},
/**
* When we register a set of remote commands, we need to provide the canon
* with a proxy executor. This is that executor.
* When we register a set of remote commands, we need to provide a proxy
* executor. This is that executor.
*/
createRemoter: function(prefix, connection) {
return function(cmdArgs, context) {
@ -142,17 +144,6 @@ var connect = {
}
};
/**
* We just need to call connectors.get later than module load time to
* enable something to load the xhr module
*/
Object.defineProperty(connect.params[1], 'defaultValue', {
get: function() {
return connectors.get('xhr');
},
enumerable : true
});
/**
* 'disconnect' command
*/
@ -173,8 +164,8 @@ var disconnect = {
exec: function(args, context) {
var connection = args.prefix;
return connection.disconnect().then(function() {
var canon = cli.getMapping(context).requisition.canon;
var removed = canon.removeProxyCommands(connection.prefix);
var commands = cli.getMapping(context).requisition.system.commands;
var removed = commands.removeProxyCommands(connection.prefix);
delete connections[connection.prefix];
return l10n.lookupFormat('disconnectReply', [ removed.length ]);
});

View File

@ -71,7 +71,7 @@ exports.items = [
cwd: context.shell.cwd
};
return host.spawn(spawnSpec).then(function(output) {
return host.spawn(context, spawnSpec).then(function(output) {
if (output.code === 0) {
return output;
}

View File

@ -127,8 +127,8 @@ function getHelpListData(commandsData, context) {
* Create a block of data suitable to be passed to the help_list.html template
*/
function getMatchingCommands(context, prefix) {
var canon = cli.getMapping(context).requisition.canon;
var commands = canon.getCommands().filter(function(command) {
var commands = cli.getMapping(context).requisition.system.commands;
var reply = commands.getAll().filter(function(command) {
if (command.hidden) {
return false;
}
@ -144,23 +144,23 @@ function getMatchingCommands(context, prefix) {
return true;
});
commands.sort(function(c1, c2) {
reply.sort(function(c1, c2) {
return c1.name.localeCompare(c2.name);
});
commands = commands.map(function(command) {
reply = reply.map(function(command) {
return command.toJson();
});
return commands;
return reply;
}
/**
* Find all the sub commands of the given command
*/
function getSubCommands(context, command) {
var canon = cli.getMapping(context).requisition.canon;
var subcommands = canon.getCommands().filter(function(subcommand) {
var commands = cli.getMapping(context).requisition.system.commands;
var subcommands = commands.getAll().filter(function(subcommand) {
return subcommand.name.indexOf(command.name) === 0 &&
subcommand.name !== command.name &&
!subcommand.hidden;
@ -218,8 +218,8 @@ exports.items = [
],
exec: function(args, context) {
var canon = cli.getMapping(context).requisition.canon;
var command = canon.getCommand(args.search);
var commands = cli.getMapping(context).requisition.system.commands;
var command = commands.get(args.search);
if (command) {
return context.typedData('commandData', {
command: command.toJson(),

View File

@ -17,7 +17,6 @@
'use strict';
var l10n = require('../util/l10n');
var languages = require('../languages/languages');
var cli = require('../cli');
exports.items = [
@ -26,8 +25,8 @@ exports.items = [
item: 'type',
name: 'language',
parent: 'selection',
lookup: function() {
return languages.getLanguages().map(function(language) {
lookup: function(context) {
return context.system.languages.getAll().map(function(language) {
return { name: language.name, value: language };
});
}

View File

@ -30,7 +30,7 @@ exports.items = [
name: 'included',
type: {
name: 'selection',
data: [ 'on', 'off']
data: [ 'on', 'off' ]
},
description: 'Turn mock commands on or off',
}
@ -45,12 +45,12 @@ exports.items = [
on: function(requisition) {
mockCommands.setup(requisition);
mockSettings.setup();
mockSettings.setup(requisition.system);
},
off: function(requisition) {
mockCommands.shutdown(requisition);
mockSettings.shutdown();
mockSettings.shutdown(requisition.system);
}
}
];

View File

@ -17,7 +17,6 @@
'use strict';
var l10n = require('../util/l10n');
var settings = require('../settings');
var Promise = require('../util/promise').Promise;
/**
@ -121,7 +120,7 @@ var prefList = {
// This can be slow, get out of the way of the main thread
setTimeout(function() {
var prefsData = {
settings: settings.getAll(args.search),
settings: context.system.settings.getAll(args.search),
search: args.search
};
resolve(prefsData);

View File

@ -74,18 +74,18 @@ exports.items = [
isFirefox: false,
isPhantomjs: false,
isNoDom: true,
requisition: new Requisition()
requisition: new Requisition(context.system)
};
options.automator = createRequisitionAutomator(options.requisition);
}
var requisition = options.requisition;
requisition.canon.getCommand('mocks').on(requisition);
requisition.system.commands.get('mocks').on(requisition);
helpers.resetResponseTimes();
examiner.reset();
return args.suite.run(options).then(function() {
requisition.canon.getCommand('mocks').off(requisition);
requisition.system.commands.get('mocks').off(requisition);
var output = context.typedData('examiner-output', examiner.toRemote());
if (output.data.summary.status === stati.pass) {

View File

@ -18,11 +18,6 @@
var Promise = require('../util/promise').Promise;
/**
* This is where we cache the connectors that we know about
*/
var connectors = {};
/**
* This is how to implement a connector
* var baseConnector = {
@ -111,38 +106,47 @@ Connection.prototype.disconnect = function() {
exports.Connection = Connection;
/**
* A manager for the registered Connectors
*/
function Connectors() {
// This is where we cache the connectors that we know about
this._registered = {};
}
/**
* Add a new connector to the cache
*/
exports.addConnector = function(connector) {
connectors[connector.name] = connector;
Connectors.prototype.add = function(connector) {
this._registered[connector.name] = connector;
};
/**
* Remove an existing connector from the cache
*/
exports.removeConnector = function(connector) {
Connectors.prototype.remove = function(connector) {
var name = typeof connector === 'string' ? connector : connector.name;
delete connectors[name];
delete this._registered[name];
};
/**
* Get access to the list of known connectors
*/
exports.getConnectors = function() {
return Object.keys(connectors).map(function(name) {
return connectors[name];
});
Connectors.prototype.getAll = function() {
return Object.keys(this._registered).map(function(name) {
return this._registered[name];
}.bind(this));
};
/**
* Get access to a connector by name. If name is undefined then use the first
* registered connector as a default.
*/
exports.get = function(name) {
Connectors.prototype.get = function(name) {
if (name == null) {
name = Object.keys(connectors)[0];
name = Object.keys(this._registered)[0];
}
return connectors[name];
return this._registered[name];
};
exports.Connectors = Connectors;

View File

@ -17,8 +17,7 @@
'use strict';
var api = require('../api');
var connectors = require('./connectors');
var Canon = require('../commands/commands').Canon;
var Commands = require('../commands/commands').Commands;
var Types = require('../types/types').Types;
// Patch-up IE9
@ -56,6 +55,7 @@ var items = [
require('../types/union').items,
require('../types/url').items,
require('../fields/fields').items,
require('../fields/delegate').items,
require('../fields/selection').items,
@ -101,32 +101,32 @@ var requiredConverters = [
.filter(function(item) { return item.item === 'converter'; });
/**
* Connect to a remote system and setup the canon/types/converters etc needed
* Connect to a remote system and setup the commands/types/converters etc needed
* to make it all work
*/
exports.connect = function(options) {
options = options || {};
var gcli = api.getApi();
var system = api.createSystem();
// Ugly hack, to aid testing
exports.api = gcli;
exports.api = system;
options.types = gcli.types = new Types();
options.canon = gcli.canon = new Canon({ types: gcli.types });
options.types = system.types = new Types();
options.commands = system.commands = new Commands(system.types);
gcli.addItems(items);
gcli.addItems(requiredConverters);
system.addItems(items);
system.addItems(requiredConverters);
var connector = connectors.get(options.connector);
var connector = system.connectors.get(options.connector);
return connector.connect(options.url).then(function(connection) {
options.connection = connection;
connection.on('canonChanged', function(specs) {
exports.addItems(gcli, specs, connection);
connection.on('commandsChanged', function(specs) {
exports.addItems(system, specs, connection);
});
return connection.call('specs').then(function(specs) {
exports.addItems(gcli, specs, connection);
exports.addItems(system, specs, connection);
return connection;
});
});
@ -139,7 +139,7 @@ exports.addItems = function(gcli, specs, connection) {
};
/**
* Take the data from the 'specs' command (or the 'canonChanged' event) and
* Take the data from the 'specs' command (or the 'commandsChanged' event) and
* add function to proxy the execution back over the connection
*/
exports.addLocalFunctions = function(specs, connection) {
@ -177,9 +177,9 @@ exports.addLocalFunctions = function(specs, connection) {
};
exports.removeRemoteItems = function(gcli, connection) {
gcli.canon.getCommands().forEach(function(command) {
gcli.commands.getAll().forEach(function(command) {
if (command.connection === connection) {
gcli.canon.removeCommand(command);
gcli.commands.remove(command);
}
});
};

View File

@ -42,12 +42,13 @@ Remoter.prototype.addListener = function(action) {
var listener = {
action: action,
caller: function() {
action('canonChanged', this.requisition.canon.getCommandSpecs());
var commands = this.requisition.system.commands;
action('commandsChanged', commands.getCommandSpecs());
}.bind(this)
};
this._listeners.push(listener);
this.requisition.canon.onCanonChange.add(listener.caller);
this.requisition.system.commands.onCommandsChange.add(listener.caller);
};
/**
@ -68,7 +69,7 @@ Remoter.prototype.removeListener = function(action) {
throw new Error('action not a known listener');
}
this.requisition.canon.onCanonChange.remove(listener.caller);
this.requisition.system.commands.onCommandsChange.remove(listener.caller);
};
/**
@ -79,7 +80,7 @@ Remoter.prototype.exposed = {
* Retrieve a list of the remotely executable commands
*/
specs: method(function() {
return this.requisition.canon.getCommandSpecs();
return this.requisition.system.commands.getCommandSpecs();
}, {
request: {},
response: RetVal("json")
@ -189,7 +190,7 @@ Remoter.prototype.exposed = {
* Perform a lookup on a selection type to get the allowed values
*/
selectioninfo: method(function(commandName, paramName, action) {
var command = this.requisition.canon.getCommand(commandName);
var command = this.requisition.system.commands.get(commandName);
if (command == null) {
throw new Error('No command called \'' + commandName + '\'');
}
@ -227,7 +228,8 @@ Remoter.prototype.exposed = {
* @return a promise of a string containing the output of the command
*/
system: method(function(cmd, args, cwd, env) {
return host.spawn({ cmd: cmd, args: args, cwd: cwd, env: env });
var context = this.requisition.executionContext;
return host.spawn(context, { cmd: cmd, args: args, cwd: cwd, env: env });
}, {
request: {
cmd: Arg(0, "string"), // The executable to call
@ -248,7 +250,8 @@ Remoter.prototype.exposed = {
matches: new RegExp(matches)
};
return fileparser.parse(typed, options).then(function(reply) {
var context = this.requisition.executionContext;
return fileparser.parse(context, typed, options).then(function(reply) {
reply.status = reply.status.toString();
if (reply.predictor == null) {
return reply;

View File

@ -134,20 +134,23 @@ function getChainConverter(first, second) {
}
/**
* This is where we cache the converters that we know about
* A manager for the registered Converters
*/
var converters = {
from: {}
};
function Converters() {
// This is where we cache the converters that we know about
this._registered = {
from: {}
};
}
/**
* Add a new converter to the cache
*/
exports.addConverter = function(converter) {
var fromMatch = converters.from[converter.from];
Converters.prototype.add = function(converter) {
var fromMatch = this._registered.from[converter.from];
if (fromMatch == null) {
fromMatch = {};
converters.from[converter.from] = fromMatch;
this._registered.from[converter.from] = fromMatch;
}
fromMatch[converter.to] = converter;
@ -156,8 +159,8 @@ exports.addConverter = function(converter) {
/**
* Remove an existing converter from the cache
*/
exports.removeConverter = function(converter) {
var fromMatch = converters.from[converter.from];
Converters.prototype.remove = function(converter) {
var fromMatch = this._registered.from[converter.from];
if (fromMatch == null) {
return;
}
@ -170,10 +173,10 @@ exports.removeConverter = function(converter) {
/**
* Work out the best converter that we've got, for a given conversion.
*/
function getConverter(from, to) {
var fromMatch = converters.from[from];
Converters.prototype.get = function(from, to) {
var fromMatch = this._registered.from[from];
if (fromMatch == null) {
return getFallbackConverter(from, to);
return this._getFallbackConverter(from, to);
}
var converter = fromMatch[to];
@ -200,15 +203,15 @@ function getConverter(from, to) {
}
}
return getFallbackConverter(from, to);
return this._getFallbackConverter(from, to);
}
return converter;
}
};
/**
* Helper for getConverter to pick the best fallback converter
* Helper for get to pick the best fallback converter
*/
function getFallbackConverter(from, to) {
Converters.prototype._getFallbackConverter = function(from, to) {
console.error('No converter from ' + from + ' to ' + to + '. Using fallback');
if (to === 'dom') {
@ -220,7 +223,7 @@ function getFallbackConverter(from, to) {
}
throw new Error('No conversion possible from ' + from + ' to ' + to + '.');
}
};
/**
* Convert some data from one type to another
@ -230,25 +233,27 @@ function getFallbackConverter(from, to) {
* @param conversionContext An execution context (i.e. simplified requisition)
* which is often required for access to a document, or createView function
*/
exports.convert = function(data, from, to, conversionContext) {
Converters.prototype.convert = function(data, from, to, conversionContext) {
try {
if (from === to) {
return Promise.resolve(data);
}
var converter = getConverter(from, to);
var converter = this.get(from, to);
return host.exec(function() {
return converter.exec(data, conversionContext);
});
}.bind(this));
}
catch (ex) {
var converter = getConverter('error', to);
var converter = this.get('error', to);
return host.exec(function() {
return converter.exec(ex, conversionContext);
});
}.bind(this));
}
};
exports.Converters = Converters;
/**
* Items for export
*/

View File

@ -17,10 +17,7 @@
'use strict';
var util = require('../util/util');
var Field = require('./fields').Field;
var fields = require('./fields');
/**
* A field that works with delegate types by delaying resolution until that
@ -41,7 +38,7 @@ DelegateField.prototype = Object.create(Field.prototype);
DelegateField.prototype.update = function() {
var subtype = this.type.getType(this.options.requisition.executionContext);
if (typeof subtype.parse !== 'function') {
subtype = this.options.requisition.types.createType(subtype);
subtype = this.options.requisition.system.types.createType(subtype);
}
// It's not clear that we can compare subtypes in this way.
@ -55,7 +52,8 @@ DelegateField.prototype.update = function() {
}
this.subtype = subtype;
this.field = fields.getField(subtype, this.options);
var fields = this.options.requisition.system.fields;
this.field = fields.get(subtype, this.options);
util.clearElement(this.element);
this.element.appendChild(this.field.element);

View File

@ -22,7 +22,7 @@ var util = require('../util/util');
* A Field is a way to get input for a single parameter.
* This class is designed to be inherited from. It's important that all
* subclasses have a similar constructor signature because they are created
* via getField(...)
* via Fields.get(...)
* @param type The type to use in conversions
* @param options A set of properties to help fields configure themselves:
* - document: The document we use in calling createElement
@ -125,20 +125,23 @@ exports.Field = Field;
/**
* Internal array of known fields
* A manager for the registered Fields
*/
var fieldCtors = [];
function Fields() {
// Internal array of known fields
this._fieldCtors = [];
}
/**
* Add a field definition by field constructor
* @param fieldCtor Constructor function of new Field
*/
exports.addField = function(fieldCtor) {
Fields.prototype.add = function(fieldCtor) {
if (typeof fieldCtor !== 'function') {
console.error('addField erroring on ', fieldCtor);
throw new Error('addField requires a Field constructor');
console.error('fields.add erroring on ', fieldCtor);
throw new Error('fields.add requires a Field constructor');
}
fieldCtors.push(fieldCtor);
this._fieldCtors.push(fieldCtor);
};
/**
@ -146,18 +149,18 @@ exports.addField = function(fieldCtor) {
* @param field A previously registered field, specified either with a field
* name or from the field name
*/
exports.removeField = function(field) {
Fields.prototype.remove = function(field) {
if (typeof field !== 'string') {
fieldCtors = fieldCtors.filter(function(test) {
this._fieldCtors = this._fieldCtors.filter(function(test) {
return test !== field;
});
}
else if (field instanceof Field) {
exports.removeField(field.name);
this.remove(field.name);
}
else {
console.error('removeField erroring on ', field);
throw new Error('removeField requires an instance of Field');
console.error('fields.remove erroring on ', field);
throw new Error('fields.remove requires an instance of Field');
}
};
@ -171,10 +174,10 @@ exports.removeField = function(field) {
* - requisition: The requisition we're monitoring,
* @return A newly constructed field that best matches the input options
*/
exports.getField = function(type, options) {
Fields.prototype.get = function(type, options) {
var FieldConstructor;
var highestClaim = -1;
fieldCtors.forEach(function(fieldCtor) {
this._fieldCtors.forEach(function(fieldCtor) {
var context = (options.requisition == null) ?
null : options.requisition.executionContext;
var claim = fieldCtor.claim(type, context);
@ -185,7 +188,7 @@ exports.getField = function(type, options) {
});
if (!FieldConstructor) {
console.error('Unknown field type ', type, ' in ', fieldCtors);
console.error('Unknown field type ', type, ' in ', this._fieldCtors);
throw new Error('Can\'t find field for ' + type);
}
@ -196,6 +199,7 @@ exports.getField = function(type, options) {
return new FieldConstructor(type, options);
};
exports.Fields = Fields;
/**
* For use with delegate types that do not yet have anything to resolve to.
@ -228,4 +232,7 @@ BlankField.prototype.getConversion = function() {
return this.type.parseString('', this.requisition.executionContext);
};
exports.addField(BlankField);
/**
* Items for export
*/
exports.items = [ BlankField ];

View File

@ -33,7 +33,7 @@ var Cu = require('chrome').Cu;
* - lib/gcli/connectors/index.js: Client only items when executing remotely
* - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
*/
var items = [
exports.items = [
require('./types/delegate').items,
require('./types/selection').items,
require('./types/array').items,
@ -51,6 +51,7 @@ var items = [
require('./types/union').items,
require('./types/url').items,
require('./fields/fields').items,
require('./fields/delegate').items,
require('./fields/selection').items,
@ -89,8 +90,14 @@ var items = [
].reduce(function(prev, curr) { return prev.concat(curr); }, []);
var api = require('./api');
api.populateApi(exports);
exports.addItems(items);
var system = api.createSystem();
// Export the system API by adding it to our exports
Object.keys(system).forEach(function(key) {
exports[key] = system[key];
});
system.addItems(exports.items);
var host = require('./util/host');
@ -110,9 +117,14 @@ exports.useTarget = host.script.useTarget;
* - hintElement: GCLITerm.hintNode
* - inputBackgroundElement: GCLITerm.inputStack
*/
exports.createDisplay = function(opts) {
exports.createDisplay = function(options) {
var FFDisplay = require('./mozui/ffdisplay').FFDisplay;
return new FFDisplay(opts);
return new FFDisplay(system, options);
};
exports.createRequisition = function(options) {
var Requisition = require('./cli').Requisition;
return new Requisition(system, options);
};
var prefSvc = Cc['@mozilla.org/preferences-service;1']

View File

@ -25,7 +25,6 @@ var Status = require('../types/types').Status;
var cli = require('../cli');
var Requisition = require('../cli').Requisition;
var CommandAssignment = require('../cli').CommandAssignment;
var fields = require('../fields/fields');
var intro = require('../ui/intro');
var RESOLVED = Promise.resolve(true);
@ -72,7 +71,7 @@ var commandLanguage = exports.commandLanguage = {
constructor: function(terminal) {
this.terminal = terminal;
this.document = terminal.document;
this.focusManager = this.terminal.focusManager;
this.focusManager = terminal.focusManager;
var options = this.terminal.options;
this.requisition = options.requisition;
@ -83,7 +82,7 @@ var commandLanguage = exports.commandLanguage = {
options.environment.window = options.environment.document.defaultView;
}
this.requisition = new Requisition(options);
this.requisition = new Requisition(terminal.system, options);
}
// We also keep track of the last known arg text for the current assignment
@ -217,7 +216,8 @@ var commandLanguage = exports.commandLanguage = {
field.destroy();
}
field = this.terminal.field = fields.getField(this.assignment.param.type, {
var fields = this.terminal.system.fields;
field = this.terminal.field = fields.get(this.assignment.param.type, {
document: this.terminal.document,
requisition: this.requisition
});

View File

@ -31,15 +31,13 @@ exports.items = [
prompt: '>',
constructor: function(terminal) {
this.terminal = terminal;
this.document = this.terminal.document;
this.document = terminal.document;
this.focusManager = terminal.focusManager;
this.updateHints();
},
destroy: function() {
this.terminal = undefined;
this.document = undefined;
},

View File

@ -21,11 +21,6 @@ var Promise = require('../util/promise').Promise;
var RESOLVED = Promise.resolve(true);
/**
* This is where we cache the languages that we know about
*/
var languages = {};
/**
* This is the base implementation for all languages
*/
@ -118,41 +113,49 @@ var baseLanguage = {
}
};
/**
* A manager for the registered Languages
*/
function Languages() {
// This is where we cache the languages that we know about
this._registered = {};
}
/**
* Add a new language to the cache
*/
exports.addLanguage = function(language) {
languages[language.name] = language;
Languages.prototype.add = function(language) {
this._registered[language.name] = language;
};
/**
* Remove an existing language from the cache
*/
exports.removeLanguage = function(language) {
Languages.prototype.remove = function(language) {
var name = typeof language === 'string' ? language : language.name;
delete languages[name];
delete this._registered[name];
};
/**
* Get access to the list of known languages
*/
exports.getLanguages = function() {
return Object.keys(languages).map(function(name) {
return languages[name];
});
Languages.prototype.getAll = function() {
return Object.keys(this._registered).map(function(name) {
return this._registered[name];
}.bind(this));
};
/**
* Find a type, previously registered using #addType()
* Find a previously registered language
*/
exports.createLanguage = function(name, terminal) {
Languages.prototype.createLanguage = function(name, terminal) {
if (name == null) {
name = Object.keys(languages)[0];
name = Object.keys(this._registered)[0];
}
var language = (typeof name === 'string') ? languages[name] : name;
var language = (typeof name === 'string') ? this._registered[name] : name;
if (!language) {
console.error('Known languages: ' + Object.keys(languages).join(', '));
console.error('Known languages: ' + Object.keys(this._registered).join(', '));
throw new Error('Unknown language: \'' + name + '\'');
}
@ -171,3 +174,5 @@ exports.createLanguage = function(name, terminal) {
return Promise.resolve(newInstance);
}
};
exports.Languages = Languages;

View File

@ -64,25 +64,19 @@ function setContentDocument(document) {
* - chromeWindow
* - commandOutputManager (optional)
*/
function FFDisplay(options) {
function FFDisplay(system, options) {
if (options.eval) {
cli.setEvalFunction(options.eval);
}
setContentDocument(options.contentDocument);
this.commandOutputManager = options.commandOutputManager;
if (this.commandOutputManager == null) {
this.commandOutputManager = new CommandOutputManager();
}
this.onOutput = this.commandOutputManager.onOutput;
this.requisition = new Requisition({
this.requisition = new Requisition(system, {
environment: options.environment,
document: options.outputDocument,
commandOutputManager: this.commandOutputManager
document: options.outputDocument
});
this.onOutput = this.requisition.commandOutputManager.onOutput;
this.focusManager = new FocusManager(options.chromeDocument);
this.focusManager = new FocusManager(options.chromeDocument, system.settings);
this.onVisibilityChange = this.focusManager.onVisibilityChange;
this.inputter = new Inputter(options, {
@ -124,7 +118,7 @@ function FFDisplay(options) {
* separate method
*/
FFDisplay.prototype.maybeShowIntro = function() {
intro.maybeShowIntro(this.commandOutputManager,
intro.maybeShowIntro(this.requisition.commandOutputManager,
this.requisition.conversionContext);
};

View File

@ -21,7 +21,6 @@ var host = require('../util/host');
var domtemplate = require('../util/domtemplate');
var CommandAssignment = require('../cli').CommandAssignment;
var fields = require('../fields/fields');
var tooltipHtml =
'<div class="gcli-tt" aria-live="polite">\n' +
@ -141,7 +140,7 @@ Tooltip.prototype.assignmentChanged = function(ev) {
this.field.destroy();
}
this.field = fields.getField(this.assignment.param.type, {
this.field = this.requisition.system.fields.get(this.assignment.param.type, {
document: this.document,
requisition: this.requisition
});

View File

@ -44,15 +44,136 @@ var util = require('./util/util');
var DEVTOOLS_PREFIX = 'devtools.gcli.';
/**
* The type library that we use in creating types for settings
* A manager for the registered Settings
*/
var types;
function Settings(types, settingValues) {
this._types = types;
if (settingValues != null) {
throw new Error('settingValues is not supported when writing to prefs');
}
// Collection of preferences for sorted access
this._settingsAll = [];
// Collection of preferences for fast indexed access
this._settingsMap = new Map();
// Flag so we know if we've read the system preferences
this._hasReadSystem = false;
// Event for use to detect when the list of settings changes
this.onChange = util.createEvent('Settings.onChange');
}
/**
* A class to wrap up the properties of a preference.
* Load system prefs if they've not been loaded already
* @return true
*/
Settings.prototype._readSystem = function() {
if (this._hasReadSystem) {
return;
}
imports.prefBranch.getChildList('').forEach(function(name) {
var setting = new Setting(this, name);
this._settingsAll.push(setting);
this._settingsMap.set(name, setting);
}.bind(this));
this._settingsAll.sort(function(s1, s2) {
return s1.name.localeCompare(s2.name);
}.bind(this));
this._hasReadSystem = true;
};
/**
* Get an array containing all known Settings filtered to match the given
* filter (string) at any point in the name of the setting
*/
Settings.prototype.getAll = function(filter) {
this._readSystem();
if (filter == null) {
return this._settingsAll;
}
return this._settingsAll.filter(function(setting) {
return setting.name.indexOf(filter) !== -1;
}.bind(this));
};
/**
* Add a new setting
* @return The new Setting object
*/
Settings.prototype.add = function(prefSpec) {
var setting = new Setting(this, prefSpec);
if (this._settingsMap.has(setting.name)) {
// Once exists already, we're going to need to replace it in the array
for (var i = 0; i < this._settingsAll.length; i++) {
if (this._settingsAll[i].name === setting.name) {
this._settingsAll[i] = setting;
}
}
}
this._settingsMap.set(setting.name, setting);
this.onChange({ added: setting.name });
return setting;
};
/**
* Getter for an existing setting. Generally use of this function should be
* avoided. Systems that define a setting should export it if they wish it to
* be available to the outside, or not otherwise. Use of this function breaks
* that boundary and also hides dependencies. Acceptable uses include testing
* and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
* @param name The name of the setting to fetch
* @return The found Setting object, or undefined if the setting was not found
*/
Settings.prototype.get = function(name) {
// We might be able to give the answer without needing to read all system
// settings if this is an internal setting
var found = this._settingsMap.get(name);
if (!found) {
found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
}
if (found) {
return found;
}
if (this._hasReadSystem) {
return undefined;
}
else {
this._readSystem();
found = this._settingsMap.get(name);
if (!found) {
found = this._settingsMap.get(DEVTOOLS_PREFIX + name);
}
return found;
}
};
/**
* Remove a setting. A no-op in this case
*/
Settings.prototype.remove = function() {
};
exports.Settings = Settings;
/**
* A class to wrap up the properties of a Setting.
* @see toolkit/components/viewconfig/content/config.js
*/
function Setting(prefSpec) {
function Setting(settings, prefSpec) {
this._settings = settings;
if (typeof prefSpec === 'string') {
// We're coming from getAll() i.e. a full listing of prefs
this.name = prefSpec;
@ -75,6 +196,14 @@ function Setting(prefSpec) {
this.onChange = util.createEvent('Setting.onChange');
}
/**
* Reset this setting to it's initial default value
*/
Setting.prototype.setDefault = function() {
imports.prefBranch.clearUserPref(this.name);
Services.prefs.savePrefFile(null);
};
/**
* What type is this property: boolean/integer/string?
*/
@ -82,13 +211,13 @@ Object.defineProperty(Setting.prototype, 'type', {
get: function() {
switch (imports.prefBranch.getPrefType(this.name)) {
case imports.prefBranch.PREF_BOOL:
return types.createType('boolean');
return this._settings._types.createType('boolean');
case imports.prefBranch.PREF_INT:
return types.createType('number');
return this._settings._types.createType('number');
case imports.prefBranch.PREF_STRING:
return types.createType('string');
return this._settings._types.createType('string');
default:
throw new Error('Unknown type for ' + this.name);
@ -154,154 +283,3 @@ Object.defineProperty(Setting.prototype, 'value', {
enumerable: true
});
/**
* Reset this setting to it's initial default value
*/
Setting.prototype.setDefault = function() {
imports.prefBranch.clearUserPref(this.name);
Services.prefs.savePrefFile(null);
};
/**
* Collection of preferences for sorted access
*/
var settingsAll = [];
/**
* Collection of preferences for fast indexed access
*/
var settingsMap = new Map();
/**
* Flag so we know if we've read the system preferences
*/
var hasReadSystem = false;
/**
* Clear out all preferences and return to initial state
*/
function reset() {
settingsMap = new Map();
settingsAll = [];
hasReadSystem = false;
}
/**
* Reset everything on startup and shutdown because we're doing lazy loading
*/
exports.startup = function(t) {
reset();
types = t;
if (types == null) {
throw new Error('no types');
}
};
exports.shutdown = function() {
reset();
};
/**
* Load system prefs if they've not been loaded already
* @return true
*/
function readSystem() {
if (hasReadSystem) {
return;
}
imports.prefBranch.getChildList('').forEach(function(name) {
var setting = new Setting(name);
settingsAll.push(setting);
settingsMap.set(name, setting);
});
settingsAll.sort(function(s1, s2) {
return s1.name.localeCompare(s2.name);
});
hasReadSystem = true;
}
/**
* Get an array containing all known Settings filtered to match the given
* filter (string) at any point in the name of the setting
*/
exports.getAll = function(filter) {
readSystem();
if (filter == null) {
return settingsAll;
}
return settingsAll.filter(function(setting) {
return setting.name.indexOf(filter) !== -1;
});
};
/**
* Add a new setting.
*/
exports.addSetting = function(prefSpec) {
var setting = new Setting(prefSpec);
if (settingsMap.has(setting.name)) {
// Once exists already, we're going to need to replace it in the array
for (var i = 0; i < settingsAll.length; i++) {
if (settingsAll[i].name === setting.name) {
settingsAll[i] = setting;
}
}
}
settingsMap.set(setting.name, setting);
exports.onChange({ added: setting.name });
return setting;
};
/**
* Getter for an existing setting. Generally use of this function should be
* avoided. Systems that define a setting should export it if they wish it to
* be available to the outside, or not otherwise. Use of this function breaks
* that boundary and also hides dependencies. Acceptable uses include testing
* and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
* @param name The name of the setting to fetch
* @return The found Setting object, or undefined if the setting was not found
*/
exports.getSetting = function(name) {
// We might be able to give the answer without needing to read all system
// settings if this is an internal setting
var found = settingsMap.get(name);
if (!found) {
found = settingsMap.get(DEVTOOLS_PREFIX + name);
}
if (found) {
return found;
}
if (hasReadSystem) {
return undefined;
}
else {
readSystem();
found = settingsMap.get(name);
if (!found) {
found = settingsMap.get(DEVTOOLS_PREFIX + name);
}
return found;
}
};
/**
* Event for use to detect when the list of settings changes
*/
exports.onChange = util.createEvent('Settings.onChange');
/**
* Remove a setting. A no-op in this case
*/
exports.removeSetting = function() { };

View File

@ -72,8 +72,8 @@ exports.items = [
},
lookup: function(context) {
var canon = cli.getMapping(context).requisition.canon;
return exports.getCommandLookup(canon);
var commands = cli.getMapping(context).requisition.system.commands;
return exports.getCommandLookup(commands);
},
parse: function(arg, context) {
@ -98,18 +98,17 @@ exports.getDisplayedParamLookup = function(requisition) {
};
exports.parse = function(context, arg, allowNonExec) {
var canon = cli.getMapping(context).requisition.canon;
var lookup = exports.getCommandLookup(canon);
var commands = cli.getMapping(context).requisition.system.commands;
var lookup = exports.getCommandLookup(commands);
var predictions = exports.findPredictions(arg, lookup);
return exports.convertPredictions(canon, arg, allowNonExec, predictions);
return exports.convertPredictions(commands, arg, allowNonExec, predictions);
};
exports.getCommandLookup = function(canon) {
var commands = canon.getCommands();
commands.sort(function(c1, c2) {
exports.getCommandLookup = function(commands) {
var sorted = commands.getAll().sort(function(c1, c2) {
return c1.name.localeCompare(c2.name);
});
return commands.map(function(command) {
return sorted.map(function(command) {
return { name: command.name, value: command };
});
};
@ -214,8 +213,8 @@ exports.findPredictions = function(arg, lookup) {
return predictions;
};
exports.convertPredictions = function(canon, arg, allowNonExec, predictions) {
var command = canon.getCommand(arg.text);
exports.convertPredictions = function(commands, arg, allowNonExec, predictions) {
var command = commands.get(arg.text);
// Helper function - Commands like 'context' work best with parent
// commands which are not executable. However obviously to execute a
// command, it needs an exec function.

View File

@ -89,7 +89,7 @@ exports.items = [
existing: this.existing,
matches: this.matches
};
var promise = fileparser.parse(arg.text, options);
var promise = fileparser.parse(context, arg.text, options);
return promise.then(function(reply) {
return new Conversion(reply.value, arg, reply.status,

View File

@ -33,22 +33,11 @@ if (typeof document !== 'undefined') {
doc = document;
}
/**
* For testing only.
* The fake empty NodeList used when there are no matches, we replace this with
* something that looks better as soon as we have a document, so not only
* should you not use this, but you shouldn't cache it either.
*/
var emptyNodeList = [];
/**
* Setter for the document that contains the nodes we're matching
*/
exports.setDocument = function(document) {
doc = document;
if (doc != null) {
emptyNodeList = util.createEmptyNodeList(doc);
}
};
/**
@ -56,7 +45,6 @@ exports.setDocument = function(document) {
*/
exports.unsetDocument = function() {
doc = undefined;
emptyNodeList = undefined;
};
/**
@ -186,6 +174,7 @@ exports.items = [
},
getBlank: function(context) {
var emptyNodeList = (doc == null ? [] : util.createEmptyNodeList(doc));
return new Conversion(emptyNodeList, new BlankArgument(), Status.VALID);
},

View File

@ -16,8 +16,6 @@
'use strict';
var settings = require('../settings');
exports.items = [
{
// A type for selecting a known setting
@ -25,12 +23,14 @@ exports.items = [
name: 'setting',
parent: 'selection',
cacheable: true,
constructor: function() {
settings.onChange.add(function(ev) {
this.clearCache();
}, this);
},
lookup: function() {
lookup: function(context) {
var settings = context.system.settings;
if (!this._registeredListener) {
settings.onChange.add(function(ev) {
this.clearCache();
}, this);
this._registeredListener = true;
}
return settings.getAll().map(function(setting) {
return { name: setting.name, value: setting };
});

View File

@ -1064,7 +1064,7 @@ Types.prototype.getTypeNames = function() {
* #getType() is called with a 'name' that matches Type.prototype.name we will
* pass the typeSpec into this constructor.
*/
Types.prototype.addType = function(type) {
Types.prototype.add = function(type) {
if (typeof type === 'object') {
if (!type.name) {
throw new Error('All registered types must have a name');
@ -1099,12 +1099,12 @@ Types.prototype.addType = function(type) {
/**
* Remove a type from the list available to the system
*/
Types.prototype.removeType = function(type) {
Types.prototype.remove = function(type) {
delete this._registered[type.name];
};
/**
* Find a type, previously registered using #addType()
* Find a previously registered type
*/
Types.prototype.createType = function(typeSpec) {
if (typeof typeSpec === 'string') {
@ -1151,8 +1151,3 @@ Types.prototype.createType = function(typeSpec) {
return newType;
};
/**
* Create a central type repository to be used by default
*/
exports.centralTypes = new Types();

View File

@ -18,7 +18,6 @@
var util = require('../util/util');
var l10n = require('../util/l10n');
var settings = require('../settings');
/**
* Record how much help the user wants from the tooltip
@ -59,12 +58,13 @@ exports.items = [
* It does this simply by postponing the hide events by 250ms to see if
* something else takes focus.
*/
function FocusManager(document) {
function FocusManager(document, settings) {
if (document == null) {
throw new Error('document == null');
}
this.document = document;
this.settings = settings;
this.debug = false;
this.blurDelay = 150;
this.window = this.document.defaultView;
@ -84,7 +84,7 @@ function FocusManager(document) {
this.document.addEventListener('focus', this._focused, true);
}
var eagerHelper = settings.getSetting('eagerHelper');
var eagerHelper = this.settings.get('eagerHelper');
eagerHelper.onChange.add(this._eagerHelperChanged, this);
this.isTooltipVisible = undefined;
@ -96,7 +96,7 @@ function FocusManager(document) {
* Avoid memory leaks
*/
FocusManager.prototype.destroy = function() {
var eagerHelper = settings.getSetting('eagerHelper');
var eagerHelper = this.settings.get('eagerHelper');
eagerHelper.onChange.remove(this._eagerHelperChanged, this);
this.document.removeEventListener('focus', this._focused, true);
@ -116,6 +116,7 @@ FocusManager.prototype.destroy = function() {
this._focused = undefined;
this.document = undefined;
this.settings = undefined;
this.window = undefined;
};
@ -355,7 +356,7 @@ FocusManager.prototype._checkShow = function() {
* available inputs
*/
FocusManager.prototype._shouldShowTooltip = function() {
var eagerHelper = settings.getSetting('eagerHelper');
var eagerHelper = this.settings.get('eagerHelper');
if (eagerHelper.value === Eagerness.NEVER) {
return { visible: false, reason: 'eagerHelperNever' };
}

View File

@ -17,7 +17,6 @@
'use strict';
var l10n = require('../util/l10n');
var settings = require('../settings');
var Output = require('../cli').Output;
var view = require('./view');
@ -38,12 +37,12 @@ exports.items = [
* Called when the UI is ready to add a welcome message to the output
*/
exports.maybeShowIntro = function(commandOutputManager, conversionContext) {
var hideIntro = settings.getSetting('hideIntro');
var hideIntro = conversionContext.system.settings.get('hideIntro');
if (hideIntro.value) {
return;
}
var output = new Output();
var output = new Output(conversionContext);
output.type = 'view';
commandOutputManager.onOutput({ output: output });
@ -79,7 +78,8 @@ exports.createView = function(ignoreArgs, conversionContext, showHideButton) {
ondblclick: conversionContext.updateExec,
showHideButton: showHideButton,
onGotIt: function(ev) {
var hideIntro = settings.getSetting('hideIntro');
var settings = conversionContext.system.settings;
var hideIntro = settings.get('hideIntro');
hideIntro.value = true;
this.mainDiv.style.display = 'none';
}

View File

@ -31,7 +31,7 @@ var Status = require('../types/types').Status;
* Helper for the parse() function from the file type.
* See gcli/util/filesystem.js for details
*/
exports.parse = function(typed, options) {
exports.parse = function(context, typed, options) {
return filesystem.stat(typed).then(function(stats) {
// The 'save-as' case - the path should not exist but does
if (options.existing === 'no' && stats.exists) {

View File

@ -18,7 +18,7 @@
var Cc = require('chrome').Cc;
var Ci = require('chrome').Ci;
var URL = require("sdk/url").URL;
var URL = require('sdk/url').URL;
var Task = require('resource://gre/modules/Task.jsm').Task;
@ -61,7 +61,7 @@ exports.Highlighter = Highlighter;
/**
* See docs in lib/gcli/util/host.js
*/
exports.spawn = function(spawnSpec) {
exports.spawn = function(context, spawnSpec) {
throw new Error('Not supported');
};