Skip to content

Module "node:readline" doesn't address escape sequence of cursor position report from terminal, causing part of it to flow into input stream as regular string #57602

Description

@UnluckyNinja

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions