diff --git a/demo/boot.js b/demo/boot.js index eec770a1..f2601c37 100644 --- a/demo/boot.js +++ b/demo/boot.js @@ -109,10 +109,6 @@ var setupPlugins = function(config, callback) { } } } -console.log(JSON.stringify({ - packagePaths: pluginPackageInfo, - paths: paths - })); require({ packagePaths: pluginPackageInfo, paths: paths diff --git a/plugins/cockpit/lib/cli.js b/plugins/cockpit/lib/cli.js index 24a25a35..eeeb289a 100644 --- a/plugins/cockpit/lib/cli.js +++ b/plugins/cockpit/lib/cli.js @@ -54,9 +54,10 @@ var canon = require('pilot/canon'); * The information required to tell the user there is a problem with their * input. */ -function Hint(status, message, start, end) { +function Hint(status, message, start, end, predictions) { this.status = status; this.message = message; + this.predictions = predictions; if (typeof start === 'number') { this.start = start; @@ -74,23 +75,21 @@ Hint.prototype = { * Loop over the array of hints finding the one we should display. * @param hints array of hints */ -Hint.worst = function(hints, cursor) { - if (hints.length === 0) { - return undefined; - } +Hint.sort = function(hints, cursor) { // Calculate 'distance from cursor' if (cursor !== undefined) { hints.forEach(function(hint) { - if (cursor < hint.start) { + if (hint.start === Argument.AT_CURSOR) { + hint.distance = 0; + } + else if (cursor < hint.start) { hint.distance = hint.start - cursor; } + else if (cursor > hint.end) { + hint.distance = cursor - hint.end; + } else { - if (cursor > hint.end) { - hint.distance = cursor - hint.end; - } - else { - hint.distance = 0; - } + hint.distance = 0; } }, this); } @@ -112,7 +111,7 @@ Hint.worst = function(hints, cursor) { delete hint.distance; }, this); } - return hints[0]; + return hints; }; exports.Hint = Hint; @@ -157,6 +156,10 @@ Argument.prototype = { this.priorSpace); }, + /** + * See notes on events in Assignment. We might need to hook changes here + * into a CliRequisition so they appear of the command line. + */ setText: function(text) { if (text == null) { throw new Error('Illegal text for Argument: ' + text); @@ -183,6 +186,10 @@ Argument.merge = function(argArray, start, end) { } return joined; }; +/** + * We sometimes need a way to say 'this error occurs whereever the cursor is' + */ +Argument.AT_CURSOR = -1; /** @@ -195,6 +202,13 @@ Argument.merge = function(argArray, start, end) { * Thus, null is a valid default value, and common because it identifies an * parameter that is optional. undefined means there is no value from * the command line. + * + *
TODO: We might need events in the future
+ * The current hope is that a GUI and CLI that share an Assignment can be in
+ * direct connection due to assignment.getValue calling arg.setText, however
+ * we might need to use events if not.
+ * oop.implement(Assignment, EventEmitter);
+ *
* @constructor
*/
function Assignment(param) {
@@ -208,6 +222,22 @@ Assignment.prototype = {
*/
param: undefined,
+ /**
+ * Report on the status of the last parse() conversion.
+ * @see types.Conversion
+ */
+ conversion: undefined,
+
+ /**
+ * The current value in a type as specified by param.type
+ */
+ value: undefined,
+
+ /**
+ * The string version of the current value
+ */
+ arg: undefined,
+
/**
* The current value (i.e. not the string representation)
* Use setValue() to mutate
@@ -217,8 +247,10 @@ Assignment.prototype = {
if (this.value === value) {
return;
}
+
if (value === undefined) {
value = this.param.defaultValue;
+ this.arg = undefined;
}
this.value = value;
@@ -228,7 +260,6 @@ Assignment.prototype = {
}
this.conversion = undefined;
- //this._dispatchEvent('change', { assignment: this });
},
/**
@@ -242,40 +273,60 @@ Assignment.prototype = {
}
this.arg = arg;
this.conversion = this.param.type.parse(arg.text);
+ this.conversion.arg = arg; // TODO: make this automatic?
this.value = this.conversion.value;
- //this._dispatchEvent('change', { assignment: this });
},
/**
- * Create a list of this hints associated with this parameter assignment
+ * Create a list of the hints associated with this parameter assignment.
+ * Generally there will be only one hint generated because we're currently
+ * only displaying one hint at a time, ordering by distance from cursor
+ * and severity. Since distance from cursor will be the same for all hints
+ * from this assignment all but the most severe will ever be used. It might
+ * make sense with more experience to alter this to function to be getHint()
*/
getHints: function() {
var hints = [];
- if (this.conversion != null &&
- (this.conversion.status !== Status.VALID ||
- this.conversion.message)) {
- hints.push(new ConversionHint(this.conversion, this.arg));
+
+ // If there is no argument, use the cursor position
+ var message = '' + this.param.name + ': ';
+ if (this.param.description) {
+ // TODO: This should be a short description - do we need to trim?
+ message += this.param.description.trim();
+
+ // Ensure the help text ends with '. '
+ if (message.charAt(message.length - 1) !== '.') {
+ message += '.';
+ }
+ if (message.charAt(message.length - 1) !== ' ') {
+ message += ' ';
+ }
+ }
+ var status = Status.VALID;
+ var start = this.arg ? this.arg.start : Argument.AT_CURSOR;
+ var end = this.arg ? this.arg.end : Argument.AT_CURSOR;
+ var predictions;
+
+ // Non-valid conversions will have useful information to pass on
+ if (this.conversion) {
+ status = this.conversion.status;
+ if (this.conversion.message) {
+ message += this.conversion.message;
+ }
+ predictions = this.conversion.predictions;
}
- var argProvided = this.arg != null && this.arg.text !== '';
+ // Hint if the param is required, but not provided
+ var argProvided = this.arg && this.arg.text !== '';
var dataProvided = this.value !== undefined || argProvided;
-
if (this.param.defaultValue === undefined && !dataProvided) {
- // If the there is no data provided, we have no start/end. Use -1
- hints.push(new Hint(Status.INVALID,
- 'Argument for ' + param.name + ' is required'
- -1, -1));
+ status = Status.INVALID;
+ message += 'Required<\strong>';
}
- return hints;
- },
- /**
- * Report on the status of the last parse() conversion.
- * @see types.Conversion
- */
- conversion: undefined
+ return [ new Hint(status, message, start, end, predictions) ];
+ }
};
-oop.implement(Assignment, EventEmitter);
exports.Assignment = Assignment;
@@ -355,15 +406,19 @@ Requisition.prototype = {
},
/**
- * Collect the statuses from the Assignments
+ * Collect the statuses from the Assignments.
+ * The hints returned are sorted by severity
*/
getHints: function() {
var hints = [];
Object.keys(this._assignments).map(function(name) {
- // Append the assignments hints to our list
- hints.push.apply(hints, this._assignments[name].getHints());
+ // Only use assignments with an argument
+ var assignment = this._assignments[name];
+ if (assignment.arg) {
+ hints.push.apply(hints, assignment.getHints());
+ }
}, this);
- return hints;
+ return Hint.sort(hints);
},
/**
@@ -437,7 +492,9 @@ oop.inherits(CliRequisition, Requisition);
*
*/
CliRequisition.prototype.update = function(input) {
- this.hints = [];
+ // TODO: We only store this so getHints can work. Find a better way.
+ this.input = input;
+ this.localHints = [];
if (util.none(input.typed)) {
this.setCommand(null);
@@ -450,38 +507,44 @@ oop.inherits(CliRequisition, Requisition);
// a complete novice a 'type help' message is very annoying, so we
// need to find a way to only display this message once, or for
// until the user click a 'close' button or similar
- this._addHint(Status.INCOMPLETE, '', 0, 0);
+ this.localHints.push(new Hint(Status.INCOMPLETE, '', 0, 0));
this.setCommand(null);
return;
}
- var command = _split(args);
- if (!command) {
+ var conversion = _split(args);
+ if (!conversion.value) {
// No command found - bail helpfully.
- var commandType = types.getType('command');
- var conversion = commandType.parse(input.typed);
- var arg = Argument.merge(args);
- this._addHint(new ConversionHint(conversion, arg));
-
+ this.localHints.push(new ConversionHint(conversion, conversion.arg));
this.setCommand(null);
}
else {
- // The user hasn't started to type any arguments
- if (args.length === 0) {
- var message = documentCommand(command);
- this._addHint(Status.VALID, message, 0, input.typed.length);
- }
+ var message = documentCommand(conversion.value);
+ this.localHints.push(new Hint(Status.VALID, message, conversion.arg));
- this.setCommand(command);
+ this.setCommand(conversion.value);
this._assign(args);
- this._addHint(CliRequisition.super_.getHints.call(this));
}
+ return;
+ };
+
+ CliRequisition.prototype.getHints = function() {
+ var hints = this.localHints.slice(0);
+
+ Object.keys(this._assignments).map(function(name) {
+ // Only use assignments with an argument
+ var assignment = this._assignments[name];
+ if (assignment.arg) {
+ hints.push.apply(hints, assignment.getHints());
+ }
+ }, this);
+
// Not knowing about cursor positioning, the requisition and assignments
// can't know this, but anything they mark as INCOMPLETE is actually
// INVALID unless the cursor is actually inside that argument.
- var c = input.cursor;
- this.hints.forEach(function(hint) {
+ var c = this.input.cursor;
+ hints.forEach(function(hint) {
var startInHint = c.start >= hint.start && c.start <= hint.end;
var endInHint = c.end >= hint.start && c.end <= hint.end;
var inHint = startInHint || endInHint;
@@ -490,30 +553,7 @@ oop.inherits(CliRequisition, Requisition);
}
}, this);
- return;
- };
-
- CliRequisition.prototype.getHints = function() {
- return this.hints;
- };
-
- /**
- * Some sugar around: 'this.hints.push(new Hint(...));', but you can also
- * pass in an array of Hints or the parameters to create a hint
- */
- CliRequisition.prototype._addHint = function(status, message, start, end) {
- if (status == null) {
- return;
- }
- if (status instanceof Hint) {
- this.hints.push(status);
- }
- else if (Array.isArray(status)) {
- this.hints.push.apply(this.hints, status);
- }
- else {
- this.hints.push(new Hint(status, message, start, end));
- }
+ return Hint.sort(hints, this.input.cursor.start);
};
/**
@@ -538,9 +578,9 @@ oop.inherits(CliRequisition, Requisition);
// TODO: previously we were doing some extra work to avoid this if
// we determined that we had args that were all whitespace, but
// probably given our tighter tokenize() this won't be an issue?
- this._addHint(Status.INVALID,
- this.command.name + ' does not take any parameters',
- Argument.merge(args));
+ this.localHints.push(new Hint(Status.INVALID,
+ this.command.name + ' does not take any parameters',
+ Argument.merge(args)));
return;
}
@@ -580,9 +620,9 @@ oop.inherits(CliRequisition, Requisition);
else {
if (i + 1 < args.length) {
// Missing value portion of this named param
- this._addHint(Status.INCOMPLETE,
- 'Missing value for: ' + namedArgText,
- args[i]);
+ this.localHints.push(new Hint(Status.INCOMPLETE,
+ 'Missing value for: ' + namedArgText,
+ args[i]));
}
else {
args.splice(i + 1, 1);
@@ -612,9 +652,9 @@ oop.inherits(CliRequisition, Requisition);
if (args.length > 0) {
var remaining = Argument.merge(args);
- this._addHint(Status.INVALID,
- 'Input \'' + remaining.text + '\' makes no sense.',
- remaining);
+ this.localHints.push(new Hint(Status.INVALID,
+ 'Input \'' + remaining.text + '\' makes no sense.',
+ remaining));
}
};
@@ -671,63 +711,71 @@ function _tokenize(typed) {
var str = unescape(typed.substring(start, i));
args.push(new Argument(str, start, i, priorSpace));
}
+ else {
+ if (i !== start) {
+ // There's a bunch of whitespace at the end of the command
+ // treat it as another argument without any content
+ priorSpace = typed.substring(start, i);
+ args.push(new Argument('', i, i, priorSpace));
+ }
+ }
break;
}
var c = typed[i];
switch (mode) {
- case OUTSIDE:
- if (c === '\'') {
- priorSpace = typed.substring(start, i);
- mode = IN_SINGLE_Q;
- start = i + 1;
- }
- else if (c === '"') {
- priorSpace = typed.substring(start, i);
- mode = IN_DOUBLE_Q;
- start = i + 1;
- }
- else if (/ /.test(c)) {
- // Still whitespace, do nothing
- }
- else {
- priorSpace = typed.substring(start, i);
- mode = IN_SIMPLE;
- start = i;
- }
- break;
+ case OUTSIDE:
+ if (c === '\'') {
+ priorSpace = typed.substring(start, i);
+ mode = IN_SINGLE_Q;
+ start = i + 1;
+ }
+ else if (c === '"') {
+ priorSpace = typed.substring(start, i);
+ mode = IN_DOUBLE_Q;
+ start = i + 1;
+ }
+ else if (/ /.test(c)) {
+ // Still whitespace, do nothing
+ }
+ else {
+ priorSpace = typed.substring(start, i);
+ mode = IN_SIMPLE;
+ start = i;
+ }
+ break;
- case IN_SIMPLE:
- // There is an edge case of xx'xx which we are assuming to be
- // a single parameter (and same with ")
- if (c === ' ') {
- var str = unescape(typed.substring(start, i));
- args.push(new Argument(str, start, i, priorSpace));
- mode = OUTSIDE;
- start = i;
- priorSpace = '';
- }
- break;
+ case IN_SIMPLE:
+ // There is an edge case of xx'xx which we are assuming to be
+ // a single parameter (and same with ")
+ if (c === ' ') {
+ var str = unescape(typed.substring(start, i));
+ args.push(new Argument(str, start, i, priorSpace));
+ mode = OUTSIDE;
+ start = i;
+ priorSpace = '';
+ }
+ break;
- case IN_SINGLE_Q:
- if (c === '\'') {
- var str = unescape(typed.substring(start, i));
- args.push(new Argument(str, start, i, priorSpace));
- mode = OUTSIDE;
- start = i + 1;
- priorSpace = '';
- }
- break;
+ case IN_SINGLE_Q:
+ if (c === '\'') {
+ var str = unescape(typed.substring(start, i));
+ args.push(new Argument(str, start, i, priorSpace));
+ mode = OUTSIDE;
+ start = i + 1;
+ priorSpace = '';
+ }
+ break;
- case IN_DOUBLE_Q:
- if (c === '"') {
- var str = unescape(typed.substring(start, i));
- args.push(new Argument(str, start, i, priorSpace));
- mode = OUTSIDE;
- start = i + 1;
- priorSpace = '';
- }
- break;
+ case IN_DOUBLE_Q:
+ if (c === '"') {
+ var str = unescape(typed.substring(start, i));
+ args.push(new Argument(str, start, i, priorSpace));
+ mode = OUTSIDE;
+ start = i + 1;
+ priorSpace = '';
+ }
+ break;
}
i++;
@@ -742,24 +790,23 @@ exports._tokenize = _tokenize;
* typed at the command line.
*/
function _split(args) {
+ var commandType = types.getType('command');
if (args.length === 0) {
- return undefined;
+ return commandType.parse('');
}
- var argsUsed = 0;
- var lookup = '';
- var command;
+ var argsUsed = 1;
+ var arg;
+ var conversion;
- while (true) {
- argsUsed++;
- lookup += args.map(function(arg) {
- return arg.text;
- }).slice(0, argsUsed).join(' ');
- command = canon.getCommand(lookup);
+ while (argsUsed <= args.length) {
+ var arg = Argument.merge(args, 0, argsUsed);
+ conversion = commandType.parse(arg.text);
+ conversion.arg = arg; // TODO: make this automatic?
- if (!command) {
- // Not found. break with command == null
- return undefined;
+ if (!conversion.value) {
+ // Not found. break with value == null
+ break;
}
/*
@@ -772,21 +819,18 @@ function _split(args) {
}
*/
- if (command.exec) {
+ if (conversion.value.exec) {
// Valid command, break with command valid
+ for (var i = 0; i < argsUsed; i++) {
+ args.shift();
+ }
break;
}
- // command, but no exec - this must be a sub-command
- lookup += ' ';
+ argsUsed++;
}
- // Remove the used args
- for (var i = 0; i < argsUsed; i++) {
- args.shift();
- }
-
- return command;
+ return conversion;
}
exports._split = _split;
diff --git a/plugins/cockpit/lib/index.js b/plugins/cockpit/lib/index.js
index beacc28f..7717e118 100644
--- a/plugins/cockpit/lib/index.js
+++ b/plugins/cockpit/lib/index.js
@@ -41,9 +41,8 @@ exports.startup = function(data, reason) {
window.testCli = require('cockpit/test/testCli');
- var plain = require('cockpit/ui/plain');
- plain.startup(data, reason);
-
+ require('cockpit/ui/settings').startup(data, reason);
+ require('cockpit/ui/plain').startup(data, reason);
};
/*
diff --git a/plugins/cockpit/lib/test/testCli.js b/plugins/cockpit/lib/test/testCli.js
index bace956e..20b0cdd0 100644
--- a/plugins/cockpit/lib/test/testCli.js
+++ b/plugins/cockpit/lib/test/testCli.js
@@ -125,19 +125,19 @@ exports.testTokenize = function() {
exports.testSplit = function() {
var args = tokenize('s');
- var command = split(args);
+ var conversion = split(args);
test.verifyEqual(1, args.length);
test.verifyEqual('s', args[0].text);
- test.verifyUndefined(command);
+ test.verifyNull(conversion.value);
var args = tokenize('set');
- var command = split(args);
+ var conversion = split(args);
test.verifyEqual([], args);
- test.verifyEqual('set', command.name);
+ test.verifyEqual('set', conversion.value.name);
var args = tokenize('set a b');
- var command = split(args);
- test.verifyEqual('set', command.name);
+ var conversion = split(args);
+ test.verifyEqual('set', conversion.value.name);
test.verifyEqual(2, args.length);
test.verifyEqual('a', args[0].text);
test.verifyEqual('b', args[1].text);
@@ -152,14 +152,26 @@ exports.testCli = function() {
var settingAssignment;
var valueAssignment;
var cli = new CliRequisition();
+ var debug = true;
function update(input) {
+ if (debug) {
+ console.log('####### TEST: typed="' + input.typed + '" cursor:', input.cursor);
+ }
cli.update(input);
hints = cli.getHints();
hint0 = (hints.length !== 0) ? hints[0] : undefined;
+ if (debug) {
+ console.log('cli=', cli);
+ console.log('hints=', hints);
+ }
if (cli.command && cli.command.name === 'set') {
settingAssignment = cli.getAssignment('setting');
valueAssignment = cli.getAssignment('value');
+ if (debug) {
+ console.log('settingAssignment=', settingAssignment);
+ console.log('valueAssignment=', valueAssignment);
+ }
}
else {
settingAssignment = undefined;
@@ -167,6 +179,12 @@ exports.testCli = function() {
}
}
+ function verifyPredictionsContains(name, predictions) {
+ return predictions.every(function(prediction) {
+ return name === prediction || name === prediction.name;
+ }, this);
+ }
+
var historyLengthSetting = settings.getSetting('historyLength');
update({ typed: '', cursor: { start: 0, end: 0 } });
@@ -184,8 +202,9 @@ exports.testCli = function() {
test.verifyEqual(1, hint0.end);
test.verifyTrue(hint0.predictions.length > 0);
// This is slightly fragile because it depends on the configuration
+ // TODO: Mock, but first we need a way to have a clear canon.
test.verifyTrue(hint0.predictions.length < 20);
- test.verifyNotEqual(-1, hint0.predictions.indexOf('set'));
+ verifyPredictionsContains('set', hint0.predictions);
test.verifyNull(cli.command);
update({ typed: 'set', cursor: { start: 3, end: 3 } });
@@ -196,73 +215,79 @@ exports.testCli = function() {
test.verifyEqual('set', cli.command.name);
update({ typed: 'set ', cursor: { start: 4, end: 4 } });
- test.verifyEqual(1, hints.length);
+ test.verifyEqual(2, hints.length);
test.verifyEqual(Status.VALID, hint0.status);
- test.verifyEqual(0, hint0.start);
- // Technically the command ends at 3, but we're returning 4 currently.
- // This is caused by us using the whole input to determine the length.
- // Maybe one day we should fix this?
- //test.verifyEqual(3, hint0.end);
+ test.verifyEqual(4, hint0.start);
+ test.verifyEqual(4, hint0.end);
+ test.verifyEqual(Status.VALID, hints[1].status);
+ test.verifyEqual(0, hints[1].start);
+ test.verifyEqual(3, hints[1].end);
test.verifyEqual('set', cli.command.name);
update({ typed: 'set h', cursor: { start: 5, end: 5 } });
- test.verifyEqual(1, hints.length);
+ test.verifyEqual(2, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
- test.verifyTrue(hint0.predictions.length > 0);
test.verifyEqual(4, hint0.start);
test.verifyEqual(5, hint0.end);
- test.verifyNotEqual(-1, hint0.predictions.indexOf('historyLength'));
+ test.verifyTrue(hint0.predictions.length > 0);
+ verifyPredictionsContains('historyLength', hint0.predictions);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('h', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt', cursor: { start: 16, end: 16 } });
- test.verifyEqual(1, hints.length);
+ test.verifyEqual(2, hints.length);
test.verifyEqual(Status.INCOMPLETE, hint0.status);
- test.verifyEqual(1, hint0.predictions.length);
test.verifyEqual(4, hint0.start);
test.verifyEqual(16, hint0.end);
- test.verifyEqual('historyLength', hint0.predictions[0]);
+ test.verifyEqual(1, hint0.predictions.length);
+ verifyPredictionsContains('historyLength', hint0.predictions);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt', cursor: { start: 1, end: 1 } });
- test.verifyEqual(1, hints.length);
- test.verifyEqual(Status.INVALID, hint0.status);
- test.verifyEqual(4, hint0.start);
- test.verifyEqual(16, hint0.end);
- test.verifyEqual(1, hint0.predictions.length);
- test.verifyEqual('historyLength', hint0.predictions[0]);
+ test.verifyEqual(2, hints.length);
+ test.verifyEqual(Status.VALID, hint0.status);
+ test.verifyEqual(0, hint0.start);
+ test.verifyEqual(3, hint0.end);
+ test.verifyEqual(Status.INVALID, hints[1].status);
+ test.verifyEqual(4, hints[1].start);
+ test.verifyEqual(16, hints[1].end);
+ test.verifyEqual(1, hints[1].predictions.length);
+ verifyPredictionsContains('historyLength', hints[1].predictions);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLengt ', cursor: { start: 17, end: 17 } });
- test.verifyEqual(1, hints.length);
- test.verifyEqual(Status.INVALID, hint0.status);
- test.verifyEqual(4, hint0.start);
- test.verifyEqual(16, hint0.end);
- test.verifyEqual(1, hint0.predictions.length);
- test.verifyEqual('historyLength', hint0.predictions[0]);
+ test.verifyEqual(3, hints.length);
+ test.verifyEqual(Status.VALID, hint0.status);
+ test.verifyEqual(17, hint0.start);
+ test.verifyEqual(17, hint0.end);
+ test.verifyEqual(Status.INVALID, hints[1].status);
+ test.verifyEqual(4, hints[1].start);
+ test.verifyEqual(16, hints[1].end);
+ test.verifyEqual(1, hints[1].predictions.length);
+ verifyPredictionsContains('historyLength', hints[1].predictions);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLengt', settingAssignment.arg.text);
test.verifyEqual(undefined, settingAssignment.value);
update({ typed: 'set historyLength', cursor: { start: 17, end: 17 } });
- test.verifyEqual(0, hints.length);
+ test.verifyEqual(2, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
update({ typed: 'set historyLength ', cursor: { start: 18, end: 18 } });
- test.verifyEqual(0, hints.length);
+ test.verifyEqual(3, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
update({ typed: 'set historyLength 6', cursor: { start: 19, end: 19 } });
- test.verifyEqual(0, hints.length);
+ test.verifyEqual(3, hints.length);
test.verifyEqual('set', cli.command.name);
test.verifyEqual('historyLength', settingAssignment.arg.text);
test.verifyEqual(historyLengthSetting, settingAssignment.value);
diff --git a/plugins/cockpit/lib/ui/plain.css b/plugins/cockpit/lib/ui/plain.css
index d6a59f54..2d72c6f0 100644
--- a/plugins/cockpit/lib/ui/plain.css
+++ b/plugins/cockpit/lib/ui/plain.css
@@ -19,13 +19,13 @@
padding: 8px;
}
-.cptCompletion.VALID { background: rgba(210, 255, 210, 1); }
-.cptCompletion.INCOMPLETE { background: rgba(255, 230, 210, 1); }
-.cptCompletion.INVALID { background: rgba(255, 210, 210, 1); }
+.cptCompletion.VALID { background: #FFF; }
+.cptCompletion.INCOMPLETE { background: #DDD; }
+.cptCompletion.INVALID { background: #DDD; }
.cptCompletion span { color: #FFF; }
-.cptCompletion span.INCOMPLETE { text-shadow: 0px 0px 2px #ff8800; }
-.cptCompletion span.INVALID { text-shadow: 0px 0px 5px #ff0000; }
+.cptCompletion span.INCOMPLETE { text-shadow: 0px 0px 2px #F80; }
+.cptCompletion span.INVALID { text-shadow: 0px 0px 5px #F00; }
#cockpit, .cptCompletion {
diff --git a/plugins/cockpit/lib/ui/plain.js b/plugins/cockpit/lib/ui/plain.js
index f6953bf5..9df2bfb4 100644
--- a/plugins/cockpit/lib/ui/plain.js
+++ b/plugins/cockpit/lib/ui/plain.js
@@ -59,9 +59,10 @@ var Status = require('pilot/types').Status;
* 2. Attach a set of events so the command line works
*/
exports.startup = function(data, reason) {
+ // TODO: We should probably cut this up into an object
+ var settings = data.env.settings;
var doc = document;
var win = doc.defaultView;
-
var cli = new CliRequisition();
// TODO: we should have a better way to specify command lines???
@@ -71,7 +72,7 @@ exports.startup = function(data, reason) {
return;
}
- var templates = doc.createElement('dic');
+ var templates = doc.createElement('div');
templates.innerHTML = plainRow;
var row = templates.firstChild;
@@ -88,12 +89,22 @@ exports.startup = function(data, reason) {
input.parentNode.insertBefore(output, input);
function resizer() {
- var style = win.getComputedStyle(input, null);
+ var top, height, left, width;
- var top = parseInt(style.getPropertyValue('top'), 10);
- var height = parseInt(style.getPropertyValue('height'), 10);
- var left = parseInt(style.getPropertyValue('left'), 10);
- var width = parseInt(style.getPropertyValue('width'), 10);
+ if (input.getClientRects) {
+ var rect = input.getClientRects()[0];
+ top = rect.top;
+ height = rect.height;
+ left = rect.left;
+ width = rect.width;
+ }
+ else {
+ var style = win.getComputedStyle(input, null);
+ top = parseInt(style.getPropertyValue('top'), 10);
+ height = parseInt(style.getPropertyValue('height'), 10);
+ left = parseInt(style.getPropertyValue('left'), 10);
+ width = parseInt(style.getPropertyValue('width'), 10);
+ }
completer.style.top = top + 'px';
completer.style.height = height + 'px';
@@ -125,6 +136,20 @@ exports.startup = function(data, reason) {
}, this);
}.bind(this));
+ var showHint = settings.getSetting('showHint');
+ function hintShower() {
+ if (showHint.get()) {
+ hinter.style.display = 'block';
+ }
+ else {
+ hinter.style.display = 'none';
+ }
+ }
+ hintShower();
+ showHint.addEventListener('change', hintShower.bind(this));
+
+ var outputHeight = settings.getSetting('outputHeight');
+
/*
// All this does is to kill TABs normal use. I wonder if we can train
// people to use right arrow? Probably not? but ...
@@ -210,10 +235,10 @@ exports.startup = function(data, reason) {
}
}
- worst = Hint.worst(hints) || NO_HINT;
+ worst = (hints.length > 0) ? hints[0] : NO_HINT;
var message = worst.message;
if (worst.predictions && worst.predictions.length > 0) {
- message += ' [ ';
+ message += ': [ ';
worst.predictions.forEach(function(prediction) {
if (prediction.name) {
message += prediction.name + ' | ';
diff --git a/plugins/cockpit/lib/ui/settings.js b/plugins/cockpit/lib/ui/settings.js
new file mode 100644
index 00000000..80d95e73
--- /dev/null
+++ b/plugins/cockpit/lib/ui/settings.js
@@ -0,0 +1,66 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+
+
+var showHintSetting = {
+ name: "showHint",
+ description: "Do we display hints while we type?",
+ type: "bool",
+ defaultValue: true
+};
+
+var outputHeightSetting = {
+ name: "outputHeight",
+ description: "What height should the output panel be?",
+ type: "number",
+ defaultValue: 300
+};
+
+exports.startup = function(data, reason) {
+ data.env.settings.addSetting(showHintSetting);
+ data.env.settings.addSetting(outputHeightSetting);
+};
+
+exports.shutdown = function(data, reason) {
+ data.env.settings.removeSetting(showHintSetting);
+ data.env.settings.removeSetting(outputHeightSetting);
+};
+
+
+});
diff --git a/plugins/pilot/lib/commands/settings.js b/plugins/pilot/lib/commands/settings.js
index efcbc5ea..c6679bc9 100644
--- a/plugins/pilot/lib/commands/settings.js
+++ b/plugins/pilot/lib/commands/settings.js
@@ -84,22 +84,15 @@ var setCommandSpec = {
'
';
});
} else {
- var setting = env.settings.get(args.setting);
- if (!setting) {
- request.doneWithError('No setting with the name ' +
- setting.name + '.');
- return;
- }
-
// set with only a setting, shows the value for that setting
if (args.value === undefined) {
html = '' + setting.name + ' = ' +
setting.get();
} else {
// Actually change the setting
- setting.set(args.value);
- html = 'Setting: ' + setting.name + ' = ' +
- setting.get();
+ args.setting.set(args.value);
+ html = 'Setting: ' + args.setting.name + ' = ' +
+ args.setting.get();
}
}
request.done(html);
diff --git a/plugins/pilot/lib/index.js b/plugins/pilot/lib/index.js
index 73262d42..f576b29c 100644
--- a/plugins/pilot/lib/index.js
+++ b/plugins/pilot/lib/index.js
@@ -50,10 +50,8 @@ packages.unshift("require", "exports", "module");
define(packages, function(require, exports, module) {
-console.log(packages);
exports.startup = function(data, reason) {
deps.forEach(function(dep) {
- console.log("test startup for " + dep);
var module = require(dep);
if (typeof module.startup === "function") {
module.startup(data, reason);
diff --git a/plugins/pilot/lib/types/basic.js b/plugins/pilot/lib/types/basic.js
index f6e2adc5..6eeb64a8 100644
--- a/plugins/pilot/lib/types/basic.js
+++ b/plugins/pilot/lib/types/basic.js
@@ -128,32 +128,32 @@ SelectionType.prototype.parse = function(str) {
}
var data = (typeof(this.data) === "function") ? this.data() : this.data;
- var match;
+ // The matchedValue could be the boolean value false
+ var hasMatched = false;
+ var matchedValue;
var completions = [];
data.forEach(function(option) {
if (str == option) {
- match = this.fromString(option);
+ matchedValue = this.fromString(option);
+ hasMatched = true;
}
else if (option.indexOf(str) === 0) {
completions.push(this.fromString(option));
}
}, this);
- if (match) {
- return new Conversion(match);
+ if (hasMatched) {
+ return new Conversion(matchedValue);
}
else {
if (completions.length > 0) {
- return new Conversion(null,
- Status.INCOMPLETE,
- 'Several possibilities for \'' + str + '\'',
- completions);
+ var msg = 'Several possibilities' +
+ (str.length === 0 ? '' : ' for \'' + str + '\'');
+ return new Conversion(null, Status.INCOMPLETE, msg, completions);
}
else {
- return new Conversion(null,
- Status.INVALID,
- 'Can\'t use \'' + str + '\'.',
- completions);
+ var msg = 'Can\'t use \'' + str + '\'.';
+ return new Conversion(null, Status.INVALID, msg, completions);
}
}
};
@@ -203,7 +203,6 @@ DeferredType.prototype.stringify = function(value) {
};
DeferredType.prototype.parse = function(value) {
- console.log(this.defer);
return this.defer().parse(value);
};