Skip to content

Commit e81bf43

Browse files
committed
test: fix lint issues in readline tab completion regression
1 parent 35c1d6b commit e81bf43

File tree

1 file changed

+174
-166
lines changed

1 file changed

+174
-166
lines changed

test/parallel/test-readline-tab-complete.js

Lines changed: 174 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -140,170 +140,178 @@ if (process.env.TERM === 'dumb') {
140140
}
141141

142142
{
143-
class VirtualScreen {
144-
constructor() {
145-
this.rows = [[]];
146-
this.row = 0;
147-
this.col = 0;
148-
}
149-
150-
ensureRow(row) {
151-
while (this.rows.length <= row) this.rows.push([]);
152-
}
153-
154-
setChar(row, col, ch) {
155-
this.ensureRow(row);
156-
const target = this.rows[row];
157-
while (target.length <= col) target.push(' ');
158-
target[col] = ch;
159-
}
160-
161-
clearLineRight() {
162-
this.ensureRow(this.row);
163-
const target = this.rows[this.row];
164-
if (this.col < target.length) {
165-
target.length = this.col;
166-
}
167-
}
168-
169-
clearFromCursor() {
170-
this.clearLineRight();
171-
if (this.row + 1 < this.rows.length) {
172-
this.rows.length = this.row + 1;
173-
}
174-
}
175-
176-
moveCursor(dx, dy) {
177-
this.row = Math.max(0, this.row + dy);
178-
this.ensureRow(this.row);
179-
this.col = Math.max(0, this.col + dx);
180-
}
181-
182-
handleEscape(params, code) {
183-
switch (code) {
184-
case 'A': // Cursor Up
185-
this.moveCursor(0, -(Number(params) || 1));
186-
break;
187-
case 'B': // Cursor Down
188-
this.moveCursor(0, Number(params) || 1);
189-
break;
190-
case 'C': // Cursor Forward
191-
this.moveCursor(Number(params) || 1, 0);
192-
break;
193-
case 'D': // Cursor Backward
194-
this.moveCursor(-(Number(params) || 1), 0);
195-
break;
196-
case 'G': // Cursor Horizontal Absolute
197-
this.col = Math.max(0, (Number(params) || 1) - 1);
198-
break;
199-
case 'H':
200-
case 'f': { // Cursor Position
201-
const [row, col] = params.split(';').map((n) => Number(n) || 1);
202-
this.row = Math.max(0, row - 1);
203-
this.col = Math.max(0, (col ?? 1) - 1);
204-
this.ensureRow(this.row);
205-
break;
206-
}
207-
case 'J':
208-
this.clearFromCursor();
209-
break;
210-
case 'K':
211-
this.clearLineRight();
212-
break;
213-
default:
214-
break;
215-
}
216-
}
217-
218-
write(chunk) {
219-
for (let i = 0; i < chunk.length; i++) {
220-
const ch = chunk[i];
221-
if (ch === '\r') {
222-
this.col = 0;
223-
continue;
224-
}
225-
if (ch === '\n') {
226-
this.row++;
227-
this.col = 0;
228-
this.ensureRow(this.row);
229-
continue;
230-
}
231-
if (ch === '\u001b' && chunk[i + 1] === '[') {
232-
const match = /^\u001b\[([0-9;]*)([A-Za-z])/.exec(chunk.slice(i));
233-
if (match) {
234-
this.handleEscape(match[1], match[2]);
235-
i += match[0].length - 1;
236-
continue;
237-
}
238-
}
239-
this.setChar(this.row, this.col, ch);
240-
this.col++;
241-
}
242-
}
243-
244-
getLines() {
245-
return this.rows.map((row) => row.join('').trimEnd());
246-
}
247-
}
248-
249-
class FakeTTY extends EventEmitter {
250-
columns = 80;
251-
rows = 24;
252-
isTTY = true;
253-
254-
constructor(screen) {
255-
super();
256-
this.screen = screen;
257-
}
258-
259-
write(data) {
260-
this.screen.write(data);
261-
return true;
262-
}
263-
264-
resume() {}
265-
266-
pause() {}
267-
268-
end() {}
269-
270-
setRawMode(mode) {
271-
this.isRaw = mode;
272-
}
273-
}
274-
275-
const screen = new VirtualScreen();
276-
const fi = new FakeTTY(screen);
277-
278-
const rli = new readline.Interface({
279-
input: fi,
280-
output: fi,
281-
terminal: true,
282-
completer: (line) => [['foobar', 'foobaz'], line],
283-
});
284-
285-
const promptLines = ['multiline', 'prompt', 'eats', 'output', '> '];
286-
rli.setPrompt(promptLines.join('\n'));
287-
rli.prompt();
288-
289-
['f', 'o', 'o', '\t', '\t'].forEach((ch) => fi.emit('data', ch));
290-
291-
const display = screen.getLines();
292-
293-
assert.strictEqual(display[0], 'multiline');
294-
assert.strictEqual(display[1], 'prompt');
295-
assert.strictEqual(display[2], 'eats');
296-
assert.strictEqual(display[3], 'output');
297-
298-
const inputLineIndex = 4;
299-
assert.ok(
300-
display[inputLineIndex].includes('> fooba'),
301-
'prompt line should keep completed input',
302-
);
303-
304-
const completionLineExists =
305-
display.some((l) => l.includes('foobar') && l.includes('foobaz'));
306-
assert.ok(completionLineExists, 'completion list should be visible');
307-
308-
rli.close();
143+
class VirtualScreen {
144+
constructor() {
145+
this.rows = [[]];
146+
this.row = 0;
147+
this.col = 0;
148+
}
149+
150+
ensureRow(row) {
151+
while (this.rows.length <= row) this.rows.push([]);
152+
}
153+
154+
setChar(row, col, ch) {
155+
this.ensureRow(row);
156+
const target = this.rows[row];
157+
while (target.length <= col) target.push(' ');
158+
target[col] = ch;
159+
}
160+
161+
clearLineRight() {
162+
this.ensureRow(this.row);
163+
const target = this.rows[this.row];
164+
if (this.col < target.length) {
165+
target.length = this.col;
166+
}
167+
}
168+
169+
clearFromCursor() {
170+
this.clearLineRight();
171+
if (this.row + 1 < this.rows.length) {
172+
this.rows.length = this.row + 1;
173+
}
174+
}
175+
176+
moveCursor(dx, dy) {
177+
this.row = Math.max(0, this.row + dy);
178+
this.ensureRow(this.row);
179+
this.col = Math.max(0, this.col + dx);
180+
}
181+
182+
handleEscape(params, code) {
183+
switch (code) {
184+
case 'A': // Cursor Up
185+
this.moveCursor(0, -(Number(params) || 1));
186+
break;
187+
case 'B': // Cursor Down
188+
this.moveCursor(0, Number(params) || 1);
189+
break;
190+
case 'C': // Cursor Forward
191+
this.moveCursor(Number(params) || 1, 0);
192+
break;
193+
case 'D': // Cursor Backward
194+
this.moveCursor(-(Number(params) || 1), 0);
195+
break;
196+
case 'G': // Cursor Horizontal Absolute
197+
this.col = Math.max(0, (Number(params) || 1) - 1);
198+
break;
199+
case 'H':
200+
case 'f': { // Cursor Position
201+
const [row, col] = params.split(';').map((n) => Number(n) || 1);
202+
this.row = Math.max(0, row - 1);
203+
this.col = Math.max(0, (col ?? 1) - 1);
204+
this.ensureRow(this.row);
205+
break;
206+
}
207+
case 'J':
208+
this.clearFromCursor();
209+
break;
210+
case 'K':
211+
this.clearLineRight();
212+
break;
213+
default:
214+
break;
215+
}
216+
}
217+
218+
write(chunk) {
219+
for (let i = 0; i < chunk.length; i++) {
220+
const ch = chunk[i];
221+
if (ch === '\r') {
222+
this.col = 0;
223+
continue;
224+
}
225+
if (ch === '\n') {
226+
this.row++;
227+
this.col = 0;
228+
this.ensureRow(this.row);
229+
continue;
230+
}
231+
if (ch === '\u001b' && chunk[i + 1] === '[') {
232+
let j = i + 2;
233+
let params = '';
234+
while (j < chunk.length) {
235+
const code = chunk[j];
236+
if ((code >= '0' && code <= '9') || code === ';') {
237+
params += code;
238+
j++;
239+
continue;
240+
}
241+
this.handleEscape(params, code);
242+
i = j;
243+
break;
244+
}
245+
continue;
246+
}
247+
this.setChar(this.row, this.col, ch);
248+
this.col++;
249+
}
250+
}
251+
252+
getLines() {
253+
return this.rows.map((row) => row.join('').trimEnd());
254+
}
255+
}
256+
257+
class FakeTTY extends EventEmitter {
258+
columns = 80;
259+
rows = 24;
260+
isTTY = true;
261+
262+
constructor(screen) {
263+
super();
264+
this.screen = screen;
265+
}
266+
267+
write(data) {
268+
this.screen.write(data);
269+
return true;
270+
}
271+
272+
resume() {}
273+
274+
pause() {}
275+
276+
end() {}
277+
278+
setRawMode(mode) {
279+
this.isRaw = mode;
280+
}
281+
}
282+
283+
const screen = new VirtualScreen();
284+
const fi = new FakeTTY(screen);
285+
286+
const rli = new readline.Interface({
287+
input: fi,
288+
output: fi,
289+
terminal: true,
290+
completer: (line) => [['foobar', 'foobaz'], line],
291+
});
292+
293+
const promptLines = ['multiline', 'prompt', 'eats', 'output', '> '];
294+
rli.setPrompt(promptLines.join('\n'));
295+
rli.prompt();
296+
297+
['f', 'o', 'o', '\t', '\t'].forEach((ch) => fi.emit('data', ch));
298+
299+
const display = screen.getLines();
300+
301+
assert.strictEqual(display[0], 'multiline');
302+
assert.strictEqual(display[1], 'prompt');
303+
assert.strictEqual(display[2], 'eats');
304+
assert.strictEqual(display[3], 'output');
305+
306+
const inputLineIndex = 4;
307+
assert.ok(
308+
display[inputLineIndex].includes('> fooba'),
309+
'prompt line should keep completed input',
310+
);
311+
312+
const completionLineExists =
313+
display.some((l) => l.includes('foobar') && l.includes('foobaz'));
314+
assert.ok(completionLineExists, 'completion list should be visible');
315+
316+
rli.close();
309317
}

0 commit comments

Comments
 (0)