Skip to content

Commit e1b6ee7

Browse files
authored
fix(multi-line): only submit on double-return at end of value (#569)
1 parent f87933f commit e1b6ee7

3 files changed

Lines changed: 87 additions & 20 deletions

File tree

.changeset/ten-vans-stand.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@clack/core": patch
3+
---
4+
5+
fix: only submit multi-line prompt when double-return happens at end of input.
6+
7+
Also fixes two minor things:
8+
9+
- Initial value is used as initial user input for multi-line prompts
10+
- Cursor is placed at the end when there is initial input

packages/core/src/prompts/multi-line.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default class MultiLinePrompt extends Prompt<string> {
7272
}
7373
const wasReturn = this.#lastKeyWasReturn;
7474
this.#lastKeyWasReturn = true;
75-
if (wasReturn) {
75+
if (wasReturn && this.cursor === this.userInput.length) {
7676
if (this.userInput[this.cursor - 1] === '\n') {
7777
this._setUserInput(
7878
this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)
@@ -87,11 +87,25 @@ export default class MultiLinePrompt extends Prompt<string> {
8787
}
8888

8989
constructor(opts: MultiLineOptions) {
90-
super(opts, false);
90+
const initialUserInput = opts.initialUserInput ?? opts.initialValue;
91+
92+
super(
93+
{
94+
...opts,
95+
initialUserInput,
96+
},
97+
false
98+
);
99+
100+
if (initialUserInput !== undefined) {
101+
this._cursor = initialUserInput.length;
102+
}
103+
91104
this.#showSubmit = opts.showSubmit ?? false;
92105

93106
this.on('key', (char, key) => {
94107
if (key?.name && cursorActions.has(key.name as CursorAction)) {
108+
this.#lastKeyWasReturn = false;
95109
this.#handleCursor(key.name as CursorAction);
96110
return;
97111
}

packages/core/test/prompts/multi-line.test.ts

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,34 @@ describe('MultiLinePrompt', () => {
5757
expect(result).to.equal('x');
5858
});
5959

60+
test('sets initial value from initialValue', async () => {
61+
const instance = new MultiLinePrompt({
62+
input,
63+
output,
64+
render: () => 'foo',
65+
initialValue: 'bleep bloop',
66+
});
67+
const resultPromise = instance.prompt();
68+
input.emit('keypress', '', { name: 'return' });
69+
input.emit('keypress', '', { name: 'return' });
70+
const result = await resultPromise;
71+
expect(result).to.equal('bleep bloop');
72+
});
73+
74+
test('sets initial value from initialUserInput', async () => {
75+
const instance = new MultiLinePrompt({
76+
input,
77+
output,
78+
render: () => 'foo',
79+
initialUserInput: 'bleep bloop',
80+
});
81+
const resultPromise = instance.prompt();
82+
input.emit('keypress', '', { name: 'return' });
83+
input.emit('keypress', '', { name: 'return' });
84+
const result = await resultPromise;
85+
expect(result).to.equal('bleep bloop');
86+
});
87+
6088
describe('cursor', () => {
6189
test('can get cursor', () => {
6290
const instance = new MultiLinePrompt({
@@ -70,17 +98,36 @@ describe('MultiLinePrompt', () => {
7098
});
7199

72100
describe('userInputWithCursor', () => {
73-
test('returns value on submit', () => {
101+
test('returns value on submit', async () => {
74102
const instance = new MultiLinePrompt({
75103
input,
76104
output,
77105
render: () => 'foo',
78106
});
79-
instance.prompt();
107+
const resultPromise = instance.prompt();
80108
input.emit('keypress', 'x', { name: 'x' });
81109
input.emit('keypress', '', { name: 'return' });
82110
input.emit('keypress', '', { name: 'return' });
83111
expect(instance.userInputWithCursor).to.equal('x');
112+
const value = await resultPromise;
113+
expect(value).to.equal('x');
114+
});
115+
116+
test('double return does not submit mid-value', async () => {
117+
const instance = new MultiLinePrompt({
118+
input,
119+
output,
120+
render: () => 'foo',
121+
});
122+
const resultPromise = instance.prompt();
123+
input.emit('keypress', 'x', { name: 'x' });
124+
input.emit('keypress', 'y', { name: 'y' });
125+
input.emit('keypress', '', { name: 'left' });
126+
input.emit('keypress', '', { name: 'return' });
127+
input.emit('keypress', '', { name: 'return' });
128+
expect(instance.userInput).to.equal('x\n\ny');
129+
input.emit('keypress', '', { name: 'escape' });
130+
await resultPromise;
84131
});
85132

86133
test('highlights cursor position', () => {
@@ -257,10 +304,9 @@ describe('MultiLinePrompt', () => {
257304
input.emit('keypress', 'x', { name: 'x' });
258305
input.emit('keypress', '', { name: 'left' });
259306
input.emit('keypress', '', { name: 'backspace' });
260-
input.emit('keypress', '', { name: 'return' });
261-
input.emit('keypress', '', { name: 'return' });
262-
const result = await resultPromise;
263-
expect(result).to.equal('x');
307+
expect(instance.userInput).to.equal('x');
308+
input.emit('keypress', '', { name: 'escape' });
309+
await resultPromise;
264310
});
265311

266312
test('left moves left until start', async () => {
@@ -274,10 +320,9 @@ describe('MultiLinePrompt', () => {
274320
input.emit('keypress', '', { name: 'left' });
275321
input.emit('keypress', '', { name: 'left' });
276322
input.emit('keypress', 'y', { name: 'y' });
277-
input.emit('keypress', '', { name: 'return' });
278-
input.emit('keypress', '', { name: 'return' });
279-
const result = await resultPromise;
280-
expect(result).to.equal('yx');
323+
expect(instance.userInput).to.equal('yx');
324+
input.emit('keypress', '', { name: 'escape' });
325+
await resultPromise;
281326
});
282327

283328
test('right moves right until end', async () => {
@@ -312,10 +357,9 @@ describe('MultiLinePrompt', () => {
312357
input.emit('keypress', '', { name: 'left' });
313358
input.emit('keypress', '', { name: 'left' });
314359
input.emit('keypress', 'z', { name: 'z' });
315-
input.emit('keypress', '', { name: 'return' });
316-
input.emit('keypress', '', { name: 'return' });
317-
const result = await resultPromise;
318-
expect(result).to.equal('xz\ny');
360+
expect(instance.userInput).to.equal('xz\ny');
361+
input.emit('keypress', '', { name: 'escape' });
362+
await resultPromise;
319363
});
320364

321365
test('right moves across lines', async () => {
@@ -351,10 +395,9 @@ describe('MultiLinePrompt', () => {
351395
input.emit('keypress', 'y', { name: 'y' });
352396
input.emit('keypress', '', { name: 'up' });
353397
input.emit('keypress', 'z', { name: 'z' });
354-
input.emit('keypress', '', { name: 'return' });
355-
input.emit('keypress', '', { name: 'return' });
356-
const result = await resultPromise;
357-
expect(result).to.equal('xz\ny');
398+
expect(instance.userInput).to.equal('xz\ny');
399+
input.emit('keypress', '', { name: 'escape' });
400+
await resultPromise;
358401
});
359402

360403
test('down moves down a line', async () => {

0 commit comments

Comments
 (0)