mirror of
https://github.com/linuxserver/core.git
synced 2026-02-20 05:07:19 +08:00
Merge pull request +15489 from c9/ide-tune-predict-echo
Tune terminal prediction
This commit is contained in:
commit
bcbdb30548
@ -29,7 +29,7 @@ define(function(require, exports, module) {
|
||||
|
||||
var MIN_PREDICTION_WAIT = 500;
|
||||
var PING_DEVIATION = 500;
|
||||
var INSERTABLE_CHARS = /^[A-Za-z0-9!"#$%&'()*+,-\.\/:;<=>?!@[\] ^_`{|}~]+$/;
|
||||
var INSERTABLE_CHARS = /^[A-Za-z0-9!"#$%&'()*+,-\.\/\\:;<=>?!@[\] ^_`{|}~]+$/;
|
||||
var INPUT_BACKSPACE = "\u007F";
|
||||
var ESC = "\u001B";
|
||||
var OUTPUTS_BACKSPACE_ALL = ["\b" + ESC + "[K", "\b" + ESC + "[1K"];
|
||||
@ -45,10 +45,9 @@ define(function(require, exports, module) {
|
||||
var INPUT_RIGHT = ESC + "[C";
|
||||
var OUTPUTS_RIGHT = [ESC + "[C", ESC + "[1C"];
|
||||
var STATE_PREDICT = 0;
|
||||
var STATE_WAIT_FOR_ECHO_OR_PROMPT = 1;
|
||||
var STATE_WAIT_FOR_ECHO = 2;
|
||||
var STATE_WAIT_FOR_PROMPT = 3;
|
||||
var STATE_INITING = 4;
|
||||
var STATE_WAIT_FOR_PROMPT_OR_ECHO = 1;
|
||||
var STATE_WAIT_FOR_PROMPT = 2;
|
||||
var STATE_INITING = 3;
|
||||
|
||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||
var emit = plugin.getEmitter();
|
||||
@ -72,6 +71,9 @@ define(function(require, exports, module) {
|
||||
terminal.on("beforeWrite", function(e) {
|
||||
return e.session.$predictor.onBeforeWrite(e);
|
||||
}, plugin);
|
||||
terminal.on("afterWrite", function(e) {
|
||||
return e.session.$predictor.onAfterWrite(e);
|
||||
}, plugin);
|
||||
terminal.on("input", function(e) {
|
||||
DEBUG && console.log(">", e.data.replace("\r", "\\r").replace("\u007F", "\\bs"));
|
||||
return e.session.$predictor.onInput(e);
|
||||
@ -86,7 +88,7 @@ define(function(require, exports, module) {
|
||||
var predictStartX = 0;
|
||||
var predictStartY = 0;
|
||||
var nonPredictStartY = 0;
|
||||
var state = STATE_WAIT_FOR_ECHO_OR_PROMPT;
|
||||
var state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
var lastInput = null;
|
||||
|
||||
// We maintain a copy of the terminal state without predictions
|
||||
@ -117,7 +119,7 @@ define(function(require, exports, module) {
|
||||
|
||||
if (isPossibleConnectionGone()) {
|
||||
DEBUG && console.log("!", "nopredict: connection gone?");
|
||||
state = STATE_WAIT_FOR_ECHO;
|
||||
state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -152,17 +154,21 @@ define(function(require, exports, module) {
|
||||
command.after = { predict: predictLine, predictIndex: predictIndex };
|
||||
command.sent = Date.now();
|
||||
|
||||
DEBUG && console.log("!"
|
||||
+ nonPredictTerminal.$debugCharsAt(nonPredictTerminal.y)
|
||||
.slice(0, predictStartX)
|
||||
.map(function(c) { return c || " "; })
|
||||
.join("")
|
||||
+ "%c" + predictLine,
|
||||
"color: lightblue");
|
||||
if (DEBUG) {
|
||||
var alreadyEchoed = predictions[0].before.predict;
|
||||
console.log("!" + debugPromptSuffix()
|
||||
+ predictLine.substr(0, alreadyEchoed.length)
|
||||
+ "%c" + predictLine.substr(alreadyEchoed.length),
|
||||
"color: lightblue"
|
||||
);
|
||||
}
|
||||
|
||||
// DEBUG && console.log("!="
|
||||
// + session.terminal.$debugCharsAt(predictStartY - session.terminal.ybase).join(""));
|
||||
|
||||
command.timeout = setTimeout(function panic() {
|
||||
if (!c9.has(c9.NETWORK) || !c9.connected) {
|
||||
state = STATE_WAIT_FOR_ECHO;
|
||||
state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
c9.once("connect", function() {
|
||||
command.timeout = setTimeout(panic, MIN_PREDICTION_WAIT);
|
||||
});
|
||||
@ -184,6 +190,12 @@ define(function(require, exports, module) {
|
||||
}
|
||||
}
|
||||
|
||||
function debugPromptSuffix() {
|
||||
return nonPredictTerminal.$debugCharsAt(nonPredictTerminal.y)
|
||||
.slice(0, predictStartX).slice(-3)
|
||||
.map(function(c) { return c || " "; }).join("");
|
||||
}
|
||||
|
||||
function isPossibleConnectionGone() {
|
||||
if (!pendingPings.length)
|
||||
return;
|
||||
@ -202,15 +214,19 @@ define(function(require, exports, module) {
|
||||
}
|
||||
|
||||
DEBUG && console.log(
|
||||
"< "
|
||||
+ (state == STATE_PREDICT ? nonPredictTerminal.$debugCharsAt(e.$startY).join("") + " < " : "")
|
||||
+ e.data
|
||||
"<"
|
||||
+ (state == STATE_PREDICT
|
||||
? debugPromptSuffix() +
|
||||
nonPredictTerminal.$debugCharsAt(e.$startY).slice(predictStartX).join("")
|
||||
: "")
|
||||
+ "%c < " + e.data,
|
||||
"color: lightblue"
|
||||
);
|
||||
|
||||
if (!predictions.length) {
|
||||
if (state == STATE_PREDICT && nonPredictStartY !== nonPredictTerminal.ybase + nonPredictTerminal.y) {
|
||||
DEBUG && console.log(" ^ disabled predictions: (row changed)");
|
||||
state = STATE_WAIT_FOR_ECHO;
|
||||
state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
}
|
||||
tryEnablePrediction(e.data);
|
||||
emit("nopredict", { data: e.data, session: session });
|
||||
@ -219,13 +235,17 @@ define(function(require, exports, module) {
|
||||
|
||||
pong();
|
||||
|
||||
var result;
|
||||
chopPredictions(e, predictions, function(err, _result) {
|
||||
result = _result;
|
||||
if (err || !result) {
|
||||
DEBUG && console.log("[predict_echo] mispredict?", e.data.replace(/\r/g, "\\r"),
|
||||
"\n@", nonPredictTerminal.$debugCharsAt(e.$startY).join(""));
|
||||
emit("mispredict", { data: e.data, predictions: predictions, session: session });
|
||||
chopPredictions(e, predictions, function(err, results, line) {
|
||||
if (err || !results) {
|
||||
DEBUG && console.log("[predict_echo] mispredict?", e.data.replace(/\r/g, "\\r")
|
||||
+ "\n!=" + session.terminal.$debugCharsAt(predictStartY - session.terminal.ybase).join("")
|
||||
+ "\n<=" + nonPredictTerminal.$debugCharsAt(e.$startY).join(""));
|
||||
emit("mispredict", {
|
||||
data: e.data,
|
||||
line: charsOf(line),
|
||||
predictions: predictions,
|
||||
session: session
|
||||
});
|
||||
undoPredictions();
|
||||
}
|
||||
// I would try to enable predictions here,
|
||||
@ -244,7 +264,7 @@ define(function(require, exports, module) {
|
||||
/**
|
||||
* Temporarily restore the unpredict terminal state to allow
|
||||
* writing incoming data, including small anomalies that may
|
||||
* not have been predict but still passed our sanity checks.
|
||||
* not have been predicted but still passed our sanity checks.
|
||||
*/
|
||||
function writePredictData(data, startX) {
|
||||
var predictTerminal = session.terminal;
|
||||
@ -288,7 +308,7 @@ define(function(require, exports, module) {
|
||||
|
||||
pendingPings = [];
|
||||
predictions = [];
|
||||
state = STATE_WAIT_FOR_ECHO;
|
||||
state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
copyTerminalLineTo(terminal);
|
||||
session.terminal.x = nonPredictTerminal.x;
|
||||
lastInput = null; // avoid immediately enabling again
|
||||
@ -304,7 +324,12 @@ define(function(require, exports, module) {
|
||||
var fromChars = target === nonPredictTerminal ? predictChars : nonPredictChars;
|
||||
var toChars = target === nonPredictTerminal ? nonPredictChars : predictChars;
|
||||
|
||||
if (!predictChars) { // terminal likely just refreshed, never mind copying to it
|
||||
state = STATE_WAIT_FOR_PROMPT;
|
||||
return;
|
||||
}
|
||||
if (!fromChars || !toChars) {
|
||||
state = STATE_WAIT_FOR_PROMPT;
|
||||
errorHandler.reportError(new Error("Warning: can't copy terminal line: "), {
|
||||
fromChars: fromChars, toChars: toChars
|
||||
});
|
||||
@ -347,14 +372,14 @@ define(function(require, exports, module) {
|
||||
* @param {Object[]} predictions
|
||||
* @param {Function} callback
|
||||
* @param {Error} callback.err
|
||||
* @param {Boolean} callback.result Whether prediction was succesful.
|
||||
* @param {Object[]|Boolean} callback.results A list of matching predictions, or `false`
|
||||
*/
|
||||
function chopPredictions(e, predictions, callback) {
|
||||
var line = nonPredictTerminal.lines[nonPredictStartY];
|
||||
var rowChanged = nonPredictStartY !== nonPredictTerminal.y + nonPredictTerminal.ybase;
|
||||
|
||||
if (!checkTextBeforePrediction())
|
||||
return callback(null, false);
|
||||
return done(null, false);
|
||||
|
||||
// Check if predictions became true
|
||||
var matchedOneOff = false;
|
||||
@ -390,26 +415,36 @@ define(function(require, exports, module) {
|
||||
predict = predictions.splice(0, i + 1);
|
||||
}
|
||||
|
||||
emit("predict", {
|
||||
data: e.data,
|
||||
session: session,
|
||||
predictions: predict
|
||||
});
|
||||
return callback(null, predict);
|
||||
return done(null, predict);
|
||||
}
|
||||
}
|
||||
|
||||
// No matches. But one got really close.
|
||||
if (matchedOneOff)
|
||||
return callback(null, []);
|
||||
return done(null, []);
|
||||
|
||||
// No matches. Return if our predictions were optional.
|
||||
if (isOptionalOnly(predictions))
|
||||
return callback(null, []);
|
||||
return done(null, []);
|
||||
|
||||
// No matches. But it seems we got a noop input. Our predictions likely happen later.
|
||||
if (matchPrediction(NoopCommand.tryCreate()))
|
||||
return done(null, []);
|
||||
|
||||
// No matches for our predictions :( We likely made a mistake.
|
||||
// Reporting false here ensures we catch mistakes early.
|
||||
return callback(null, false);
|
||||
return done(null, false);
|
||||
|
||||
function done(err, result) {
|
||||
if (result) {
|
||||
emit("predict", {
|
||||
data: e.data,
|
||||
session: session,
|
||||
predictions: predict
|
||||
});
|
||||
}
|
||||
callback(err, result, line);
|
||||
}
|
||||
|
||||
function matchPrediction(prediction) {
|
||||
var predict = prediction.after.predict;
|
||||
@ -466,7 +501,7 @@ define(function(require, exports, module) {
|
||||
return;
|
||||
|
||||
// Enable prediction when we see a prompt
|
||||
if ((state == STATE_WAIT_FOR_PROMPT || state === STATE_WAIT_FOR_ECHO_OR_PROMPT)
|
||||
if ((state == STATE_WAIT_FOR_PROMPT || state === STATE_WAIT_FOR_PROMPT_OR_ECHO)
|
||||
&& data.match(/[$#] $/)) {
|
||||
if (DEBUG) console.log(" ^ re-enabled predictions: (prompt)");
|
||||
return startPredict();
|
||||
@ -474,30 +509,43 @@ define(function(require, exports, module) {
|
||||
|
||||
// Enable prediction when we see echoing
|
||||
if (lastInput
|
||||
&& (state === STATE_WAIT_FOR_ECHO || state === STATE_WAIT_FOR_ECHO_OR_PROMPT)
|
||||
&& (state === STATE_WAIT_FOR_PROMPT_OR_ECHO)
|
||||
&& lastInput === data.substr(data.length - lastInput.length)
|
||||
&& (!BASH_ONLY || isBashActive())) {
|
||||
if (DEBUG) console.log(" ^ re-enabled predictions:", lastInput);
|
||||
return startPredict();
|
||||
}
|
||||
|
||||
// Enable predictions when we see echoing *and* a prompt
|
||||
if (lastInput
|
||||
&& state == STATE_WAIT_FOR_PROMPT
|
||||
&& lastInput === data.substr(data.length - lastInput.length)
|
||||
&& isBashActive()) {
|
||||
if (DEBUG) console.log(" ^ re-enabled predictions:", lastInput);
|
||||
return startPredict();
|
||||
}
|
||||
}
|
||||
|
||||
function startPredict() {
|
||||
state = STATE_INITING;
|
||||
predictIndex = 0;
|
||||
predictLine = "";
|
||||
predictStartX = nonPredictTerminal.x;
|
||||
nonPredictStartY = nonPredictTerminal.y + nonPredictTerminal.ybase;
|
||||
predictStartY = session.terminal.y + session.terminal.ybase;
|
||||
terminal.once("afterWrite", function() {
|
||||
predictStartY = session.terminal.y + session.terminal.ybase;
|
||||
state = STATE_PREDICT;
|
||||
if (!checkTextBeforePrediction()) {
|
||||
// Appears to happen when tmux or shell unexpectedly sends a new line
|
||||
console.warn("Unable to init predictions");
|
||||
state = STATE_WAIT_FOR_ECHO;
|
||||
}
|
||||
});
|
||||
state = STATE_INITING;
|
||||
}
|
||||
|
||||
function onAfterWrite(e) {
|
||||
if (state !== STATE_INITING)
|
||||
return;
|
||||
|
||||
predictStartY = session.terminal.y + session.terminal.ybase;
|
||||
state = STATE_PREDICT;
|
||||
if (!checkTextBeforePrediction()) {
|
||||
// Appears to happen when tmux or shell unexpectedly sends a new line
|
||||
console.log("[predict_echo] Unable to init predictions; will try again later");
|
||||
state = STATE_WAIT_FOR_PROMPT;
|
||||
}
|
||||
}
|
||||
|
||||
function isBashActive() {
|
||||
@ -600,12 +648,10 @@ define(function(require, exports, module) {
|
||||
};
|
||||
function BackspaceCommand() {
|
||||
var after = predictLine.substr(predictIndex);
|
||||
var deletedChar;
|
||||
var outputText = OUTPUTS_BACKSPACE_CHAR[0];
|
||||
return {
|
||||
$outputText: outputText,
|
||||
do: function() {
|
||||
deletedChar = peek(-1);
|
||||
predictLine = predictLine.substr(0, predictIndex - 1) + after;
|
||||
predictIndex--;
|
||||
echo(outputText);
|
||||
@ -622,11 +668,9 @@ define(function(require, exports, module) {
|
||||
};
|
||||
function DeleteCommand() {
|
||||
var after = predictLine.substr(predictIndex + 1);
|
||||
var deletedChar;
|
||||
return {
|
||||
$outputText: OUTPUTS_DELETE_CHAR[0],
|
||||
do: function() {
|
||||
deletedChar = peek();
|
||||
predictLine = predictLine.substr(0, predictIndex) + after;
|
||||
echo(OUTPUTS_DELETE_CHAR[0]);
|
||||
}
|
||||
@ -641,12 +685,10 @@ define(function(require, exports, module) {
|
||||
return new CursorLeftCommand();
|
||||
};
|
||||
function CursorLeftCommand() {
|
||||
var noChange = false;
|
||||
return {
|
||||
$outputText: OUTPUTS_LEFT[0],
|
||||
do: function() {
|
||||
if (predictIndex === 0) {
|
||||
noChange = true;
|
||||
clearTimeout(this.timeout);
|
||||
return;
|
||||
}
|
||||
@ -683,12 +725,30 @@ define(function(require, exports, module) {
|
||||
return new HomeCommand();
|
||||
};
|
||||
function HomeCommand() {
|
||||
var oldIndex;
|
||||
var outputText = predictIndex ? getCursorLeft(predictIndex) : "";
|
||||
return {
|
||||
$outputText: outputText,
|
||||
do: function() {
|
||||
oldIndex = predictIndex;
|
||||
echo(outputText);
|
||||
predictIndex = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Noop command. Factory method: tryCreate().
|
||||
*/
|
||||
NoopCommand.tryCreate = function() {
|
||||
var result = new NoopCommand();
|
||||
result.before = { predict: predictLine, predictIndex: predictIndex };
|
||||
result.after = { predict: predictLine, predictIndex: predictIndex };
|
||||
return result;
|
||||
};
|
||||
function NoopCommand() {
|
||||
var outputText = predictIndex ? getCursorLeft(predictIndex) : "";
|
||||
return {
|
||||
$outputText: outputText,
|
||||
do: function() {
|
||||
echo(outputText);
|
||||
predictIndex = 0;
|
||||
}
|
||||
@ -708,18 +768,13 @@ define(function(require, exports, module) {
|
||||
get predictions() { return predictions; },
|
||||
undoPredictions: undoPredictions,
|
||||
onInput: onInput,
|
||||
onBeforeWrite: onBeforeWrite
|
||||
onBeforeWrite: onBeforeWrite,
|
||||
onAfterWrite: onAfterWrite,
|
||||
};
|
||||
}
|
||||
|
||||
function charsOf(s) {
|
||||
var r1 = [];
|
||||
var r2 = [];
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
r1.push(s.charAt(i));
|
||||
r2.push(s.charCodeAt(i));
|
||||
}
|
||||
return [r1, r2];
|
||||
function charsOf(line) {
|
||||
return line.map(function(c) { return c[1] }).join("");
|
||||
}
|
||||
|
||||
function getCursorLeft(n) {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/assertions"], function (architect, chai, baseProc) {
|
||||
var expect = chai.expect;
|
||||
var TMUXNAME = "cloud9test2";
|
||||
|
||||
expect.setupArchitectTest([
|
||||
{
|
||||
@ -14,7 +15,6 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
debug: true,
|
||||
hosted: true,
|
||||
local: false,
|
||||
davPrefix: "/"
|
||||
},
|
||||
|
||||
"plugins/c9.core/ext",
|
||||
@ -45,7 +45,7 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
"plugins/c9.ide.ui/forms",
|
||||
{
|
||||
packagePath: "plugins/c9.fs/proc",
|
||||
tmuxName: "cloud9test2"
|
||||
tmuxName: TMUXNAME
|
||||
},
|
||||
"plugins/c9.vfs.client/vfs_client",
|
||||
"plugins/c9.vfs.client/endpoint",
|
||||
@ -55,16 +55,6 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
baseProc: baseProc
|
||||
},
|
||||
|
||||
// Mock plugins
|
||||
{
|
||||
consumes: ["apf", "ui", "Plugin"],
|
||||
provides: [
|
||||
"commands", "menus", "commands", "layout", "watcher",
|
||||
"save", "anims", "clipboard", "dialog.alert", "auth.bootstrap",
|
||||
"info", "dialog.error"
|
||||
],
|
||||
setup: expect.html.mocked
|
||||
},
|
||||
{
|
||||
consumes: ["tabManager", "proc", "terminal", "terminal.predict_echo", "c9"],
|
||||
provides: [],
|
||||
@ -91,9 +81,9 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
var INPUT_DELETE = ESC + "[3~";
|
||||
var OUTPUT_BACKSPACE = "\b" + ESC + "[K";
|
||||
var OUTPUT_DELETE_CHAR = ESC + "[P";
|
||||
var STATE_WAIT_FOR_ECHO_OR_PROMPT = 1;
|
||||
var STATE_WAIT_FOR_ECHO = 2;
|
||||
var STATE_WAIT_FOR_PROMPT = 3;
|
||||
var STATE_WAIT_FOR_PROMPT_OR_ECHO = 1;
|
||||
var STATE_WAIT_FOR_PROMPT = 2;
|
||||
var STATE_INITING = 3;
|
||||
|
||||
expect.html.setConstructor(function(tab) {
|
||||
if (typeof tab == "object")
|
||||
@ -105,8 +95,6 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
|
||||
before(function(done) {
|
||||
this.timeout(45000);
|
||||
apf.config.setProperty("allow-select", false);
|
||||
apf.config.setProperty("allow-blur", false);
|
||||
|
||||
bar.$ext.style.background = "rgba(220, 220, 220, 0.93)";
|
||||
bar.$ext.style.position = "fixed";
|
||||
@ -120,7 +108,7 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
predictor.$setTestTimeouts();
|
||||
predictor.DEBUG = true;
|
||||
|
||||
proc.execFile("~/.c9/bin/tmux", { args: ["-L", "cloud9test", "kill-server"]}, function(err) {
|
||||
proc.execFile("~/.c9/bin/tmux", { args: ["-L", TMUXNAME, "kill-server"]}, function(err) {
|
||||
tabs.once("ready", function() {
|
||||
tabs.getPanes()[0].focus();
|
||||
openTerminal(done);
|
||||
@ -128,8 +116,10 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
});
|
||||
});
|
||||
|
||||
function openTerminal(done) {
|
||||
function openTerminal(callback) {
|
||||
tabs.openEditor("terminal", function(err, tab) {
|
||||
if (err) return callback(err);
|
||||
|
||||
editor = tab.editor;
|
||||
session = editor.ace.getSession().c9session;
|
||||
send = session.send;
|
||||
@ -137,25 +127,23 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
setTimeout(init);
|
||||
|
||||
function init() {
|
||||
afterPrompt(function() { setTimeout(start); });
|
||||
afterPrompt(function() { setTimeout(callback); });
|
||||
// Make sure we have a prompt with a dollar for tests
|
||||
// And terminal won't send rename commands in the middle of the test
|
||||
// TODO: do we need to handle rename sequence in predict_echo instead?
|
||||
editor.ace.onTextInput("PS1='. $ ';"
|
||||
editor.ace.onTextInput("PS1='P$ ';"
|
||||
+ "tmux setw automatic-rename off;"
|
||||
+ "printf '\\x1b]0;predict echo\\x07'\n");
|
||||
// editor.ace.onTextInput("ssh lennart\n");
|
||||
// editor.ace.onTextInput("ssh ubuntu@ci.c9.io\n");
|
||||
}
|
||||
});
|
||||
function start() {
|
||||
predictor.on("mispredict", function(e) {
|
||||
console.error("MISPREDICTED", e);
|
||||
delete e.session;
|
||||
throw new Error("MISPREDICTED: " + JSON.stringify(e));
|
||||
});
|
||||
setTimeout(done);
|
||||
}
|
||||
}
|
||||
|
||||
function reportMispredict(e) {
|
||||
console.error("MISPREDICTED", e);
|
||||
delete e.session;
|
||||
throw new Error("MISPREDICTED: " + JSON.stringify(e));
|
||||
}
|
||||
|
||||
function peek(offset) {
|
||||
@ -206,19 +194,23 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
setTimeout(function() {
|
||||
send(key);
|
||||
sendAll(keys, callback);
|
||||
});
|
||||
}, 5);
|
||||
}
|
||||
|
||||
describe.skip("predict_echo", function() {
|
||||
beforeEach(function(done) {
|
||||
predictor.off("mispredict", reportMispredict);
|
||||
|
||||
afterPredict("*", function() {
|
||||
afterPrompt(function() {
|
||||
session.$predictor.state = 0;
|
||||
predictor.on("mispredict", reportMispredict);
|
||||
done();
|
||||
});
|
||||
send("\r");
|
||||
});
|
||||
session.$predictor.state = 0;
|
||||
console.log("! next test");
|
||||
sendAll(" # next*".split(""));
|
||||
});
|
||||
|
||||
@ -373,8 +365,8 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
afterPredict("[", function() {
|
||||
afterPredict("[", function() {
|
||||
assert.equal(peek(-1), " ");
|
||||
assert.equal(peek(), "e");
|
||||
assert.equal(peek(1), "c");
|
||||
assert.equal(peek(), "#");
|
||||
assert.equal(peek(1), "p");
|
||||
|
||||
afterPrompt(done);
|
||||
send("\r");
|
||||
@ -384,16 +376,16 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
sendAll([INPUT_RIGHT]);
|
||||
});
|
||||
|
||||
sendAll(["eecho bleep", INPUT_HOME]);
|
||||
sendAll(["##print some chars; home; right; backspace", INPUT_HOME]);
|
||||
});
|
||||
|
||||
it("supports insert with repeated characters; stress test", function loop(done, attempt) {
|
||||
it("supports insert with repeated characters (prxaat); stress test", function loop(done, attempt) {
|
||||
this.timeout && this.timeout(60000);
|
||||
session.$predictor.state = 0;
|
||||
if (attempt === 5)
|
||||
return done();
|
||||
|
||||
sendAll("echo blaat".split(""), function() {
|
||||
sendAll("echo praat".split(""), function() {
|
||||
var sawX;
|
||||
|
||||
afterPredict("t", function() {
|
||||
@ -402,8 +394,9 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
});
|
||||
predictor.on("predict", function wait(e) {
|
||||
sawX = sawX || e.data.match(/x/);
|
||||
if (!sawX || e.data.match(/xaat/) || !e.data.match(/a/))
|
||||
return; // console.log(" -", e.data, sawX)*
|
||||
// Wait until we've seen an 'x' and then an 'a'
|
||||
if (!sawX || e.data.match(/xaat$/))
|
||||
return console.log(" -", e.data, !!sawX);
|
||||
predictor.off("predict", wait);
|
||||
|
||||
assert.equal(peek(), "a");
|
||||
@ -494,11 +487,11 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
});
|
||||
|
||||
// sometimes backspace will re-enable state 0; we reset it here
|
||||
session.$predictor.state = STATE_WAIT_FOR_ECHO_OR_PROMPT;
|
||||
session.$predictor.state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
send(":");
|
||||
});
|
||||
|
||||
session.$predictor.state = STATE_WAIT_FOR_ECHO_OR_PROMPT;
|
||||
session.$predictor.state = STATE_WAIT_FOR_PROMPT_OR_ECHO;
|
||||
send(INPUT_BACKSPACE);
|
||||
});
|
||||
|
||||
@ -542,6 +535,19 @@ require(["lib/architect/architect", "lib/chai/chai", "/vfs-root", "ace/test/asse
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it("recovers after spurious backspaces on a prompt", function(done) {
|
||||
var afterBackspace = false;
|
||||
predictor.once("nopredict", function() {
|
||||
assert.equal(afterBackspace, true);
|
||||
// assert.equal(session.$predictor.state, STATE_INITING);
|
||||
afterPrompt(done);
|
||||
send("\r");
|
||||
});
|
||||
send(INPUT_BACKSPACE);
|
||||
afterBackspace = true;
|
||||
send("Q");
|
||||
});
|
||||
});
|
||||
|
||||
if (!onload.remain) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user