From eb43e1910fc40811a9ef44afdea24f2da4a8a4c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 19 Apr 2024 05:46:43 -0700 Subject: [PATCH] Tidy up asserting when command executes, wait for throttle and don't force a sync --- .../commandDetection/promptInputModel.ts | 8 +- .../commandDetection/promptInputModel.test.ts | 112 +++++++++--------- 2 files changed, 57 insertions(+), 63 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts index 3379e2ffb3d..6aee9b230b6 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts @@ -82,6 +82,9 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { getCombinedString(): string { const value = this._value.replaceAll('\n', '\u23CE'); + if (this._cursorIndex === -1) { + return value; + } let result = `${value.substring(0, this.cursorIndex)}|`; if (this.ghostTextIndex !== -1) { result += `${value.substring(this.cursorIndex, this.ghostTextIndex)}[`; @@ -111,6 +114,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { } this._state = PromptInputState.Execute; + this._cursorIndex = -1; this._onDidFinishInput.fire(); } @@ -120,10 +124,6 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { @throttle(0) private _sync() { - this._syncNow(); - } - - protected _syncNow() { if (this._state !== PromptInputState.Input) { return; } diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index d7607739ac7..9284fdd2067 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -14,16 +14,10 @@ import { Terminal } from '@xterm/headless'; import { strictEqual } from 'assert'; import { timeout } from 'vs/base/common/async'; -class TestPromptInputModel extends PromptInputModel { - forceSync() { - this._syncNow(); - } -} - suite('PromptInputModel', () => { const store = ensureNoDisposablesAreLeakedInTestSuite(); - let promptInputModel: TestPromptInputModel; + let promptInputModel: PromptInputModel; let xterm: Terminal; let onCommandStart: Emitter; let onCommandExecuted: Emitter; @@ -40,13 +34,13 @@ suite('PromptInputModel', () => { onCommandExecuted.fire(null!); } - function assertPromptInput(valueWithCursor: string) { - if (!valueWithCursor.includes('|')) { + async function assertPromptInput(valueWithCursor: string) { + await timeout(0); + + if (promptInputModel.cursorIndex !== -1 && !valueWithCursor.includes('|')) { throw new Error('assertPromptInput must contain | character'); } - promptInputModel.forceSync(); - const actualValueWithCursor = promptInputModel.getCombinedString(); strictEqual( actualValueWithCursor, @@ -64,123 +58,123 @@ suite('PromptInputModel', () => { xterm = store.add(new Terminal({ allowProposedApi: true })); onCommandStart = store.add(new Emitter()); onCommandExecuted = store.add(new Emitter()); - promptInputModel = store.add(new TestPromptInputModel(xterm, onCommandStart.event, onCommandExecuted.event, new NullLogService)); + promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandExecuted.event, new NullLogService)); }); test('basic input and execute', async () => { await writePromise('$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await writePromise('foo bar'); - assertPromptInput('foo bar|'); + await assertPromptInput('foo bar|'); await writePromise('\r\n'); fireCommandExecuted(); - assertPromptInput('foo bar|'); + await assertPromptInput('foo bar'); await writePromise('(command output)\r\n$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); }); test('cursor navigation', async () => { await writePromise('$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await writePromise('foo bar'); - assertPromptInput('foo bar|'); + await assertPromptInput('foo bar|'); await writePromise('\x1b[3D'); - assertPromptInput('foo |bar'); + await assertPromptInput('foo |bar'); await writePromise('\x1b[4D'); - assertPromptInput('|foo bar'); + await assertPromptInput('|foo bar'); await writePromise('\x1b[3C'); - assertPromptInput('foo| bar'); + await assertPromptInput('foo| bar'); await writePromise('\x1b[4C'); - assertPromptInput('foo bar|'); + await assertPromptInput('foo bar|'); await writePromise('\x1b[D'); - assertPromptInput('foo ba|r'); + await assertPromptInput('foo ba|r'); await writePromise('\x1b[C'); - assertPromptInput('foo bar|'); + await assertPromptInput('foo bar|'); }); test('ghost text', async () => { await writePromise('$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await writePromise('foo\x1b[2m bar\x1b[0m\x1b[4D'); - assertPromptInput('foo|[ bar]'); + await assertPromptInput('foo|[ bar]'); await writePromise('\x1b[2D'); - assertPromptInput('f|oo[ bar]'); + await assertPromptInput('f|oo[ bar]'); }); test('wide input (Korean)', async () => { await writePromise('$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await writePromise('안영'); - assertPromptInput('안영|'); + await assertPromptInput('안영|'); await writePromise('\r\n컴퓨터'); - assertPromptInput('안영\n컴퓨터|'); + await assertPromptInput('안영\n컴퓨터|'); await writePromise('\r\n사람'); - assertPromptInput('안영\n컴퓨터\n사람|'); + await assertPromptInput('안영\n컴퓨터\n사람|'); await writePromise('\x1b[G'); - assertPromptInput('안영\n컴퓨터\n|사람'); + await assertPromptInput('안영\n컴퓨터\n|사람'); await writePromise('\x1b[A'); - assertPromptInput('안영\n|컴퓨터\n사람'); + await assertPromptInput('안영\n|컴퓨터\n사람'); await writePromise('\x1b[4C'); - assertPromptInput('안영\n컴퓨|터\n사람'); + await assertPromptInput('안영\n컴퓨|터\n사람'); await writePromise('\x1b[1;4H'); - assertPromptInput('안|영\n컴퓨터\n사람'); + await assertPromptInput('안|영\n컴퓨터\n사람'); await writePromise('\x1b[D'); - assertPromptInput('|안영\n컴퓨터\n사람'); + await assertPromptInput('|안영\n컴퓨터\n사람'); }); test('emoji input', async () => { await writePromise('$ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await writePromise('✌️👍'); - assertPromptInput('✌️👍|'); + await assertPromptInput('✌️👍|'); await writePromise('\r\n😎😕😅'); - assertPromptInput('✌️👍\n😎😕😅|'); + await assertPromptInput('✌️👍\n😎😕😅|'); await writePromise('\r\n🤔🤷😩'); - assertPromptInput('✌️👍\n😎😕😅\n🤔🤷😩|'); + await assertPromptInput('✌️👍\n😎😕😅\n🤔🤷😩|'); await writePromise('\x1b[G'); - assertPromptInput('✌️👍\n😎😕😅\n|🤔🤷😩'); + await assertPromptInput('✌️👍\n😎😕😅\n|🤔🤷😩'); await writePromise('\x1b[A'); - assertPromptInput('✌️👍\n|😎😕😅\n🤔🤷😩'); + await assertPromptInput('✌️👍\n|😎😕😅\n🤔🤷😩'); await writePromise('\x1b[2C'); - assertPromptInput('✌️👍\n😎😕|😅\n🤔🤷😩'); + await assertPromptInput('✌️👍\n😎😕|😅\n🤔🤷😩'); await writePromise('\x1b[1;4H'); - assertPromptInput('✌️|👍\n😎😕😅\n🤔🤷😩'); + await assertPromptInput('✌️|👍\n😎😕😅\n🤔🤷😩'); await writePromise('\x1b[D'); - assertPromptInput('|✌️👍\n😎😕😅\n🤔🤷😩'); + await assertPromptInput('|✌️👍\n😎😕😅\n🤔🤷😩'); }); // To "record a session" for these tests: @@ -206,7 +200,7 @@ suite('PromptInputModel', () => { '\r\n03:13:47  vscode   tyriar/prompt_input_model  $⇡  via  v18.18.2 \r\n❯ ', ]); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await replayEvents([ '[?25lfakecommand[?25h', @@ -216,7 +210,7 @@ suite('PromptInputModel', () => { '[?25lfoo[?25h', '', ]); - assertPromptInput('foo|'); + await assertPromptInput('foo|'); }); test('input with accepted and run ghost text', async () => { await replayEvents([ @@ -229,62 +223,62 @@ suite('PromptInputModel', () => { ]); promptInputModel.setContinuationPrompt('∙ '); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('e|[cho "hello world"]'); + await assertPromptInput('e|[cho "hello world"]'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('ec|[ho "hello world"]'); + await assertPromptInput('ec|[ho "hello world"]'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('ech|[o "hello world"]'); + await assertPromptInput('ech|[o "hello world"]'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('echo|[ "hello world"]'); + await assertPromptInput('echo|[ "hello world"]'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('echo |["hello world"]'); + await assertPromptInput('echo |["hello world"]'); await replayEvents([ '[?25lecho "hello world"[?25h', '', ]); - assertPromptInput('echo "hello world"|'); + await assertPromptInput('echo "hello world"|'); await replayEvents([ ']633;E;echo "hello world";ff464d39-bc80-4bae-9ead-b1cafc4adf6f]633;C', ]); fireCommandExecuted(); - assertPromptInput('echo "hello world"|'); + await assertPromptInput('echo "hello world"'); await replayEvents([ '\r\n', 'hello world\r\n', ]); - assertPromptInput('echo "hello world"|'); + await assertPromptInput('echo "hello world"'); await replayEvents([ ']633;D;0]633;A]633;P;Cwd=C:\x5cGithub\x5cmicrosoft\x5cvscode]633;B', '\r\n03:41:42  vscode   tyriar/prompt_input_model  $  via  v18.18.2 \r\n❯ ', ]); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); }); test('input, go to start (ctrl+home), delete word in front (ctrl+delete)', async () => { @@ -297,7 +291,7 @@ suite('PromptInputModel', () => { '\r\n16:07:06  vscode   tyriar/210662  $!  via  v18.18.2 \r\n❯ ', ]); fireCommandStart(); - assertPromptInput('|'); + await assertPromptInput('|'); await replayEvents([ '[?25lGit push[?25h', @@ -306,7 +300,7 @@ suite('PromptInputModel', () => { '', '[?25lGet-ChildItem -Path a[?25h', ]); - assertPromptInput('Get|[-ChildItem -Path a]'); + await assertPromptInput('Get|[-ChildItem -Path a]'); await replayEvents([ '',