Repository: microsoft/vscode (TypeScript)
Date: 2022-09-23 21:48:21+00:00
Task type: Bug Fix

Issue:
contextual actions don't appear sometimes for `git push`
bug 1:

1. `git checkout -b merogge/test123`
2. `git push`
3. 🐛 no 💡 

bug 2:

1. `git push --set-upstream origin merogge/test123`
2. 🐛 no 💡 


Issue discussion:

Solution patch:
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts
--- a/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalBaseContextualActions.ts
@@ -14,9 +14,9 @@ export const GitCommandLineRegex = /git/;
 export const GitPushCommandLineRegex = /git\s+push/;
 export const AnyCommandLineRegex = /.{4,}/;
 export const GitSimilarOutputRegex = /most similar command is\s+([^\s]{3,})/;
-export const FreePortOutputRegex = /address already in use \d\.\d\.\d\.\d:(\d\d\d\d)\s+|Unable to bind [^ ]*:(\d+)|can't listen on port (\d+)|listen EADDRINUSE [^ ]*:(\d+)/;
-export const GitPushOutputRegex = /git push --set-upstream origin ([^\s]+)\s+/;
-export const GitCreatePrOutputRegex = /Create a pull request for \'([^\s]+)\' on GitHub by visiting:\s+remote:\s+(https:.+pull.+)\s+/;
+export const FreePortOutputRegex = /address already in use \d\.\d\.\d\.\d:(\d\d\d\d)|Unable to bind [^ ]*:(\d+)|can't listen on port (\d+)|listen EADDRINUSE [^ ]*:(\d+)/;
+export const GitPushOutputRegex = /git push --set-upstream origin ([^\s]+)/;
+export const GitCreatePrOutputRegex = /Create a pull request for \'([^\s]+)\' on GitHub by visiting:\s+remote:\s+(https:.+pull.+)/;
 
 export function gitSimilarCommand(): ITerminalContextualActionOptions {
 	return {
diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts
--- a/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts
+++ b/src/vs/workbench/contrib/terminal/browser/xterm/contextualActionAddon.ts
@@ -152,23 +152,22 @@ export function getMatchActions(command: ITerminalCommand, actionOptions: Map<st
 				outputMatch = command.getOutputMatch(outputMatcher);
 			}
 			const actions = actionOption.getActions({ commandLineMatch, outputMatch }, command);
-			if (!actions) {
-				return matchActions.length === 0 ? undefined : matchActions;
-			}
-			for (const a of actions) {
-				matchActions.push({
-					id: a.id,
-					label: a.label,
-					class: a.class,
-					enabled: a.enabled,
-					run: async () => {
-						await a.run();
-						if (a.commandToRunInTerminal) {
-							onDidRequestRerunCommand?.fire({ command: a.commandToRunInTerminal, addNewLine: a.addNewLine });
-						}
-					},
-					tooltip: a.tooltip
-				});
+			if (actions) {
+				for (const a of actions) {
+					matchActions.push({
+						id: a.id,
+						label: a.label,
+						class: a.class,
+						enabled: a.enabled,
+						run: async () => {
+							await a.run();
+							if (a.commandToRunInTerminal) {
+								onDidRequestRerunCommand?.fire({ command: a.commandToRunInTerminal, addNewLine: a.addNewLine });
+							}
+						},
+						tooltip: a.tooltip
+					});
+				}
 			}
 		}
 	}


Test patch:
diff --git a/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts
--- a/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts
+++ b/src/vs/workbench/contrib/terminal/test/browser/contextualActionAddon.test.ts
@@ -117,7 +117,7 @@ suite('ContextualActionAddon', () => {
 					strictEqual(getMatchActions(createCommand(portCommand, `invalid output`, FreePortOutputRegex), expected), undefined);
 				});
 			});
-			test.skip('returns actions', () => {
+			test('returns actions', () => {
 				assertMatchOptions(getMatchActions(createCommand(portCommand, output, FreePortOutputRegex), expected), actionOptions);
 			});
 		});
@@ -127,7 +127,7 @@ suite('ContextualActionAddon', () => {
 			const output = `fatal: The current branch test22 has no upstream branch.
 			To push the current branch and set the remote as upstream, use
 
-				git push --set-upstream origin test22 `;
+				git push --set-upstream origin test22`;
 			const exitCode = 128;
 			const actions = [
 				{
@@ -151,7 +151,7 @@ suite('ContextualActionAddon', () => {
 					strictEqual(getMatchActions(createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap), undefined);
 				});
 			});
-			suite('returns undefined when', () => {
+			suite('returns actions when', () => {
 				test('expected unix exit code', () => {
 					assertMatchOptions(getMatchActions(createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap), actions);
 				});
@@ -204,6 +204,47 @@ suite('ContextualActionAddon', () => {
 			});
 		});
 	});
+	suite('gitPush - multiple providers', () => {
+		const expectedMap = new Map();
+		const command = `git push`;
+		const output = `fatal: The current branch test22 has no upstream branch.
+		To push the current branch and set the remote as upstream, use
+
+			git push --set-upstream origin test22`;
+		const exitCode = 128;
+		const actions = [
+			{
+				id: 'terminal.gitPush',
+				label: 'Git push test22',
+				run: true,
+				tooltip: 'Git push test22',
+				enabled: true
+			}
+		];
+		setup(() => {
+			const pushCommand = gitPushSetUpstream();
+			const prCommand = gitCreatePr(openerService);
+			contextualActionAddon.registerCommandFinishedListener(pushCommand);
+			contextualActionAddon.registerCommandFinishedListener(prCommand);
+			expectedMap.set(pushCommand.commandLineMatcher.toString(), [pushCommand, prCommand]);
+		});
+		suite('returns undefined when', () => {
+			test('output does not match', () => {
+				strictEqual(getMatchActions(createCommand(command, `invalid output`, GitPushOutputRegex, exitCode), expectedMap), undefined);
+			});
+			test('command does not match', () => {
+				strictEqual(getMatchActions(createCommand(`git status`, output, GitPushOutputRegex, exitCode), expectedMap), undefined);
+			});
+		});
+		suite('returns actions when', () => {
+			test('expected unix exit code', () => {
+				assertMatchOptions(getMatchActions(createCommand(command, output, GitPushOutputRegex, exitCode), expectedMap), actions);
+			});
+			test('matching exit status', () => {
+				assertMatchOptions(getMatchActions(createCommand(command, output, GitPushOutputRegex, 2), expectedMap), actions);
+			});
+		});
+	});
 });
 
 function createCommand(command: string, output: string, outputMatcher?: RegExp | string, exitCode?: number): ITerminalCommand {