lots of work fixing bugs with the command line, also removal of useless console.log

This commit is contained in:
Joe Walker 2010-12-11 19:01:02 +00:00
commit 9cff2e0908
10 changed files with 385 additions and 240 deletions

View file

@ -109,10 +109,6 @@ var setupPlugins = function(config, callback) {
}
}
}
console.log(JSON.stringify({
packagePaths: pluginPackageInfo,
paths: paths
}));
require({
packagePaths: pluginPackageInfo,
paths: paths

View file

@ -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;

View file

@ -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);
};
/*

View file

@ -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);

View file

@ -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 {

View file

@ -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 + ' | ';

View 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);
};
});

View file

@ -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);

View file

@ -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);

View file

@ -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);
};