Version
22.14.0
Platform
Microsoft Windows NT 10.0.19045.0 x64
Subsystem
node:readline
What steps will reproduce the bug?
You can reproduce it by running the following script in a terminal:
import * as readline from 'node:readline';
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const dumpedKeys = []
process.stdin.on('keypress', (s, key)=>{
dumpedKeys.push(key)
})
process.stdout.write('You will see an R here -->')
process.stdout.write('\x1b[6n');
setTimeout(()=>{
process.stdout.write('\nEmitted keypress events: ' + JSON.stringify(dumpedKeys, null, 2))
dumpedKeys.splice(0)
process.stdout.write('\nBut not here \n-->')
process.stdout.write('\x1b[6n');
setTimeout(()=>{
process.stdout.write('\nEmitted keypress events: ' + JSON.stringify(dumpedKeys, null, 2))
rl.close()
}, 100)
}, 100)
How often does it reproduce? Is there a required condition?
I have tested on Windows terminal & msys2, and identified the problem in source code:
|
// ANSI escape sequence |
|
let code = ch; |
|
let modifier = 0; |
|
|
|
if (ch === 'O') { |
|
// ESC O letter |
|
// ESC O modifier letter |
|
s += (ch = yield); |
|
|
|
if (ch >= '0' && ch <= '9') { |
|
modifier = (ch >> 0) - 1; |
|
s += (ch = yield); |
|
} |
|
|
|
code += ch; |
|
} else if (ch === '[') { |
|
// ESC [ letter |
|
// ESC [ modifier letter |
|
// ESC [ [ modifier letter |
|
// ESC [ [ num char |
|
s += (ch = yield); |
|
|
|
if (ch === '[') { |
|
// \x1b[[A |
|
// ^--- escape codes might have a second bracket |
|
code += ch; |
|
s += (ch = yield); |
|
} |
|
|
|
/* |
|
* Here and later we try to buffer just enough data to get |
|
* a complete ascii sequence. |
|
* |
|
* We have basically two classes of ascii characters to process: |
|
* |
|
* |
|
* 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 } |
|
* |
|
* This particular example is featuring Ctrl+F12 in xterm. |
|
* |
|
* - `;5` part is optional, e.g. it could be `\x1b[24~` |
|
* - first part can contain one or two digits |
|
* - there is also special case when there can be 3 digits |
|
* but without modifier. They are the case of paste bracket mode |
|
* |
|
* So the generic regexp is like /^(?:\d\d?(;\d)?[~^$]|\d{3}~)$/ |
|
* |
|
* |
|
* 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 } |
|
* |
|
* This particular example is featuring Ctrl+Home in xterm. |
|
* |
|
* - `1;5` part is optional, e.g. it could be `\x1b[H` |
|
* - `1;` part is optional, e.g. it could be `\x1b[5H` |
|
* |
|
* So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/ |
|
* |
|
*/ |
|
const cmdStart = s.length - 1; |
|
|
|
// Skip one or two leading digits |
|
if (ch >= '0' && ch <= '9') { |
|
s += (ch = yield); |
|
|
|
if (ch >= '0' && ch <= '9') { |
|
s += (ch = yield); |
|
|
|
if (ch >= '0' && ch <= '9') { |
|
s += (ch = yield); |
|
} |
|
} |
|
} |
|
|
|
// skip modifier |
|
if (ch === ';') { |
|
s += (ch = yield); |
|
|
|
if (ch >= '0' && ch <= '9') { |
|
s += yield; |
|
} |
|
} |
, where it processes the escape sequence from input stream.
What is the expected behavior? Why is that the expected behavior?
The reported back string like "\u001b[12;34R" (means row 12, column 34) should be considered together, avoid part of it flowing into normal input stream as regular string. It will be best if it can support arbitray longer valid values, for example "\u001b[1234;5678R" (though in real life hardly will any terminal be such large)
What do you see instead?
If the cursor position makes the report string longer than 7 chars (like "\u001b[12;34R"), the part after the 7th char will flow into normal input stream causing it to appear on screen and mess up user input.
Additional information
Fixing it would be helpful for developing interactive console programs that needs current cursor position on screen after writing to output, which can be used as a workaround for nodejs/help#49 (comment) until an official api is implemented.
Version
22.14.0
Platform
Subsystem
node:readline
What steps will reproduce the bug?
You can reproduce it by running the following script in a terminal:
How often does it reproduce? Is there a required condition?
I have tested on Windows terminal & msys2, and identified the problem in source code:
node/lib/internal/readline/utils.js
Lines 108 to 188 in c3b6f94
What is the expected behavior? Why is that the expected behavior?
The reported back string like "\u001b[12;34R" (means row 12, column 34) should be considered together, avoid part of it flowing into normal input stream as regular string. It will be best if it can support arbitray longer valid values, for example "\u001b[1234;5678R" (though in real life hardly will any terminal be such large)
What do you see instead?
If the cursor position makes the report string longer than 7 chars (like "\u001b[12;34R"), the part after the 7th char will flow into normal input stream causing it to appear on screen and mess up user input.
Additional information
Fixing it would be helpful for developing interactive console programs that needs current cursor position on screen after writing to output, which can be used as a workaround for nodejs/help#49 (comment) until an official api is implemented.