From e9fd9637242d304e679e4026e54000692f8b1e8a Mon Sep 17 00:00:00 2001 From: Joe Walker Date: Wed, 15 Dec 2010 13:38:02 +0000 Subject: [PATCH] adding the convept of next/previous to values in the command line --- plugins/cockpit/lib/cli.js | 22 +++ plugins/cockpit/lib/test/testCli.js | 251 +++++++++++++++++----------- plugins/cockpit/lib/ui/cliView.js | 73 ++++---- plugins/pilot/lib/canon.js | 23 ++- plugins/pilot/lib/settings.js | 15 +- plugins/pilot/lib/types.js | 18 +- plugins/pilot/lib/types/basic.js | 52 +++++- 7 files changed, 310 insertions(+), 144 deletions(-) diff --git a/plugins/cockpit/lib/cli.js b/plugins/cockpit/lib/cli.js index a9d5acc6..780408c2 100644 --- a/plugins/cockpit/lib/cli.js +++ b/plugins/cockpit/lib/cli.js @@ -363,6 +363,28 @@ Assignment.prototype = { this.conversion.predictions.length > 0) { this.setValue(this.conversion.predictions[0]); } + }, + + /** + * Replace the current value with the lower value if such a concept + * exists. + */ + decrement: function() { + var replacement = this.param.type.decrement(this.value); + if (replacement != null) { + this.setValue(replacement); + } + }, + + /** + * Replace the current value with the higher value if such a concept + * exists. + */ + increment: function() { + var replacement = this.param.type.increment(this.value); + if (replacement != null) { + this.setValue(replacement); + } } }; exports.Assignment = Assignment; diff --git a/plugins/cockpit/lib/test/testCli.js b/plugins/cockpit/lib/test/testCli.js index 1d40520a..809df6b1 100644 --- a/plugins/cockpit/lib/test/testCli.js +++ b/plugins/cockpit/lib/test/testCli.js @@ -54,9 +54,10 @@ exports.testAll = function() { }; exports.testTokenize = function() { + var args; var cli = new CliRequisition(); - var args = cli._tokenize(''); + args = cli._tokenize(''); test.verifyEqual(0, args.length); args = cli._tokenize('s'); @@ -66,6 +67,13 @@ exports.testTokenize = function() { test.verifyEqual(1, args[0].end); test.verifyEqual('', args[0].priorSpace); + args = cli._tokenize(' '); + test.verifyEqual(1, args.length); + test.verifyEqual('', args[0].text); + test.verifyEqual(1, args[0].start); + test.verifyEqual(1, args[0].end); + test.verifyEqual(' ', args[0].priorSpace); + args = cli._tokenize('s s'); test.verifyEqual(2, args.length); test.verifyEqual('s', args[0].text); @@ -126,22 +134,23 @@ exports.testTokenize = function() { }; exports.testSplit = function() { + var args; var cli = new CliRequisition(); - var args = cli._tokenize('s'); - var conversion = cli._split(args); + args = cli._tokenize('s'); + cli._split(args); test.verifyEqual(1, args.length); test.verifyEqual('s', args[0].text); - test.verifyNull(conversion.value); + test.verifyNull(cli.commandAssignment.value); - var args = cli._tokenize('set'); - var conversion = cli._split(args); + args = cli._tokenize('set'); + cli._split(args); test.verifyEqual([], args); - test.verifyEqual('set', conversion.value.name); + test.verifyEqual('set', cli.commandAssignment.value.name); - var args = cli._tokenize('set a b'); - var conversion = cli._split(args); - test.verifyEqual('set', conversion.value.name); + args = cli._tokenize('set a b'); + cli._split(args); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual(2, args.length); test.verifyEqual('a', args[0].text); test.verifyEqual('b', args[1].text); @@ -151,31 +160,32 @@ exports.testSplit = function() { }; exports.testCli = function() { - var hints; - var hint0; var settingAssignment; var valueAssignment; var cli = new CliRequisition(); var debug = true; + var worst; + var display; + var statuses; 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); + console.log('####### TEST: typed="' + input.typed + + '" cur=' + input.cursor.start + + ' cli=', cli); } - if (cli.command && cli.command.name === 'set') { + + worst = cli.getWorstHint(); + display = cli.getAssignmentAt(input.cursor.start).getHint(); + statuses = cli.getInputStatusMarkup().map(function(status) { + return status.valueOf(); + }).join(''); + + if (cli.commandAssignment.value && cli.commandAssignment.value.name === 'set') { settingAssignment = cli.getAssignment('setting'); valueAssignment = cli.getAssignment('value'); - if (debug) { - console.log('settingAssignment=', settingAssignment); - console.log('valueAssignment=', valueAssignment); - } } else { settingAssignment = undefined; @@ -191,108 +201,145 @@ exports.testCli = function() { var historyLengthSetting = settings.getSetting('historyLength'); - update({ typed: '', cursor: { start: 0, end: 0 } }); - test.verifyEqual(1, hints.length); - test.verifyEqual(Status.INCOMPLETE, hint0.status); - test.verifyEqual(0, hint0.start); - test.verifyEqual(0, hint0.end); - test.verifyNull(cli.command); + update({ typed: '', cursor: { start: 0, end: 0 } }); + test.verifyEqual('', statuses); + test.verifyEqual(1, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(0, display.start); + test.verifyEqual(0, display.end); + test.verifyEqual(display, worst); + test.verifyNull(cli.commandAssignment.value); - update({ typed: 's', cursor: { start: 1, end: 1 } }); - test.verifyEqual(1, hints.length); - test.verifyEqual(Status.INCOMPLETE, hint0.status); - test.verifyNotEqual(-1, hint0.message.indexOf('possibilities')); - test.verifyEqual(0, hint0.start); - test.verifyEqual(1, hint0.end); - test.verifyTrue(hint0.predictions.length > 0); + update({ typed: ' ', cursor: { start: 1, end: 1 } }); + test.verifyEqual('1', statuses); + test.verifyEqual(1, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(1, display.start); + test.verifyEqual(1, display.end); + test.verifyEqual(display, worst); + test.verifyNull(cli.commandAssignment.value); + + update({ typed: ' ', cursor: { start: 0, end: 0 } }); + test.verifyEqual('1', statuses); + test.verifyEqual(1, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(1, display.start); + test.verifyEqual(1, display.end); + test.verifyEqual(display, worst); + test.verifyNull(cli.commandAssignment.value); + + update({ typed: 's', cursor: { start: 1, end: 1 } }); + test.verifyEqual('1', statuses); + test.verifyEqual(1, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(0, display.start); + test.verifyEqual(1, display.end); + test.verifyEqual(display, worst); + test.verifyTrue(display.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); - verifyPredictionsContains('set', hint0.predictions); - test.verifyNull(cli.command); + test.verifyTrue(display.predictions.length < 20); + verifyPredictionsContains('set', display.predictions); + test.verifyNull(cli.commandAssignment.value); - update({ typed: 'set', cursor: { start: 3, end: 3 } }); - test.verifyEqual(1, hints.length); - test.verifyEqual(Status.VALID, hint0.status); - test.verifyEqual(0, hint0.start); - test.verifyEqual(3, hint0.end); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set', cursor: { start: 3, end: 3 } }); + test.verifyEqual('000', statuses); + test.verifyEqual(1, cli._hints.length); + test.verifyEqual(Status.VALID, display.status); + test.verifyEqual(0, display.start); + test.verifyEqual(3, display.end); + test.verifyEqual('set', cli.commandAssignment.value.name); - update({ typed: 'set ', cursor: { start: 4, end: 4 } }); - test.verifyEqual(2, hints.length); - test.verifyEqual(Status.VALID, hint0.status); - 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 ', cursor: { start: 4, end: 4 } }); + test.verifyEqual('0000', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual(Status.VALID, display.status); + test.verifyEqual(4, display.start); + test.verifyEqual(4, display.end); + test.verifyEqual(display, worst); + test.verifyEqual('set', cli.commandAssignment.value.name); - update({ typed: 'set h', cursor: { start: 5, end: 5 } }); - test.verifyEqual(2, hints.length); - test.verifyEqual(Status.INCOMPLETE, hint0.status); - test.verifyEqual(4, hint0.start); - test.verifyEqual(5, hint0.end); - test.verifyTrue(hint0.predictions.length > 0); - verifyPredictionsContains('historyLength', hint0.predictions); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set ', cursor: { start: 2, end: 2 } }); + test.verifyEqual('0000', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual(Status.VALID, display.status); + test.verifyEqual(0, display.start); + test.verifyEqual(3, display.end); + test.verifyEqual('set', cli.commandAssignment.value.name); + + update({ typed: 'set h', cursor: { start: 5, end: 5 } }); + test.verifyEqual('00001', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(4, display.start); + test.verifyEqual(5, display.end); + test.verifyTrue(display.predictions.length > 0); + verifyPredictionsContains('historyLength', display.predictions); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('h', settingAssignment.arg.text); test.verifyEqual(undefined, settingAssignment.value); - update({ typed: 'set historyLengt', cursor: { start: 16, end: 16 } }); - test.verifyEqual(2, hints.length); - test.verifyEqual(Status.INCOMPLETE, hint0.status); - test.verifyEqual(4, hint0.start); - test.verifyEqual(16, hint0.end); - test.verifyEqual(1, hint0.predictions.length); - verifyPredictionsContains('historyLength', hint0.predictions); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set historyLengt', cursor: { start: 16, end: 16 } }); + test.verifyEqual('0000111111111111', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual(Status.INCOMPLETE, display.status); + test.verifyEqual(4, display.start); + test.verifyEqual(16, display.end); + test.verifyEqual(1, display.predictions.length); + verifyPredictionsContains('historyLength', display.predictions); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLengt', settingAssignment.arg.text); test.verifyEqual(undefined, settingAssignment.value); - update({ typed: 'set historyLengt', cursor: { start: 1, end: 1 } }); - 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); + update({ typed: 'set historyLengt', cursor: { start: 1, end: 1 } }); + test.verifyEqual('0000222222222222', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual(Status.VALID, display.status); + test.verifyEqual(0, display.start); + test.verifyEqual(3, display.end); + test.verifyEqual(Status.INVALID, worst.status); + test.verifyEqual(4, worst.start); + test.verifyEqual(16, worst.end); + test.verifyEqual(1, worst.predictions.length); + verifyPredictionsContains('historyLength', worst.predictions); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLengt', settingAssignment.arg.text); test.verifyEqual(undefined, settingAssignment.value); - update({ typed: 'set historyLengt ', cursor: { start: 17, end: 17 } }); - 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); + update({ typed: 'set historyLengt ', cursor: { start: 17, end: 17 } }); + // TODO: would '00002222222222220' be better? + test.verifyEqual('00002222222222222', statuses); + test.verifyEqual(3, cli._hints.length); + test.verifyEqual(Status.VALID, display.status); + test.verifyEqual(17, display.start); + test.verifyEqual(17, display.end); + test.verifyEqual(Status.INVALID, worst.status); + test.verifyEqual(4, worst.start); + test.verifyEqual(16, worst.end); + test.verifyEqual(1, worst.predictions.length); + verifyPredictionsContains('historyLength', worst.predictions); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLengt', settingAssignment.arg.text); test.verifyEqual(undefined, settingAssignment.value); - update({ typed: 'set historyLength', cursor: { start: 17, end: 17 } }); - test.verifyEqual(2, hints.length); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set historyLength', cursor: { start: 17, end: 17 } }); + test.verifyEqual('00000000000000000', statuses); + test.verifyEqual(2, cli._hints.length); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLength', settingAssignment.arg.text); test.verifyEqual(historyLengthSetting, settingAssignment.value); - update({ typed: 'set historyLength ', cursor: { start: 18, end: 18 } }); - test.verifyEqual(3, hints.length); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set historyLength ', cursor: { start: 18, end: 18 } }); + test.verifyEqual('000000000000000000', statuses); + test.verifyEqual(3, cli._hints.length); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLength', settingAssignment.arg.text); test.verifyEqual(historyLengthSetting, settingAssignment.value); - update({ typed: 'set historyLength 6', cursor: { start: 19, end: 19 } }); - test.verifyEqual(3, hints.length); - test.verifyEqual('set', cli.command.name); + update({ typed: 'set historyLength 6', cursor: { start: 19, end: 19 } }); + test.verifyEqual('0000000000000000000', statuses); + test.verifyEqual(3, cli._hints.length); + test.verifyEqual('set', cli.commandAssignment.value.name); test.verifyEqual('historyLength', settingAssignment.arg.text); test.verifyEqual(historyLengthSetting, settingAssignment.value); test.verifyEqual('6', valueAssignment.arg.text); diff --git a/plugins/cockpit/lib/ui/cliView.js b/plugins/cockpit/lib/ui/cliView.js index 5e34777b..bd2c047f 100644 --- a/plugins/cockpit/lib/ui/cliView.js +++ b/plugins/cockpit/lib/ui/cliView.js @@ -192,7 +192,9 @@ CliView.prototype = { // var handled = keyboardManager.processKeyEvent(ev, this, { // isCommandLine: true, isKeyUp: false // }); - if (ev.keyCode === keyutil.KeyHelper.KEY.TAB) { + if (ev.keyCode === keyutil.KeyHelper.KEY.TAB || + ev.keyCode === keyutil.KeyHelper.KEY.UP || + ev.keyCode === keyutil.KeyHelper.KEY.DOWN) { return true; } this.isUpdating = false; @@ -213,7 +215,7 @@ CliView.prototype = { // RETURN does a special exec/highlight thing if (ev.keyCode === keyutil.KeyHelper.KEY.RETURN) { - var worst = this.getWorstHint(); + var worst = this.cli.getWorstHint(); // Deny RETURN unless the command might work if (worst.status === Status.VALID) { this.cli.exec(); @@ -228,14 +230,25 @@ CliView.prototype = { } } - // TAB does a special complete thing - if (ev.keyCode === keyutil.KeyHelper.KEY.TAB) { - var assignment = this.cli.getAssignmentAt(this.element.selectionStart); - if (assignment) { - this.isUpdating = false; - assignment.complete(); - this.isUpdating = true; + // Special actions which delegate to the assignment + var current = this.cli.getAssignmentAt(this.element.selectionStart); + if (current) { + this.isUpdating = false; + + // TAB does a special complete thing + if (ev.keyCode === keyutil.KeyHelper.KEY.TAB) { + current.complete(); } + + // UP/DOWN look for some history + if (ev.keyCode === keyutil.KeyHelper.KEY.UP) { + current.increment(); + } + if (ev.keyCode === keyutil.KeyHelper.KEY.DOWN) { + current.decrement(); + } + + this.isUpdating = true; } this.update(); @@ -268,7 +281,7 @@ CliView.prototype = { var highlightedInput = '> '; if (this.element.value.length > 0) { var scores = this.cli.getInputStatusMarkup(); - // Create markup + // Create mark-up var i = 0; var lastStatus = -1; while (true) { @@ -290,26 +303,30 @@ CliView.prototype = { // Display the "-> prediction" at the end of the completer var display = this.cli.getAssignmentAt(this.element.selectionStart).getHint(); - var message = display.message; - if (display.predictions && display.predictions.length > 0) { - message += ': [ '; - display.predictions.forEach(function(prediction) { - if (prediction.name) { - message += prediction.name + ' | '; - } - else { - message += prediction + ' | '; - } - }, this); - message = message.replace(/\| $/, ']'); + var message = ''; + if (this.element.value.length !== 0) { + message += display.message; + if (display.predictions && display.predictions.length > 0) { + message += ': [ '; + display.predictions.forEach(function(prediction) { + if (prediction.name) { + message += prediction.name + ' | '; + } + else { + message += prediction + ' | '; + } + }, this); + message = message.replace(/\| $/, ']'); - var onTab = display.predictions[0]; - onTab = onTab.name ? onTab.name : onTab; - this.completer.innerHTML = highlightedInput + '  ⇥ ' + onTab; - } - else { - this.completer.innerHTML = highlightedInput; + var onTab = display.predictions[0]; + onTab = onTab.name ? onTab.name : onTab; + this.completer.innerHTML = highlightedInput + '  ⇥ ' + onTab; + } + else { + this.completer.innerHTML = highlightedInput; + } } + this.hinter.innerHTML = message; if (message.length === 0) { this.hinter.classList.add('cptNoHints'); diff --git a/plugins/pilot/lib/canon.js b/plugins/pilot/lib/canon.js index 9e88af03..476afa01 100644 --- a/plugins/pilot/lib/canon.js +++ b/plugins/pilot/lib/canon.js @@ -45,6 +45,7 @@ var EventEmitter = require('pilot/event_emitter').EventEmitter; var catalog = require('pilot/catalog'); var Status = require('pilot/types').Status; var types = require('pilot/types'); +var util = require('pilot/util'); /* // TODO: this doesn't belong here - or maybe anywhere? @@ -105,8 +106,16 @@ var thingCommand = { } }; +/** + * A lookup hash of our registered commands + */ var commands = {}; +/** + * A sorted list of command names, we regularly want them in order, so pre-sort + */ +var commandNames = []; + /** * This registration method isn't like other Ace registration methods because * it doesn't return a decorated command because there is no functional @@ -131,6 +140,9 @@ function addCommand(command) { upgradeType(param); }, this); commands[command.name] = command; + + commandNames.push(command.name); + commandNames.sort(); }; function upgradeType(param) { @@ -143,12 +155,9 @@ function upgradeType(param) { } function removeCommand(command) { - if (typeof command === 'string') { - delete commands[command]; - } - else { - delete commands[command.name]; - } + var name = (typeof command === 'string' ? command : command.name); + delete commands[name]; + util.arrayRemove(commandNames, name); }; function getCommand(name) { @@ -156,7 +165,7 @@ function getCommand(name) { }; function getCommandNames() { - return Object.keys(commands); + return commandNames; }; /** diff --git a/plugins/pilot/lib/settings.js b/plugins/pilot/lib/settings.js index 2024d62f..19c722b3 100644 --- a/plugins/pilot/lib/settings.js +++ b/plugins/pilot/lib/settings.js @@ -146,12 +146,13 @@ oop.implement(Setting.prototype, EventEmitter); * @constructor */ function Settings(persister) { - /** - * Storage for deactivated values - */ + // Storage for deactivated values this._deactivated = {}; + // Storage for the active settings this._settings = {}; + // We often want sorted setting names. Cache + this._settingNames = []; if (persister) { this.setPersister(persister); @@ -175,14 +176,18 @@ Settings.prototype = { addSetting: function(settingSpec) { var setting = new Setting(settingSpec, this); this._settings[setting.name] = setting; + this._settingNames.push(setting.name); + this._settingNames.sort(); }, - removeSetting: function(name) { + removeSetting: function(setting) { + var name = (typeof setting === 'string' ? setting : setting.name); delete this._settings[name]; + util.arrayRemove(this._settingNames, name); }, getSettingNames: function() { - return Object.keys(this._settings); + return this._settingNames; }, getSetting: function(name) { diff --git a/plugins/pilot/lib/types.js b/plugins/pilot/lib/types.js index abc1c4d7..0a95972f 100644 --- a/plugins/pilot/lib/types.js +++ b/plugins/pilot/lib/types.js @@ -163,7 +163,23 @@ Type.prototype = { *

In old bespin, equality was based on the name. This may turn out to be * important in Ace too. */ - name: undefined + name: undefined, + + /** + * If there is some concept of a higher value, return it, + * otherwise return undefined. + */ + increment: function(value) { + return undefined; + }, + + /** + * If there is some concept of a lower value, return it, + * otherwise return undefined. + */ + decrement: function(value) { + return undefined; + } }; exports.Type = Type; diff --git a/plugins/pilot/lib/types/basic.js b/plugins/pilot/lib/types/basic.js index 6eeb64a8..b119e879 100644 --- a/plugins/pilot/lib/types/basic.js +++ b/plugins/pilot/lib/types/basic.js @@ -90,6 +90,10 @@ number.parse = function(value) { throw new Error('non-string passed to number.parse()'); } + if (value.replace(/\s/g, '').length === 0) { + return new Conversion(null, Status.INCOMPLETE, ''); + } + var reply = new Conversion(parseInt(value, 10)); if (isNaN(reply.value)) { reply.status = Status.INVALID; @@ -99,6 +103,14 @@ number.parse = function(value) { return reply; }; +number.decrement = function(value) { + return value - 1; +}; + +number.increment = function(value) { + return value + 1; +}; + number.name = 'number'; /** @@ -126,7 +138,7 @@ SelectionType.prototype.parse = function(str) { if (!this.data) { throw new Error('Missing data on selection type extension.'); } - var data = (typeof(this.data) === "function") ? this.data() : this.data; + var data = (typeof(this.data) === 'function') ? this.data() : this.data; // The matchedValue could be the boolean value false var hasMatched = false; @@ -162,6 +174,34 @@ SelectionType.prototype.fromString = function(str) { return str; }; +SelectionType.prototype.decrement = function(value) { + var data = (typeof this.data === 'function') ? this.data() : this.data; + var index; + if (value == null) { + index = data.length - 1; + } + else { + var name = this.stringify(value); + var index = data.indexOf(name); + index = (index === 0 ? data.length - 1 : index - 1); + } + return this.fromString(data[index]); +}; + +SelectionType.prototype.increment = function(value) { + var data = (typeof this.data === 'function') ? this.data() : this.data; + var index; + if (value == null) { + index = 0; + } + else { + var name = this.stringify(value); + var index = data.indexOf(name); + index = (index === data.length - 1 ? 0 : index + 1); + } + return this.fromString(data[index]); +}; + SelectionType.prototype.name = 'selection'; /** @@ -206,6 +246,16 @@ DeferredType.prototype.parse = function(value) { return this.defer().parse(value); }; +DeferredType.prototype.decrement = function(value) { + var deferred = this.defer(); + return (deferred.decrement ? deferred.decrement(value) : undefined); +}; + +DeferredType.prototype.increment = function(value) { + var deferred = this.defer(); + return (deferred.increment ? deferred.increment(value) : undefined); +}; + DeferredType.prototype.name = 'deferred'; /**