Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/ten-vans-stand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@clack/core": patch
---

fix: only submit multi-line prompt when double-return happens at end of input.

Also fixes two minor things:

- Initial value is used as initial user input for multi-line prompts
- Cursor is placed at the end when there is initial input
18 changes: 16 additions & 2 deletions packages/core/src/prompts/multi-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default class MultiLinePrompt extends Prompt<string> {
}
const wasReturn = this.#lastKeyWasReturn;
this.#lastKeyWasReturn = true;
if (wasReturn) {
if (wasReturn && this.cursor === this.userInput.length) {
if (this.userInput[this.cursor - 1] === '\n') {
this._setUserInput(
this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)
Expand All @@ -87,11 +87,25 @@ export default class MultiLinePrompt extends Prompt<string> {
}

constructor(opts: MultiLineOptions) {
super(opts, false);
const initialUserInput = opts.initialUserInput ?? opts.initialValue;

super(
{
...opts,
initialUserInput,
},
false
);

if (initialUserInput !== undefined) {
this._cursor = initialUserInput.length;
}

this.#showSubmit = opts.showSubmit ?? false;

this.on('key', (char, key) => {
if (key?.name && cursorActions.has(key.name as CursorAction)) {
this.#lastKeyWasReturn = false;
this.#handleCursor(key.name as CursorAction);
return;
}
Expand Down
79 changes: 61 additions & 18 deletions packages/core/test/prompts/multi-line.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ describe('MultiLinePrompt', () => {
expect(result).to.equal('x');
});

test('sets initial value from initialValue', async () => {
const instance = new MultiLinePrompt({
input,
output,
render: () => 'foo',
initialValue: 'bleep bloop',
});
const resultPromise = instance.prompt();
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('bleep bloop');
});

test('sets initial value from initialUserInput', async () => {
const instance = new MultiLinePrompt({
input,
output,
render: () => 'foo',
initialUserInput: 'bleep bloop',
});
const resultPromise = instance.prompt();
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('bleep bloop');
});

describe('cursor', () => {
test('can get cursor', () => {
const instance = new MultiLinePrompt({
Expand All @@ -70,17 +98,36 @@ describe('MultiLinePrompt', () => {
});

describe('userInputWithCursor', () => {
test('returns value on submit', () => {
test('returns value on submit', async () => {
const instance = new MultiLinePrompt({
input,
output,
render: () => 'foo',
});
instance.prompt();
const resultPromise = instance.prompt();
input.emit('keypress', 'x', { name: 'x' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
expect(instance.userInputWithCursor).to.equal('x');
const value = await resultPromise;
expect(value).to.equal('x');
});

test('double return does not submit mid-value', async () => {
const instance = new MultiLinePrompt({
input,
output,
render: () => 'foo',
});
const resultPromise = instance.prompt();
input.emit('keypress', 'x', { name: 'x' });
input.emit('keypress', 'y', { name: 'y' });
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
expect(instance.userInput).to.equal('x\n\ny');
input.emit('keypress', '', { name: 'escape' });
await resultPromise;
});

test('highlights cursor position', () => {
Expand Down Expand Up @@ -257,10 +304,9 @@ describe('MultiLinePrompt', () => {
input.emit('keypress', 'x', { name: 'x' });
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', '', { name: 'backspace' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('x');
expect(instance.userInput).to.equal('x');
input.emit('keypress', '', { name: 'escape' });
await resultPromise;
});

test('left moves left until start', async () => {
Expand All @@ -274,10 +320,9 @@ describe('MultiLinePrompt', () => {
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', 'y', { name: 'y' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('yx');
expect(instance.userInput).to.equal('yx');
input.emit('keypress', '', { name: 'escape' });
await resultPromise;
});

test('right moves right until end', async () => {
Expand Down Expand Up @@ -312,10 +357,9 @@ describe('MultiLinePrompt', () => {
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', '', { name: 'left' });
input.emit('keypress', 'z', { name: 'z' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('xz\ny');
expect(instance.userInput).to.equal('xz\ny');
input.emit('keypress', '', { name: 'escape' });
await resultPromise;
});

test('right moves across lines', async () => {
Expand Down Expand Up @@ -351,10 +395,9 @@ describe('MultiLinePrompt', () => {
input.emit('keypress', 'y', { name: 'y' });
input.emit('keypress', '', { name: 'up' });
input.emit('keypress', 'z', { name: 'z' });
input.emit('keypress', '', { name: 'return' });
input.emit('keypress', '', { name: 'return' });
const result = await resultPromise;
expect(result).to.equal('xz\ny');
expect(instance.userInput).to.equal('xz\ny');
input.emit('keypress', '', { name: 'escape' });
await resultPromise;
});

test('down moves down a line', async () => {
Expand Down
Loading