diff --git a/package.json b/package.json index fa7e961..2d9df57 100644 --- a/package.json +++ b/package.json @@ -484,33 +484,47 @@ "gitlens.gitCommands.skipConfirmations": { "type": "array", "default": [ - "fetch", - "stash-push", - "switch" + "fetch:command", + "stash-push:command", + "switch:command" ], "items": { "type": "string", "enum": [ - "fetch", - "pull", - "push", - "stash-apply", - "stash-pop", - "stash-push", - "switch" + "fetch:command", + "fetch:menu", + "pull:command", + "pull:menu", + "push:command", + "push:menu", + "stash-apply:command", + "stash-apply:menu", + "stash-pop:command", + "stash-pop:menu", + "stash-push:command", + "stash-push:menu", + "switch:command", + "switch:menu" ], "enumDescriptions": [ - "Skips fetch command confirmation", - "Skips pull command confirmation", - "Skips push command confirmation", - "Skips stash apply command confirmation", - "Skips stash pop command confirmation", - "Skips stash push command confirmation", - "Skips switch command confirmation" + "Skips fetch confirmations when run from a command, e.g. a view action", + "Skips fetch confirmations when run from the `Git Commands` menu", + "Skips pull confirmations when run from a command, e.g. a view action", + "Skips pull confirmations when run from the `Git Commands` menu", + "Skips push confirmations when run from a command, e.g. a view action", + "Skips push confirmations when run from the `Git Commands` menu", + "Skips stash apply confirmations when run from a command, e.g. a view action", + "Skips stash apply confirmations when run from the `Git Commands` menu", + "Skips stash pop confirmations when run from a command, e.g. a view action", + "Skips stash pop confirmations when run from the `Git Commands` menu", + "Skips stash push confirmations when run from a command, e.g. a view action", + "Skips stash push confirmations when run from the `Git Commands` menu", + "Skips switch confirmations when run from a command, e.g. a view action", + "Skips switch confirmations when run from the `Git Commands` menu" ] }, "minItems": 0, - "maxItems": 4, + "maxItems": 14, "uniqueItems": true, "markdownDescription": "Specifies which Git commands should have their confirmations skipped when executed from a GitLens view", "scope": "window" diff --git a/src/commands/git/cherry-pick.ts b/src/commands/git/cherry-pick.ts index db5ae26..8a4cf90 100644 --- a/src/commands/git/cherry-pick.ts +++ b/src/commands/git/cherry-pick.ts @@ -36,9 +36,9 @@ export interface CherryPickGitCommandArgs { export class CherryPickGitCommand extends QuickCommandBase { constructor(args?: CherryPickGitCommandArgs) { - super('cherry-pick', 'cherry-pick', 'Cherry Pick', false, { description: 'via Terminal' }); + super('cherry-pick', 'cherry-pick', 'Cherry Pick', { description: 'via Terminal' }); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repo !== undefined) { @@ -56,6 +56,10 @@ export class CherryPickGitCommand extends QuickCommandBase { }; } + get canSkipConfirm(): boolean { + return false; + } + execute(state: State) { runGitCommandInTerminal( 'cherry-pick', diff --git a/src/commands/git/fetch.ts b/src/commands/git/fetch.ts index 5d0d194..c579748 100644 --- a/src/commands/git/fetch.ts +++ b/src/commands/git/fetch.ts @@ -23,7 +23,7 @@ export class FetchGitCommand extends QuickCommandBase { constructor(args?: FetchGitCommandArgs) { super('fetch', 'fetch', 'Fetch'); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repos !== undefined && args.state.repos.length !== 0) { diff --git a/src/commands/git/merge.ts b/src/commands/git/merge.ts index bf959e3..16d11c2 100644 --- a/src/commands/git/merge.ts +++ b/src/commands/git/merge.ts @@ -34,9 +34,9 @@ export interface MergeGitCommandArgs { export class MergeGitCommand extends QuickCommandBase { constructor(args?: MergeGitCommandArgs) { - super('merge', 'merge', 'Merge', false, { description: 'via Terminal' }); + super('merge', 'merge', 'Merge', { description: 'via Terminal' }); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repo !== undefined) { @@ -54,6 +54,10 @@ export class MergeGitCommand extends QuickCommandBase { }; } + get canSkipConfirm(): boolean { + return false; + } + execute(state: State) { runGitCommandInTerminal('merge', [...state.flags, state.reference.ref].join(' '), state.repo.path, true); } diff --git a/src/commands/git/pull.ts b/src/commands/git/pull.ts index f231b7a..25ac5cb 100644 --- a/src/commands/git/pull.ts +++ b/src/commands/git/pull.ts @@ -23,7 +23,7 @@ export class PullGitCommand extends QuickCommandBase { constructor(args?: PullGitCommandArgs) { super('pull', 'pull', 'Pull'); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repos !== undefined && args.state.repos.length !== 0) { diff --git a/src/commands/git/push.ts b/src/commands/git/push.ts index 30c9177..0653252 100644 --- a/src/commands/git/push.ts +++ b/src/commands/git/push.ts @@ -23,7 +23,7 @@ export class PushGitCommand extends QuickCommandBase { constructor(args?: PushGitCommandArgs) { super('push', 'push', 'Push'); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repos !== undefined && args.state.repos.length !== 0) { diff --git a/src/commands/git/rebase.ts b/src/commands/git/rebase.ts index 2d4854a..9f3b8ed 100644 --- a/src/commands/git/rebase.ts +++ b/src/commands/git/rebase.ts @@ -37,9 +37,9 @@ export interface RebaseGitCommandArgs { export class RebaseGitCommand extends QuickCommandBase { constructor(args?: RebaseGitCommandArgs) { - super('rebase', 'rebase', 'Rebase', false, { description: 'via Terminal' }); + super('rebase', 'rebase', 'Rebase', { description: 'via Terminal' }); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repo !== undefined) { @@ -57,6 +57,10 @@ export class RebaseGitCommand extends QuickCommandBase { }; } + get canSkipConfirm(): boolean { + return false; + } + execute(state: State) { runGitCommandInTerminal('rebase', [...state.flags, state.reference.ref].join(' '), state.repo.path, true); } diff --git a/src/commands/git/reset.ts b/src/commands/git/reset.ts index 954c08f..d6ec2df 100644 --- a/src/commands/git/reset.ts +++ b/src/commands/git/reset.ts @@ -28,9 +28,9 @@ export interface ResetGitCommandArgs { export class ResetGitCommand extends QuickCommandBase { constructor(args?: ResetGitCommandArgs) { - super('reset', 'reset', 'Reset', false, { description: 'via Terminal' }); + super('reset', 'reset', 'Reset', { description: 'via Terminal' }); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repo !== undefined) { @@ -48,6 +48,10 @@ export class ResetGitCommand extends QuickCommandBase { }; } + get canSkipConfirm(): boolean { + return false; + } + execute(state: State) { runGitCommandInTerminal('reset', [...state.flags, state.reference!.ref].join(' '), state.repo.path, true); } diff --git a/src/commands/git/revert.ts b/src/commands/git/revert.ts index 6b21375..187a62e 100644 --- a/src/commands/git/revert.ts +++ b/src/commands/git/revert.ts @@ -28,9 +28,9 @@ export interface RevertGitCommandArgs { export class RevertGitCommand extends QuickCommandBase { constructor(args?: RevertGitCommandArgs) { - super('revert', 'revert', 'Revert', false, { description: 'via Terminal' }); + super('revert', 'revert', 'Revert', { description: 'via Terminal' }); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repo !== undefined) { @@ -48,6 +48,10 @@ export class RevertGitCommand extends QuickCommandBase { }; } + get canSkipConfirm(): boolean { + return false; + } + execute(state: State) { runGitCommandInTerminal( 'revert', diff --git a/src/commands/git/stash.ts b/src/commands/git/stash.ts index 0ad8c3d..cda9d02 100644 --- a/src/commands/git/stash.ts +++ b/src/commands/git/stash.ts @@ -61,7 +61,7 @@ export class StashGitCommand extends QuickCommandBase { constructor(args?: StashGitCommandArgs) { super('stash', 'stash', 'Stash'); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.subcommand !== undefined) { @@ -100,8 +100,8 @@ export class StashGitCommand extends QuickCommandBase { return this._subcommand === 'drop' ? false : super.canSkipConfirm; } - get confirmationKey() { - return this._subcommand === undefined ? undefined : `${super.confirmationKey}-${this._subcommand}`; + get skipConfirmKey() { + return this._subcommand === undefined ? undefined : `${this.key}-${this._subcommand}:${this.pickedVia}`; } protected async *steps(): StepAsyncGenerator { diff --git a/src/commands/git/switch.ts b/src/commands/git/switch.ts index 208835b..7b30666 100644 --- a/src/commands/git/switch.ts +++ b/src/commands/git/switch.ts @@ -33,7 +33,7 @@ export class SwitchGitCommand extends QuickCommandBase { constructor(args?: SwitchGitCommandArgs) { super('switch', 'switch', 'Switch'); - if (args === undefined || args.state === undefined) return; + if (args == null || args.state === undefined) return; let counter = 0; if (args.state.repos !== undefined && args.state.repos.length !== 0) { diff --git a/src/commands/gitCommands.ts b/src/commands/gitCommands.ts index 3c514df..a50b221 100644 --- a/src/commands/gitCommands.ts +++ b/src/commands/gitCommands.ts @@ -63,7 +63,17 @@ class PickCommandStep implements QuickPickStep { get command(): QuickCommandBase | undefined { return this._active; } - set command(value: QuickCommandBase | undefined) { + + find(commandName: string, fuzzy: boolean = false) { + if (fuzzy) { + const cmd = commandName.toLowerCase(); + return this.items.find(c => c.isMatch(cmd)); + } + + return this.items.find(c => c.key === commandName); + } + + setCommand(value: QuickCommandBase | undefined, reason: 'menu' | 'command'): void { if (this._active !== undefined) { this._active.picked = false; } @@ -72,17 +82,9 @@ class PickCommandStep implements QuickPickStep { if (this._active !== undefined) { this._active.picked = true; + this._active.pickedVia = reason; } } - - find(commandName: string, fuzzy: boolean = false) { - if (fuzzy) { - const cmd = commandName.toLowerCase(); - return this.items.find(c => c.isMatch(cmd)); - } - - return this.items.find(c => c.key === commandName); - } } @command() @@ -113,6 +115,8 @@ export class GitCommandsCommand extends Command { }; }; + private _pickedVia: 'menu' | 'command' = 'menu'; + constructor() { super(Commands.GitCommands); } @@ -126,7 +130,8 @@ export class GitCommandsCommand extends Command { if (args) { const command = commandsStep.find(args.command); if (command !== undefined) { - commandsStep.command = command; + this._pickedVia = 'command'; + commandsStep.setCommand(command, this._pickedVia); const next = await command.next(); if (next.done) return; @@ -207,7 +212,8 @@ export class GitCommandsCommand extends Command { // If we are starting over clear the previously active command if (commandsStep.command !== undefined && step === commandsStep) { - commandsStep.command = undefined; + this._pickedVia = 'menu'; + commandsStep.setCommand(undefined, this._pickedVia); } input.show(); @@ -285,7 +291,7 @@ export class GitCommandsCommand extends Command { const command = commandsStep.find(quickpick.value.trim(), true); if (command === undefined) return; - commandsStep.command = command; + commandsStep.setCommand(command, this._pickedVia); } else { const step = commandsStep.command.value; if (step === undefined || !isQuickPickStep(step)) return; @@ -299,7 +305,7 @@ export class GitCommandsCommand extends Command { items = [item]; } - resolve(await this.nextStep(quickpick, commandsStep.command, items)); + resolve(await this.nextStep(quickpick, commandsStep.command!, items)); return; } @@ -335,7 +341,7 @@ export class GitCommandsCommand extends Command { const buttons: QuickInputButton[] = []; if (command.canSkipConfirm) { - if (command.confirmationKey !== undefined) { + if (command.skipConfirmKey !== undefined) { buttons.push( command.confirm() ? this.GitQuickInputButtons.WillConfirm @@ -396,10 +402,10 @@ export class GitCommandsCommand extends Command { const command = items[0]; if (!QuickCommandBase.is(command)) return; - commandsStep.command = command; + commandsStep.setCommand(command, this._pickedVia); } - resolve(await this.nextStep(quickpick, commandsStep.command, items as QuickPickItem[])); + resolve(await this.nextStep(quickpick, commandsStep.command!, items as QuickPickItem[])); }) ); @@ -420,7 +426,8 @@ export class GitCommandsCommand extends Command { // If we are starting over clear the previously active command if (commandsStep.command !== undefined && step === commandsStep) { - commandsStep.command = undefined; + this._pickedVia = 'menu'; + commandsStep.setCommand(undefined, this._pickedVia); } // Needs to be after we reset the command @@ -454,7 +461,7 @@ export class GitCommandsCommand extends Command { } if (command.canSkipConfirm) { - if (command.confirmationKey === undefined) return buttons; + if (command.skipConfirmKey === undefined) return buttons; buttons.push( command.confirm() ? this.GitQuickInputButtons.WillConfirm : this.GitQuickInputButtons.WillSkipConfirm @@ -485,16 +492,16 @@ export class GitCommandsCommand extends Command { input: InputBox | QuickPick, command: QuickCommandBase | undefined ) { - if (command === undefined || command.confirmationKey === undefined) return; + if (command === undefined || command.skipConfirmKey === undefined) return; const section = configuration.name('gitCommands')('skipConfirmations').value; const skipConfirmations = configuration.get(section) || []; - const index = skipConfirmations.indexOf(command.confirmationKey); + const index = skipConfirmations.indexOf(command.skipConfirmKey); if (index !== -1) { skipConfirmations.splice(index, 1); } else { - skipConfirmations.push(command.confirmationKey); + skipConfirmations.push(command.skipConfirmKey); } void (await configuration.updateEffective( diff --git a/src/commands/quickCommand.ts b/src/commands/quickCommand.ts index e5305ce..bc3e1b2 100644 --- a/src/commands/quickCommand.ts +++ b/src/commands/quickCommand.ts @@ -57,7 +57,7 @@ export type StepSelection = T extends QuickPickStep : never; export type StepState = Partial & { counter: number; confirm?: boolean }; -export abstract class QuickCommandBase implements QuickPickItem { +export abstract class QuickCommandBase implements QuickPickItem { static is(item: QuickPickItem): item is QuickCommandBase { return item instanceof QuickCommandBase; } @@ -65,7 +65,7 @@ export abstract class QuickCommandBase implements QuickPickItem { readonly description?: string; readonly detail?: string; - protected _initialState?: StepState; + protected _initialState?: StepState; private _current: QuickPickStep | QuickInputStep | undefined; private _stepsIterator: StepAsyncGenerator | undefined; @@ -74,7 +74,6 @@ export abstract class QuickCommandBase implements QuickPickItem { public readonly key: string, public readonly label: string, public readonly title: string, - private readonly _canSkipConfirm: boolean = true, options: { description?: string; detail?: string; @@ -85,11 +84,7 @@ export abstract class QuickCommandBase implements QuickPickItem { } get canSkipConfirm(): boolean { - return this._canSkipConfirm; - } - - get confirmationKey(): string | undefined { - return this.key; + return true; } private _picked: boolean = false; @@ -98,14 +93,29 @@ export abstract class QuickCommandBase implements QuickPickItem { } set picked(value: boolean) { this._picked = value; + if (!value) { + this._pickedVia = 'menu'; + } + } + + private _pickedVia: 'menu' | 'command' = 'menu'; + get pickedVia() { + return this._pickedVia; + } + set pickedVia(value: 'menu' | 'command') { + this._pickedVia = value; + } + + get skipConfirmKey(): string | undefined { + return `${this.key}:${this.pickedVia}`; } confirm(override?: boolean) { - if (!this.canSkipConfirm || this.confirmationKey === undefined) return true; + if (!this.canSkipConfirm || this.skipConfirmKey === undefined) return true; return override !== undefined ? override - : !Container.config.gitCommands.skipConfirmations.includes(this.confirmationKey); + : !Container.config.gitCommands.skipConfirmations.includes(this.skipConfirmKey); } isMatch(name: string) {