lots of work fixing bugs with the command line, also removal of useless console.log
This commit is contained in:
parent
1e65d71bde
commit
9cff2e0908
10 changed files with 385 additions and 240 deletions
|
|
@ -109,10 +109,6 @@ var setupPlugins = function(config, callback) {
|
|||
}
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify({
|
||||
packagePaths: pluginPackageInfo,
|
||||
paths: paths
|
||||
}));
|
||||
require({
|
||||
packagePaths: pluginPackageInfo,
|
||||
paths: paths
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
* <p>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 = '<strong>' + this.param.name + '</strong>: ';
|
||||
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 += '<strong>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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 + ' | ';
|
||||
|
|
|
|||
66
plugins/cockpit/lib/ui/settings.js
Normal file
66
plugins/cockpit/lib/ui/settings.js
Normal file
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -84,22 +84,15 @@ var setCommandSpec = {
|
|||
'<br/>';
|
||||
});
|
||||
} else {
|
||||
var setting = env.settings.get(args.setting);
|
||||
if (!setting) {
|
||||
request.doneWithError('No setting with the name <strong>' +
|
||||
setting.name + '</strong>.');
|
||||
return;
|
||||
}
|
||||
|
||||
// set with only a setting, shows the value for that setting
|
||||
if (args.value === undefined) {
|
||||
html = '<strong>' + setting.name + '</strong> = ' +
|
||||
setting.get();
|
||||
} else {
|
||||
// Actually change the setting
|
||||
setting.set(args.value);
|
||||
html = 'Setting: <strong>' + setting.name + '</strong> = ' +
|
||||
setting.get();
|
||||
args.setting.set(args.value);
|
||||
html = 'Setting: <strong>' + args.setting.name + '</strong> = ' +
|
||||
args.setting.get();
|
||||
}
|
||||
}
|
||||
request.done(html);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue