Repository: microsoft/vscode (TypeScript)
Date: 2023-05-10 14:05:25+00:00
Task type: Bug Fix

Issue:
Revert change to always update status
Just saw https://github.com/microsoft/vscode/pull/180903, this goes against the design. It's meant to only add the status if it doesn't already exist, if the status changed then it should use a different id and/or remove the old status first.


Issue discussion:
@Tyriar I'm not clear on the solution. There're a limited number of Terminal Status IDs: https://github.com/microsoft/vscode/blob/9b322006a9f28e550c51223485c2f1d93e5bfcdb/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts#L21-L27

In this case status ID hasn't changed, it's still `EnvironmentVariableInfoChangesActive`, only the description which comes with it has updated.
@karrtikr working on it, I'll send a PR to you in a bit

Solution patch:
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts
--- a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts
@@ -38,6 +38,8 @@ export interface ITerminalStatusList {
 
 	/**
 	 * Adds a status to the list.
+	 * @param status The status object. Ideally a single status object that does not change will be
+	 * shared as this call will no-op if the status is already set (checked by by object reference).
 	 * @param duration An optional duration in milliseconds of the status, when specified the status
 	 * will remove itself when the duration elapses unless the status gets re-added.
 	 */
@@ -87,6 +89,11 @@ export class TerminalStatusList extends Disposable implements ITerminalStatusLis
 			const timeout = window.setTimeout(() => this.remove(status), duration);
 			this._statusTimeouts.set(status.id, timeout);
 		}
+		const existingStatus = this._statuses.get(status.id);
+		if (existingStatus && existingStatus !== status) {
+			this._onDidRemoveStatus.fire(existingStatus);
+			this._statuses.delete(existingStatus.id);
+		}
 		if (!this._statuses.has(status.id)) {
 			const oldPrimary = this.primary;
 			this._statuses.set(status.id, status);
@@ -95,11 +102,6 @@ export class TerminalStatusList extends Disposable implements ITerminalStatusLis
 			if (oldPrimary !== newPrimary) {
 				this._onDidChangePrimaryStatus.fire(newPrimary);
 			}
-		} else {
-			this._statuses.set(status.id, status);
-			// It maybe the case that status hasn't changed, there isn't a good way to check this based on
-			// `ITerminalStatus`, so just fire the event anyway.
-			this._onDidAddStatus.fire(status);
 		}
 	}
 


Test patch:
diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts
--- a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts
+++ b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts
@@ -130,6 +130,19 @@ suite('Workbench - TerminalStatusList', () => {
 		strictEqual(list.statuses[1].icon!.id, Codicon.zap.id, 'zap~spin should have animation removed only');
 	});
 
+	test('add should fire onDidRemoveStatus if same status id with a different object reference was added', () => {
+		const eventCalls: string[] = [];
+		list.onDidAddStatus(() => eventCalls.push('add'));
+		list.onDidRemoveStatus(() => eventCalls.push('remove'));
+		list.add({ id: 'test', severity: Severity.Info });
+		list.add({ id: 'test', severity: Severity.Info });
+		deepStrictEqual(eventCalls, [
+			'add',
+			'remove',
+			'add'
+		]);
+	});
+
 	test('remove', () => {
 		list.add({ id: 'info', severity: Severity.Info });
 		list.add({ id: 'warning', severity: Severity.Warning });