Skip to content

Commit

Permalink
Chore (core): Fix exit not restoring the cursor. (Fix #1286, Fix #1114)
Browse files Browse the repository at this point in the history
  • Loading branch information
SBoudrias committed Sep 9, 2023
1 parent 7ad8f19 commit 5c48552
Show file tree
Hide file tree
Showing 21 changed files with 27 additions and 33 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
31 changes: 11 additions & 20 deletions packages/core/core.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,27 @@ describe('createPrompt()', () => {
}
});

return config.message;
return `${config.message} ${ansiEscapes.cursorHide}`;
};

const prompt = createPrompt(Prompt);
const { answer, events } = await render(prompt, { message: 'Question' });
const { answer, events, getFullOutput } = await render(prompt, {
message: 'Question',
});

answer.cancel();
events.keypress('enter');

await expect(answer).rejects.toThrowErrorMatchingInlineSnapshot(
'"Prompt was canceled"',
);

const output = getFullOutput();
expect(output).toContain(ansiEscapes.cursorHide);
expect(output).toContain(ansiEscapes.cursorShow);
expect(output.lastIndexOf(ansiEscapes.cursorHide)).toBeLessThan(
output.lastIndexOf(ansiEscapes.cursorShow),
);
});

it('allow cleaning the prompt after completion', async () => {
Expand Down Expand Up @@ -448,24 +457,6 @@ describe('Error handling', () => {
'"[Inquirer] Hook functions can only be called from within a prompt"',
);
});

it('cleanup prompt on exit', async () => {
const Prompt = () => `Question ${ansiEscapes.cursorHide}`;

const prompt = createPrompt(Prompt);
const { answer, getFullOutput } = await render(prompt, { message: 'Question' });

process.emit('SIGINT');

await expect(answer).rejects.toMatchInlineSnapshot(
'[Error: User force closed the prompt with CTRL+C]',
);

const output = getFullOutput();
expect(output.lastIndexOf(ansiEscapes.cursorHide)).toBeLessThan(
output.lastIndexOf(ansiEscapes.cursorShow),
);
});
});

describe('Separator', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"figures": "^3.2.0",
"mute-stream": "^1.0.0",
"run-async": "^3.0.0",
"signal-exit": "^4.1.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^6.2.0"
},
Expand Down
20 changes: 7 additions & 13 deletions packages/core/src/lib/create-prompt.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as readline from 'node:readline';
import { CancelablePromise, type Prompt } from '@inquirer/type';
import MuteStream from 'mute-stream';
import { onExit as onSignalExit } from 'signal-exit';
import ScreenManager from './screen-manager.mjs';
import type { InquirerReadline } from './read-line.type.mjs';
import { withHooks, effectScheduler } from './hook-engine.mjs';
Expand Down Expand Up @@ -57,6 +58,11 @@ export function createPrompt<Value, Config extends AsyncPromptConfig>(
screen.checkCursorPos();
}

const removeExitListener = onSignalExit((code, signal) => {
onExit();
reject(new Error(`User force closed the prompt with ${code} ${signal}`));
});

function onExit() {
try {
store.hooksCleanup.forEach((cleanFn) => {
Expand All @@ -73,7 +79,7 @@ export function createPrompt<Value, Config extends AsyncPromptConfig>(
}
screen.done();

process.removeListener('SIGINT', onForceExit);
removeExitListener();
store.rl.input.removeListener('keypress', checkCursorPos);
}

Expand All @@ -82,18 +88,6 @@ export function createPrompt<Value, Config extends AsyncPromptConfig>(
reject(new Error('Prompt was canceled'));
};

let shouldHandleExit = true;
function onForceExit() {
if (shouldHandleExit) {
shouldHandleExit = false;
onExit();
reject(new Error('User force closed the prompt with CTRL+C'));
}
}

// Handle cleanup on force exit. Main reason is so we restore the cursor if a prompt hide it.
process.on('SIGINT', onForceExit);

function done(value: Value) {
// Delay execution to let time to the hookCleanup functions to registers.
setImmediate(() => {
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ __metadata:
figures: ^3.2.0
mute-stream: ^1.0.0
run-async: ^3.0.0
signal-exit: ^4.1.0
strip-ansi: ^6.0.1
wrap-ansi: ^6.2.0
languageName: unknown
Expand Down Expand Up @@ -7314,6 +7315,13 @@ __metadata:
languageName: node
linkType: hard

"signal-exit@npm:^4.1.0":
version: 4.1.0
resolution: "signal-exit@npm:4.1.0"
checksum: 64c757b498cb8629ffa5f75485340594d2f8189e9b08700e69199069c8e3070fb3e255f7ab873c05dc0b3cec412aea7402e10a5990cb6a050bd33ba062a6c549
languageName: node
linkType: hard

"sigstore@npm:^1.3.0, sigstore@npm:^1.4.0":
version: 1.8.0
resolution: "sigstore@npm:1.8.0"
Expand Down

0 comments on commit 5c48552

Please sign in to comment.