fix: batched keypress events failed to be recognized. (#167)

* fix: use 'keypress' event instead of 'data' and use readline's built in parsing to support batched key actions

Signed-off-by: Chapman Pendery <cpendery@vt.edu>

* fix: drop coming pr code

Signed-off-by: Chapman Pendery <cpendery@vt.edu>

---------

Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
Chapman Pendery
2024-02-15 09:37:59 -08:00
committed by GitHub
parent ffae75fbbf
commit ec0798af90
4 changed files with 30 additions and 49 deletions
-10
View File
@@ -1,10 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
export const inputModifier = (input: Buffer): string => {
switch (input.toString()) {
case "\b":
return "\u007F"; // DEL
}
return input.toString();
};
+18 -9
View File
@@ -7,8 +7,8 @@ import { ISTerm } from "../isterm/pty.js";
import { renderBox, truncateText, truncateMultilineText } from "./utils.js";
import ansi from "ansi-escapes";
import chalk from "chalk";
import { parseKeystroke } from "../utils/ansi.js";
import { Shell } from "../utils/shell.js";
import log from "../utils/log.js";
const maxSuggestions = 5;
const suggestionWidth = 40;
@@ -22,6 +22,13 @@ type SuggestionsSequence = {
rows: number;
};
export type KeyPressEvent = [string | null | undefined, KeyPress];
type KeyPress = {
sequence: string;
name: string;
};
export class SuggestionManager {
#term: ISTerm;
#command: string;
@@ -140,16 +147,15 @@ export class SuggestionManager {
};
}
update(input: Buffer): "handled" | "fully-handled" | false {
const keyStroke = parseKeystroke(input);
if (keyStroke == null) return false;
if (keyStroke == "esc") {
update(keyPress: KeyPress): boolean {
const { name } = keyPress;
if (name == "escape") {
this.#suggestBlob = undefined;
} else if (keyStroke == "up") {
} else if (name == "up") {
this.#activeSuggestionIdx = Math.max(0, this.#activeSuggestionIdx - 1);
} else if (keyStroke == "down") {
} else if (name == "down") {
this.#activeSuggestionIdx = Math.min(this.#activeSuggestionIdx + 1, (this.#suggestBlob?.suggestions.length ?? 1) - 1);
} else if (keyStroke == "tab") {
} else if (name == "tab") {
const removals = "\u007F".repeat(this.#suggestBlob?.charactersToDrop ?? 0);
const suggestion = this.#suggestBlob?.suggestions.at(this.#activeSuggestionIdx);
const chars = suggestion?.insertValue ?? suggestion?.name + " ";
@@ -157,7 +163,10 @@ export class SuggestionManager {
return false;
}
this.#term.write(removals + chars);
} else {
return false;
}
return "handled";
log.debug({ msg: "handled keypress", ...keyPress });
return true;
}
}
+11 -8
View File
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import readline from "node:readline";
import ansi from "ansi-escapes";
import chalk from "chalk";
import { inputModifier } from "./input.js";
import log from "../utils/log.js";
import { Shell } from "../utils/shell.js";
import isterm from "../isterm/index.js";
import { eraseLinesBelow } from "../utils/ansi.js";
import { SuggestionManager, MAX_LINES } from "./suggestionManager.js";
import { SuggestionManager, MAX_LINES, KeyPressEvent } from "./suggestionManager.js";
export const renderConfirmation = (live: boolean): string => {
const statusMessage = live ? chalk.green("live") : chalk.red("not found");
@@ -21,7 +21,8 @@ export const render = async (shell: Shell) => {
const suggestionManager = new SuggestionManager(term, shell);
let hasActiveSuggestions = false;
let previousSuggestionsRows = 0;
process.stdin.setRawMode(true);
if (process.stdin.isTTY) process.stdin.setRawMode(true);
readline.emitKeypressEvents(process.stdin);
const writeOutput = (data: string) => {
log.debug({ msg: "writing data", data });
@@ -122,12 +123,14 @@ export const render = async (shell: Shell) => {
previousSuggestionsRows = suggestion.rows;
});
});
process.stdin.on("data", (d: Buffer) => {
const suggestionResult = suggestionManager.update(d);
if (previousSuggestionsRows > 0 && suggestionResult == "handled") {
process.stdin.on("keypress", (...keyPress: KeyPressEvent) => {
const press = keyPress[1];
const inputHandled = suggestionManager.update(press);
if (previousSuggestionsRows > 0 && inputHandled) {
term.noop();
} else if (suggestionResult != "fully-handled") {
term.write(inputModifier(d));
} else if (!inputHandled) {
term.write(press.sequence);
}
});
+1 -22
View File
@@ -2,10 +2,9 @@
// Licensed under the MIT License.
const ESC = "\u001B";
const CSI = "\u001B[";
const CSI = ESC + "[";
const OSC = "\u001B]";
const BEL = "\u0007";
const SS3 = "\u001BO";
export const IsTermOscPs = 6973;
const IS_OSC = OSC + IsTermOscPs + ";";
@@ -35,23 +34,3 @@ export const scrollDown = (count = 1) => CSI + count + "T";
export const eraseLinesBelow = (count = 1) => {
return [...Array(count).keys()].map(() => cursorNextLine + eraseLine).join("");
};
export const parseKeystroke = (b: Buffer): "up" | "down" | "tab" | "esc" | undefined => {
let s: string;
if (b[0] > 127 && b[1] === undefined) {
b[0] -= 128;
s = "\u001B" + String(b);
} else {
s = String(b);
}
if (s == ESC) {
return "esc";
} else if (s == CSI + "A" || s == SS3 + "A") {
return "up";
} else if (s == CSI + "B" || s == SS3 + "B") {
return "down";
} else if (s == "\t") {
return "tab";
}
};