From c7c0acd2ce4e0e541554fe8958922170a0fc53dc Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:06:16 +0200 Subject: [PATCH] Git - Commit action button extension api (#154555) --- extensions/git/src/actionButton.ts | 56 ++++++----- extensions/git/src/api/api1.ts | 98 ++++++++++--------- extensions/git/src/api/git.d.ts | 11 ++- extensions/git/src/commands.ts | 15 ++- extensions/git/src/main.ts | 4 + extensions/git/src/model.ts | 23 ++++- extensions/git/src/postCommitCommands.ts | 30 ++++++ extensions/git/src/repository.ts | 4 +- .../contrib/scm/browser/scmViewPane.ts | 20 ++-- 9 files changed, 165 insertions(+), 96 deletions(-) create mode 100644 extensions/git/src/postCommitCommands.ts diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index 856280b414a..0f0741eb78c 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vscode-nls'; -import { Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace } from 'vscode'; +import { Command, Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace } from 'vscode'; +import { ApiRepository } from './api/api1'; import { Branch, Status } from './api/git'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; import { Repository, Operation } from './repository'; import { dispose } from './util'; @@ -34,7 +36,9 @@ export class ActionButtonCommand { private disposables: Disposable[] = []; - constructor(readonly repository: Repository) { + constructor( + readonly repository: Repository, + readonly postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry) { this._state = { HEAD: undefined, isCommitInProgress: false, @@ -84,7 +88,7 @@ export class ActionButtonCommand { // The button is disabled if (!showActionButton.commit) { return undefined; } - let title: string, tooltip: string; + let title: string, tooltip: string, commandArg: string; const postCommitCommand = config.get('postCommitCommand'); // Branch protection @@ -99,6 +103,7 @@ export class ActionButtonCommand { // Title, tooltip switch (postCommitCommand) { case 'push': { + commandArg = 'git.push'; title = localize('scm button commit and push title', "{0} Commit & Push", icon ?? '$(arrow-up)'); if (alwaysCommitToNewBranch) { tooltip = this.state.isCommitInProgress ? @@ -112,6 +117,7 @@ export class ActionButtonCommand { break; } case 'sync': { + commandArg = 'git.sync'; title = localize('scm button commit and sync title', "{0} Commit & Sync", icon ?? '$(sync)'); if (alwaysCommitToNewBranch) { tooltip = this.state.isCommitInProgress ? @@ -125,6 +131,7 @@ export class ActionButtonCommand { break; } default: { + commandArg = ''; title = localize('scm button commit title', "{0} Commit", icon ?? '$(check)'); if (alwaysCommitToNewBranch) { tooltip = this.state.isCommitInProgress ? @@ -144,31 +151,34 @@ export class ActionButtonCommand { command: 'git.commit', title: title, tooltip: tooltip, - arguments: [this.repository.sourceControl], + arguments: [this.repository.sourceControl, commandArg], }, - secondaryCommands: [ - [ - { - command: 'git.commit', - title: localize('scm secondary button commit', "Commit"), - arguments: [this.repository.sourceControl, ''], - }, - { - command: 'git.commit', - title: localize('scm secondary button commit and push', "Commit & Push"), - arguments: [this.repository.sourceControl, 'push'], - }, - { - command: 'git.commit', - title: localize('scm secondary button commit and sync', "Commit & Sync"), - arguments: [this.repository.sourceControl, 'sync'], - }, - ] - ], + secondaryCommands: this.getCommitActionButtonSecondaryCommands(), enabled: this.state.repositoryHasChangesToCommit && !this.state.isCommitInProgress && !this.state.isMergeInProgress }; } + private getCommitActionButtonSecondaryCommands(): Command[][] { + const commandGroups: Command[][] = []; + + for (const provider of this.postCommitCommandsProviderRegistry.getPostCommitCommandsProviders()) { + const commands = provider.getCommands(new ApiRepository(this.repository)); + commandGroups.push((commands ?? []).map(c => { + return { + command: 'git.commit', + title: c.title, + arguments: [this.repository.sourceControl, c.command] + }; + })); + } + + if (commandGroups.length > 0) { + commandGroups[0].splice(0, 0, { command: 'git.commit', title: localize('scm secondary button commit', "Commit") }); + } + + return commandGroups; + } + private getPublishBranchActionButton(): SourceControlActionButton | undefined { const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); const showActionButton = config.get<{ publish: boolean }>('showActionButton', { publish: true }); diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 32417c2a6d1..4581eaa1abc 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,7 +5,7 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode'; import { combinedDisposable, mapEvent } from '../util'; import { toGitUri } from '../uri'; @@ -57,157 +57,157 @@ export class ApiRepositoryUIState implements RepositoryUIState { export class ApiRepository implements Repository { - readonly rootUri: Uri = Uri.file(this._repository.root); - readonly inputBox: InputBox = new ApiInputBox(this._repository.inputBox); - readonly state: RepositoryState = new ApiRepositoryState(this._repository); - readonly ui: RepositoryUIState = new ApiRepositoryUIState(this._repository.sourceControl); + readonly rootUri: Uri = Uri.file(this.repository.root); + readonly inputBox: InputBox = new ApiInputBox(this.repository.inputBox); + readonly state: RepositoryState = new ApiRepositoryState(this.repository); + readonly ui: RepositoryUIState = new ApiRepositoryUIState(this.repository.sourceControl); - constructor(private _repository: BaseRepository) { } + constructor(readonly repository: BaseRepository) { } apply(patch: string, reverse?: boolean): Promise { - return this._repository.apply(patch, reverse); + return this.repository.apply(patch, reverse); } getConfigs(): Promise<{ key: string; value: string }[]> { - return this._repository.getConfigs(); + return this.repository.getConfigs(); } getConfig(key: string): Promise { - return this._repository.getConfig(key); + return this.repository.getConfig(key); } setConfig(key: string, value: string): Promise { - return this._repository.setConfig(key, value); + return this.repository.setConfig(key, value); } getGlobalConfig(key: string): Promise { - return this._repository.getGlobalConfig(key); + return this.repository.getGlobalConfig(key); } getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> { - return this._repository.getObjectDetails(treeish, path); + return this.repository.getObjectDetails(treeish, path); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { - return this._repository.detectObjectType(object); + return this.repository.detectObjectType(object); } buffer(ref: string, filePath: string): Promise { - return this._repository.buffer(ref, filePath); + return this.repository.buffer(ref, filePath); } show(ref: string, path: string): Promise { - return this._repository.show(ref, path); + return this.repository.show(ref, path); } getCommit(ref: string): Promise { - return this._repository.getCommit(ref); + return this.repository.getCommit(ref); } add(paths: string[]) { - return this._repository.add(paths.map(p => Uri.file(p))); + return this.repository.add(paths.map(p => Uri.file(p))); } revert(paths: string[]) { - return this._repository.revert(paths.map(p => Uri.file(p))); + return this.repository.revert(paths.map(p => Uri.file(p))); } clean(paths: string[]) { - return this._repository.clean(paths.map(p => Uri.file(p))); + return this.repository.clean(paths.map(p => Uri.file(p))); } diff(cached?: boolean) { - return this._repository.diff(cached); + return this.repository.diff(cached); } diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; diffWithHEAD(path?: string): Promise { - return this._repository.diffWithHEAD(path); + return this.repository.diffWithHEAD(path); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; diffWith(ref: string, path?: string): Promise { - return this._repository.diffWith(ref, path); + return this.repository.diffWith(ref, path); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; diffIndexWithHEAD(path?: string): Promise { - return this._repository.diffIndexWithHEAD(path); + return this.repository.diffIndexWithHEAD(path); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffIndexWith(ref: string, path?: string): Promise { - return this._repository.diffIndexWith(ref, path); + return this.repository.diffIndexWith(ref, path); } diffBlobs(object1: string, object2: string): Promise { - return this._repository.diffBlobs(object1, object2); + return this.repository.diffBlobs(object1, object2); } diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { - return this._repository.diffBetween(ref1, ref2, path); + return this.repository.diffBetween(ref1, ref2, path); } hashObject(data: string): Promise { - return this._repository.hashObject(data); + return this.repository.hashObject(data); } createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { - return this._repository.branch(name, checkout, ref); + return this.repository.branch(name, checkout, ref); } deleteBranch(name: string, force?: boolean): Promise { - return this._repository.deleteBranch(name, force); + return this.repository.deleteBranch(name, force); } getBranch(name: string): Promise { - return this._repository.getBranch(name); + return this.repository.getBranch(name); } getBranches(query: BranchQuery): Promise { - return this._repository.getBranches(query); + return this.repository.getBranches(query); } setBranchUpstream(name: string, upstream: string): Promise { - return this._repository.setBranchUpstream(name, upstream); + return this.repository.setBranchUpstream(name, upstream); } getMergeBase(ref1: string, ref2: string): Promise { - return this._repository.getMergeBase(ref1, ref2); + return this.repository.getMergeBase(ref1, ref2); } tag(name: string, upstream: string): Promise { - return this._repository.tag(name, upstream); + return this.repository.tag(name, upstream); } deleteTag(name: string): Promise { - return this._repository.deleteTag(name); + return this.repository.deleteTag(name); } status(): Promise { - return this._repository.status(); + return this.repository.status(); } checkout(treeish: string): Promise { - return this._repository.checkout(treeish); + return this.repository.checkout(treeish); } addRemote(name: string, url: string): Promise { - return this._repository.addRemote(name, url); + return this.repository.addRemote(name, url); } removeRemote(name: string): Promise { - return this._repository.removeRemote(name); + return this.repository.removeRemote(name); } renameRemote(name: string, newName: string): Promise { - return this._repository.renameRemote(name, newName); + return this.repository.renameRemote(name, newName); } fetch(arg0?: FetchOptions | string | undefined, @@ -216,30 +216,30 @@ export class ApiRepository implements Repository { prune?: boolean | undefined ): Promise { if (arg0 !== undefined && typeof arg0 !== 'string') { - return this._repository.fetch(arg0); + return this.repository.fetch(arg0); } - return this._repository.fetch({ remote: arg0, ref, depth, prune }); + return this.repository.fetch({ remote: arg0, ref, depth, prune }); } pull(unshallow?: boolean): Promise { - return this._repository.pull(undefined, unshallow); + return this.repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise { - return this._repository.pushTo(remoteName, branchName, setUpstream, force); + return this.repository.pushTo(remoteName, branchName, setUpstream, force); } blame(path: string): Promise { - return this._repository.blame(path); + return this.repository.blame(path); } log(options?: LogOptions): Promise { - return this._repository.log(options); + return this.repository.log(options); } commit(message: string, opts?: CommitOptions): Promise { - return this._repository.commit(message, opts); + return this.repository.commit(message, opts); } } @@ -318,6 +318,10 @@ export class ApiImpl implements API { return this._model.registerCredentialsProvider(provider); } + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { + return this._model.registerPostCommitCommandsProvider(provider); + } + registerPushErrorHandler(handler: PushErrorHandler): Disposable { return this._model.registerPushErrorHandler(handler); } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 6dfba24823e..cb6265558df 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, Event, Disposable, ProviderResult } from 'vscode'; +import { Uri, Event, Disposable, ProviderResult, Command } from 'vscode'; export { ProviderResult } from 'vscode'; export interface Git { @@ -129,8 +129,6 @@ export interface LogOptions { readonly path?: string; } -export type PostCommitCommand = 'push' | 'sync' | string; - export interface CommitOptions { all?: boolean | 'tracked'; amend?: boolean; @@ -141,7 +139,7 @@ export interface CommitOptions { requireUserConfig?: boolean; useEditor?: boolean; verbose?: boolean; - postCommitCommand?: PostCommitCommand; + postCommitCommand?: string; } export interface FetchOptions { @@ -256,6 +254,10 @@ export interface CredentialsProvider { getCredentials(host: Uri): ProviderResult; } +export interface PostCommitCommandsProvider { + getCommands(repository: Repository): Command[]; +} + export interface PushErrorHandler { handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise; } @@ -284,6 +286,7 @@ export interface API { registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable; registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; registerCredentialsProvider(provider: CredentialsProvider): Disposable; + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; registerPushErrorHandler(handler: PushErrorHandler): Disposable; } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 22649e86fc7..4bbb17e5137 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -9,7 +9,7 @@ import { Command, commands, Disposable, LineChange, MessageOptions, Position, Pr import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; -import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, PostCommitCommand } from './api/git'; +import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git'; import { Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; @@ -1623,12 +1623,11 @@ export class CommandCenter { await repository.commit(message, opts); - const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand'); - if ((opts.postCommitCommand === undefined && postCommitCommand === 'push') || opts.postCommitCommand === 'push') { - await this._push(repository, { pushType: PushType.Push }); - } - if ((opts.postCommitCommand === undefined && postCommitCommand === 'sync') || opts.postCommitCommand === 'sync') { - await this.sync(repository); + // Execute post commit command + if (opts.postCommitCommand?.length) { + await commands.executeCommand( + opts.postCommitCommand, + new ApiRepository(repository)); } return true; @@ -1677,7 +1676,7 @@ export class CommandCenter { } @command('git.commit', { repository: true }) - async commit(repository: Repository, postCommitCommand?: PostCommitCommand): Promise { + async commit(repository: Repository, postCommitCommand?: string): Promise { await this.commitWithAnyInput(repository, { postCommitCommand }); } diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 6fbc88cc9fd..40147d6c0e6 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -27,6 +27,7 @@ import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; import { createIPCServer, IPCServer } from './ipc/ipcServer'; import { GitEditor } from './gitEditor'; +import { GitPostCommitCommandsProvider } from './postCommitCommands'; const deactivateTasks: { (): Promise }[] = []; @@ -117,6 +118,9 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu new GitTimelineProvider(model, cc) ); + const postCommitCommandsProvider = new GitPostCommitCommandsProvider(); + model.registerPostCommitCommandsProvider(postCommitCommandsProvider); + checkGitVersion(info); return model; diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 87c510b3b03..505ceeb16c0 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -13,12 +13,13 @@ import * as path from 'path'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; import { fromGitUri } from './uri'; -import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher } from './api/git'; +import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider } from './api/git'; import { Askpass } from './askpass'; import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { OutputChannelLogger } from './log'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; const localize = nls.loadMessageBundle(); @@ -50,7 +51,7 @@ interface OpenRepository extends Disposable { repository: Repository; } -export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerRegistry { +export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { private _onDidOpenRepository = new EventEmitter(); readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; @@ -105,6 +106,8 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR private _onDidRemoveRemoteSourcePublisher = new EventEmitter(); readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event; + private postCommitCommandsProviders = new Set(); + private showRepoOnHomeDriveRootWarning = true; private pushErrorHandlers = new Set(); @@ -369,7 +372,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap @@ -506,6 +509,10 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return this.openRepositories.filter(r => r.repository === hint)[0]; } + if (hint instanceof ApiRepository) { + return this.openRepositories.filter(r => r.repository === hint.repository)[0]; + } + if (typeof hint === 'string') { hint = Uri.file(hint); } @@ -582,6 +589,16 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return [...this.remoteSourcePublishers.values()]; } + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { + this.postCommitCommandsProviders.add(provider); + + return toDisposable(() => this.postCommitCommandsProviders.delete(provider)); + } + + getPostCommitCommandsProviders(): PostCommitCommandsProvider[] { + return [...this.postCommitCommandsProviders.values()]; + } + registerCredentialsProvider(provider: CredentialsProvider): Disposable { return this.askpass.registerCredentialsProvider(provider); } diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts new file mode 100644 index 00000000000..2fd6dc5676b --- /dev/null +++ b/extensions/git/src/postCommitCommands.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +import { Command, Disposable } from 'vscode'; +import { PostCommitCommandsProvider } from './api/git'; + +export interface IPostCommitCommandsProviderRegistry { + getPostCommitCommandsProviders(): PostCommitCommandsProvider[]; + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; +} + +const localize = nls.loadMessageBundle(); + +export class GitPostCommitCommandsProvider implements PostCommitCommandsProvider { + getCommands(): Command[] { + return [ + { + command: 'git.push', + title: localize('scm secondary button commit and push', "Commit & Push") + }, + { + command: 'git.sync', + title: localize('scm secondary button commit and sync', "Commit & Sync") + }, + ]; + } +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index eb0de19bcf6..7c3ba92f792 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -22,6 +22,7 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { ActionButtonCommand } from './actionButton'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -876,6 +877,7 @@ export class Repository implements Disposable { private readonly repository: BaseRepository, private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, + postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry, globalState: Memento, outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter @@ -997,7 +999,7 @@ export class Repository implements Disposable { statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables); this._sourceControl.statusBarCommands = statusBar.commands; - const actionButton = new ActionButtonCommand(this); + const actionButton = new ActionButtonCommand(this, postCommitCommandsProviderRegistry); this.disposables.push(actionButton); actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button); this._sourceControl.actionButton = actionButton.button; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 9af7e3e8a77..56be4b4065f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2644,19 +2644,11 @@ export class SCMActionButton implements IDisposable { return; } - const executeButtonAction = async (commandId: string, ...args: any[]) => { - try { - await this.commandService.executeCommand(commandId, ...args); - } catch (ex) { - this.notificationService.error(ex); - } - }; - if (button.secondaryCommands?.length) { const actions: IAction[] = []; for (let index = 0; index < button.secondaryCommands.length; index++) { for (const command of button.secondaryCommands[index]) { - actions.push(new Action(command.id, command.title, undefined, true, async () => await executeButtonAction(command.id, ...(command.arguments || [])))); + actions.push(new Action(command.id, command.title, undefined, true, async () => await this.executeCommand(command.id, ...(command.arguments || [])))); } if (index !== button.secondaryCommands.length - 1) { actions.push(new Separator()); @@ -2682,7 +2674,7 @@ export class SCMActionButton implements IDisposable { this.button.enabled = button.enabled; this.button.label = button.command.title; - this.button.onDidClick(async () => await executeButtonAction(button.command.id, ...(button.command.arguments || [])), null, this.disposables.value); + this.button.onDidClick(async () => await this.executeCommand(button.command.id, ...(button.command.arguments || [])), null, this.disposables.value); this.disposables.value!.add(this.button); this.disposables.value!.add(attachButtonStyler(this.button, this.themeService)); @@ -2697,4 +2689,12 @@ export class SCMActionButton implements IDisposable { this.button = undefined; clearNode(this.container); } + + private async executeCommand(commandId: string, ...args: any[]): Promise { + try { + await this.commandService.executeCommand(commandId, ...args); + } catch (ex) { + this.notificationService.error(ex); + } + } }