Skip to content

Commit 5d09b38

Browse files
committed
PromptInputModel: Don't fire change event on no change
1 parent eb43e19 commit 5d09b38

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,19 +141,16 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
141141
return;
142142
}
143143

144-
// Command start line
145-
this._value = commandLine;
146-
147-
// Get cursor index
148144
const absoluteCursorY = buffer.baseY + buffer.cursorY;
149-
this._cursorIndex = absoluteCursorY === commandStartY ? this._getRelativeCursorIndex(this._commandStartX, buffer, line) : commandLine.length + 1;
150-
this._ghostTextIndex = -1;
145+
let value = commandLine;
146+
let cursorIndex = absoluteCursorY === commandStartY ? this._getRelativeCursorIndex(this._commandStartX, buffer, line) : commandLine.length + 1;
147+
let ghostTextIndex = -1;
151148

152149
// Detect ghost text by looking for italic or dim text in or after the cursor and
153150
// non-italic/dim text in the cell closest non-whitespace cell before the cursor
154151
if (absoluteCursorY === commandStartY && buffer.cursorX > 1) {
155152
// Ghost text in pwsh only appears to happen on the cursor line
156-
this._ghostTextIndex = this._scanForGhostText(buffer, line);
153+
ghostTextIndex = this._scanForGhostText(buffer, line);
157154
}
158155

159156
// IDEA: Detect line continuation if it's not set
@@ -167,8 +164,8 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
167164
// user likely just pressed enter
168165
if (this._continuationPrompt === undefined || this._lineContainsContinuationPrompt(lineText)) {
169166
lineText = this._trimContinuationPrompt(lineText);
170-
this._value += `\n${lineText}`;
171-
this._cursorIndex += (absoluteCursorY === y
167+
value += `\n${lineText}`;
168+
cursorIndex += (absoluteCursorY === y
172169
? this._getRelativeCursorIndex(this._getContinuationPromptCellWidth(line, lineText), buffer, line)
173170
: lineText.length + 1);
174171
} else {
@@ -183,7 +180,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
183180
const lineText = line?.translateToString(true);
184181
if (lineText && line) {
185182
if (this._continuationPrompt === undefined || this._lineContainsContinuationPrompt(lineText)) {
186-
this._value += `\n${this._trimContinuationPrompt(lineText)}`;
183+
value += `\n${this._trimContinuationPrompt(lineText)}`;
187184
} else {
188185
break;
189186
}
@@ -194,7 +191,12 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
194191
this._logService.trace(`PromptInputModel#_sync: ${this.getCombinedString()}`);
195192
}
196193

197-
this._onDidChangeInput.fire();
194+
if (this._value !== value || this._cursorIndex !== cursorIndex || this._ghostTextIndex !== ghostTextIndex) {
195+
this._value = value;
196+
this._cursorIndex = cursorIndex;
197+
this._ghostTextIndex = ghostTextIndex;
198+
this._onDidChangeInput.fire();
199+
}
198200
}
199201

200202
/**

src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { ITerminalCommand } from 'vs/platform/terminal/common/capabilities/
1111

1212
// eslint-disable-next-line local/code-import-patterns, local/code-amd-node-module
1313
import { Terminal } from '@xterm/headless';
14-
import { strictEqual } from 'assert';
14+
import { notDeepStrictEqual, strictEqual } from 'assert';
1515
import { timeout } from 'vs/base/common/async';
1616

1717
suite('PromptInputModel', () => {
@@ -78,6 +78,46 @@ suite('PromptInputModel', () => {
7878
await assertPromptInput('|');
7979
});
8080

81+
test('should not fire events when nothing changes', async () => {
82+
const events: {
83+
value: string;
84+
cursorIndex: number;
85+
ghostTextIndex: number;
86+
}[] = [];
87+
store.add(promptInputModel.onDidChangeInput(() => {
88+
events.push({
89+
value: promptInputModel.value,
90+
cursorIndex: promptInputModel.cursorIndex,
91+
ghostTextIndex: promptInputModel.ghostTextIndex
92+
});
93+
}));
94+
95+
await writePromise('$ ');
96+
fireCommandStart();
97+
await assertPromptInput('|');
98+
99+
await writePromise('foo');
100+
await assertPromptInput('foo|');
101+
102+
await writePromise(' bar');
103+
await assertPromptInput('foo bar|');
104+
105+
await writePromise('\r\n');
106+
fireCommandExecuted();
107+
await assertPromptInput('foo bar');
108+
109+
await writePromise('$ ');
110+
fireCommandStart();
111+
await assertPromptInput('|');
112+
113+
await writePromise('foo bar');
114+
await assertPromptInput('foo bar|');
115+
116+
for (let i = 0; i < events.length - 1; i++) {
117+
notDeepStrictEqual(events[i], events[i + 1], 'not adjacent events should fire with the same value');
118+
}
119+
});
120+
81121
test('cursor navigation', async () => {
82122
await writePromise('$ ');
83123
fireCommandStart();

0 commit comments

Comments
 (0)