90 lines
No EOL
3 KiB
TypeScript
90 lines
No EOL
3 KiB
TypeScript
import type { ServerChannel } from "ssh2";
|
|
|
|
export async function readFromKeyboard(stream: ServerChannel, disableEcho: boolean = false): Promise<string> {
|
|
const leftEscape = "\x1B[D";
|
|
const rightEscape = "\x1B[C";
|
|
|
|
const ourBackspace = "\u0008";
|
|
|
|
// \x7F = Ascii escape code for backspace (client side)
|
|
|
|
let line = "";
|
|
let lineIndex = 0;
|
|
let isReady = false;
|
|
|
|
async function eventLoop(): Promise<any> {
|
|
const readStreamData = stream.read();
|
|
if (readStreamData == null) return setTimeout(eventLoop, 5);
|
|
|
|
if (readStreamData.includes("\r") || readStreamData.includes("\n")) {
|
|
line = line.replace("\r", "");
|
|
isReady = true;
|
|
|
|
return;
|
|
} else if (readStreamData.includes("\x7F")) {
|
|
if (line.length == 0) return setTimeout(eventLoop, 5); // Here because if we do it in the parent if statement, shit breaks
|
|
line = line.substring(0, lineIndex - 1) + line.substring(lineIndex);
|
|
|
|
if (!disableEcho) {
|
|
const deltaCursor = line.length - lineIndex;
|
|
|
|
if (deltaCursor == line.length) return setTimeout(eventLoop, 5);
|
|
|
|
if (deltaCursor < 0) {
|
|
// Use old technique if the delta is < 0, as the new one is tailored to the start + 1 to end - 1
|
|
stream.write(ourBackspace + " " + ourBackspace);
|
|
} else {
|
|
// Jump forward to the front, and remove the last character
|
|
stream.write(rightEscape.repeat(deltaCursor) + " " + ourBackspace);
|
|
|
|
// Go backwards & rerender text & go backwards again (wtf?)
|
|
stream.write(leftEscape.repeat(deltaCursor + 1) + line.substring(lineIndex - 1) + leftEscape.repeat(deltaCursor + 1));
|
|
}
|
|
|
|
lineIndex -= 1;
|
|
}
|
|
} else if (readStreamData.includes("\x1B")) {
|
|
if (readStreamData.includes(rightEscape)) {
|
|
if (lineIndex + 1 > line.length) return setTimeout(eventLoop, 5);
|
|
lineIndex += 1;
|
|
} else if (readStreamData.includes(leftEscape)) {
|
|
if (lineIndex - 1 < 0) return setTimeout(eventLoop, 5);
|
|
lineIndex -= 1;
|
|
} else {
|
|
return setTimeout(eventLoop, 5);
|
|
}
|
|
|
|
if (!disableEcho) stream.write(readStreamData);
|
|
} else {
|
|
lineIndex += readStreamData.length;
|
|
|
|
// There isn't a splice method for String prototypes. So, ugh:
|
|
line = line.substring(0, lineIndex - 1) + readStreamData + line.substring(lineIndex - 1);
|
|
|
|
if (!disableEcho) {
|
|
let deltaCursor = line.length - lineIndex;
|
|
|
|
// wtf?
|
|
if (deltaCursor < 0) {
|
|
console.log("FIXME: somehow, our deltaCursor value is negative! please investigate me");
|
|
deltaCursor = 0;
|
|
}
|
|
|
|
stream.write(line.substring(lineIndex - 1) + leftEscape.repeat(deltaCursor));
|
|
}
|
|
}
|
|
|
|
setTimeout(eventLoop, 5);
|
|
}
|
|
|
|
// Yes, this is bad practice. Currently, I don't care.
|
|
return new Promise(async(resolve) => {
|
|
eventLoop();
|
|
|
|
while (!isReady) {
|
|
await new Promise((i) => setTimeout(i, 5));
|
|
}
|
|
|
|
resolve(line);
|
|
});
|
|
}; |