mirror of
https://github.com/nodejs/node.git
synced 2025-05-05 13:43:55 +00:00

PR-URL: https://github.com/nodejs/node/pull/38466 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
97 lines
2.5 KiB
JavaScript
97 lines
2.5 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
SafeStringIterator,
|
|
Symbol,
|
|
} = primordials;
|
|
|
|
const {
|
|
charLengthAt,
|
|
CSI,
|
|
emitKeys,
|
|
} = require('internal/readline/utils');
|
|
|
|
const { clearTimeout, setTimeout } = require('timers');
|
|
const {
|
|
kEscape,
|
|
} = CSI;
|
|
|
|
const { StringDecoder } = require('string_decoder');
|
|
|
|
const KEYPRESS_DECODER = Symbol('keypress-decoder');
|
|
const ESCAPE_DECODER = Symbol('escape-decoder');
|
|
|
|
// GNU readline library - keyseq-timeout is 500ms (default)
|
|
const ESCAPE_CODE_TIMEOUT = 500;
|
|
|
|
/**
|
|
* accepts a readable Stream instance and makes it emit "keypress" events
|
|
*/
|
|
|
|
function emitKeypressEvents(stream, iface = {}) {
|
|
if (stream[KEYPRESS_DECODER]) return;
|
|
|
|
stream[KEYPRESS_DECODER] = new StringDecoder('utf8');
|
|
|
|
stream[ESCAPE_DECODER] = emitKeys(stream);
|
|
stream[ESCAPE_DECODER].next();
|
|
|
|
const triggerEscape = () => stream[ESCAPE_DECODER].next('');
|
|
const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface;
|
|
let timeoutId;
|
|
|
|
function onData(input) {
|
|
if (stream.listenerCount('keypress') > 0) {
|
|
const string = stream[KEYPRESS_DECODER].write(input);
|
|
if (string) {
|
|
clearTimeout(timeoutId);
|
|
|
|
// This supports characters of length 2.
|
|
iface._sawKeyPress = charLengthAt(string, 0) === string.length;
|
|
iface.isCompletionEnabled = false;
|
|
|
|
let length = 0;
|
|
for (const character of new SafeStringIterator(string)) {
|
|
length += character.length;
|
|
if (length === string.length) {
|
|
iface.isCompletionEnabled = true;
|
|
}
|
|
|
|
try {
|
|
stream[ESCAPE_DECODER].next(character);
|
|
// Escape letter at the tail position
|
|
if (length === string.length && character === kEscape) {
|
|
timeoutId = setTimeout(triggerEscape, escapeCodeTimeout);
|
|
}
|
|
} catch (err) {
|
|
// If the generator throws (it could happen in the `keypress`
|
|
// event), we need to restart it.
|
|
stream[ESCAPE_DECODER] = emitKeys(stream);
|
|
stream[ESCAPE_DECODER].next();
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Nobody's watching anyway
|
|
stream.removeListener('data', onData);
|
|
stream.on('newListener', onNewListener);
|
|
}
|
|
}
|
|
|
|
function onNewListener(event) {
|
|
if (event === 'keypress') {
|
|
stream.on('data', onData);
|
|
stream.removeListener('newListener', onNewListener);
|
|
}
|
|
}
|
|
|
|
if (stream.listenerCount('keypress') > 0) {
|
|
stream.on('data', onData);
|
|
} else {
|
|
stream.on('newListener', onNewListener);
|
|
}
|
|
}
|
|
|
|
module.exports = emitKeypressEvents;
|