ソースを参照

Adds confirmation toggle to git commands

main
Eric Amodio 5年前
コミット
400aac071d
14個のファイルの変更287行の追加166行の削除
  1. +3
    -0
      images/dark/icon-check.svg
  2. +3
    -0
      images/dark/icon-no-check.svg
  3. +3
    -0
      images/light/icon-check.svg
  4. +3
    -0
      images/light/icon-no-check.svg
  5. +29
    -34
      src/commands/git/checkout.ts
  6. +1
    -1
      src/commands/git/cherry-pick.ts
  7. +9
    -16
      src/commands/git/fetch.ts
  8. +1
    -1
      src/commands/git/merge.ts
  9. +45
    -45
      src/commands/git/pull.ts
  10. +9
    -16
      src/commands/git/push.ts
  11. +1
    -1
      src/commands/git/rebase.ts
  12. +34
    -29
      src/commands/git/stash.ts
  13. +120
    -19
      src/commands/gitCommands.ts
  14. +26
    -4
      src/commands/quickCommand.ts

+ 3
- 0
images/dark/icon-check.svg ファイルの表示

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path fill="#C5C5C5" fill-rule="evenodd" d="M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.052-9.506.762.646z" clip-rule="evenodd"/>
</svg>

+ 3
- 0
images/dark/icon-no-check.svg ファイルの表示

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path fill="#C5C5C5" fill-rule="evenodd" d="M14.431 3.323l-3.158 3.73c-.687.1-1.328.339-1.894.688l4.29-5.064.762.646zM7 12v.096l-1.038 1.227-.791-.036-3.35-4.77.818-.574 2.978 4.24 1.75-2.066A4.986 4.986 0 0 0 7 12zm5 3c.648 0 1.248-.205 1.738-.555l-4.183-4.183A3 3 0 0 0 12 15zm-1.738-5.445l4.183 4.183a3 3 0 0 0-4.184-4.184zM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" clip-rule="evenodd"/>
</svg>

+ 3
- 0
images/light/icon-check.svg ファイルの表示

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path fill="#424242" fill-rule="evenodd" d="M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.052-9.506.762.646z" clip-rule="evenodd"/>
</svg>

+ 3
- 0
images/light/icon-no-check.svg ファイルの表示

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16">
<path fill="#424242" fill-rule="evenodd" d="M14.431 3.323l-3.158 3.73c-.687.1-1.328.339-1.894.688l4.29-5.064.762.646zM7 12v.096l-1.038 1.227-.791-.036-3.35-4.77.818-.574 2.978 4.24 1.75-2.066A4.986 4.986 0 0 0 7 12zm5 3c.648 0 1.248-.205 1.738-.555l-4.183-4.183A3 3 0 0 0 12 15zm-1.738-5.445l4.183 4.183a3 3 0 0 0-4.184-4.184zM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" clip-rule="evenodd"/>
</svg>

+ 29
- 34
src/commands/git/checkout.ts ファイルの表示

@ -1,6 +1,6 @@
'use strict';
/* eslint-disable no-loop-func */
import { ProgressLocation, QuickInputButtons, window } from 'vscode';
import { ProgressLocation, QuickInputButton, window } from 'vscode';
import { Container } from '../../container';
import { GitBranch, GitReference, GitTag, Repository } from '../../git/gitService';
import { GlyphChars } from '../../constants';
@ -9,22 +9,24 @@ import { ReferencesQuickPickItem, RefQuickPickItem, RepositoryQuickPickItem } fr
import { Strings } from '../../system';
import { Logger } from '../../logger';
type Mutable<T, K extends keyof T> = Omit<T, K> & { -readonly [P in K]: T[P] };
interface State {
repos: Repository[];
branchOrTagOrRef: GitBranch | GitTag | GitReference;
createBranch?: string;
}
export interface CommandArgs {
export interface CheckoutGitCommandArgs {
readonly command: 'checkout';
state?: Partial<State>;
skipConfirmation?: boolean;
confirm?: boolean;
}
export class CheckoutGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('checkout', 'Checkout');
constructor(args?: CheckoutGitCommandArgs) {
super('checkout', 'checkout', 'Checkout');
if (args === undefined || args.state === undefined) return;
@ -37,16 +39,9 @@ export class CheckoutGitCommand extends QuickCommandBase {
counter++;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(this.label)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 1 && args.skipConfirmation,
confirm: args.confirm,
...args.state
};
}
@ -109,13 +104,22 @@ export class CheckoutGitCommand extends QuickCommandBase {
}
if (state.branchOrTagOrRef === undefined || state.counter < 2) {
const includeTags = showTags || state.repos.length === 1;
showTags = state.repos.length === 1;
const toggleTagsButton: Mutable<QuickInputButton, 'tooltip'> = {
iconPath: {
dark: Container.context.asAbsolutePath('images/dark/icon-tag.svg') as any,
light: Container.context.asAbsolutePath('images/light/icon-tag.svg') as any
},
tooltip: showTags ? 'Hide Tags' : 'Show Tags'
};
const items = await getBranchesAndOrTags(
state.repos,
includeTags,
showTags,
state.repos.length === 1 ? undefined : { filterBranches: b => !b.remote }
);
const step = this.createPickStep<ReferencesQuickPickItem>({
title: `${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1
@ -123,39 +127,30 @@ export class CheckoutGitCommand extends QuickCommandBase {
: `${state.repos.length} repositories`
}`,
placeholder: `Choose a branch${
includeTags ? ' or tag' : ''
showTags ? ' or tag' : ''
} to checkout to${GlyphChars.Space.repeat(3)}(select or enter a reference)`,
matchOnDescription: true,
items: items,
selectedItems: state.branchOrTagOrRef
? items.filter(ref => ref.label === state.branchOrTagOrRef!.ref)
: undefined,
buttons: includeTags
? [QuickInputButtons.Back]
: [
QuickInputButtons.Back,
{
iconPath: {
dark: Container.context.asAbsolutePath('images/dark/icon-tag.svg') as any,
light: Container.context.asAbsolutePath('images/light/icon-tag.svg') as any
},
tooltip: 'Show Tags'
}
],
additionalButtons: [toggleTagsButton],
onDidClickButton: async (quickpick, button) => {
quickpick.busy = true;
quickpick.enabled = false;
if (!showTags) {
showTags = true;
}
showTags = !showTags;
toggleTagsButton.tooltip = showTags ? 'Hide Tags' : 'Show Tags';
quickpick.placeholder = `Choose a branch${
showTags ? ' or tag' : ''
} to checkout to${GlyphChars.Space.repeat(3)}(select or enter a reference)`;
quickpick.buttons = [QuickInputButtons.Back];
quickpick.items = await getBranchesAndOrTags(state.repos!, showTags);
quickpick.items = await getBranchesAndOrTags(
state.repos!,
showTags,
state.repos!.length === 1 ? undefined : { filterBranches: b => !b.remote }
);
quickpick.busy = false;
quickpick.enabled = true;
@ -228,7 +223,7 @@ export class CheckoutGitCommand extends QuickCommandBase {
}
}
if (!state.skipConfirmation) {
if (this.confirm(state.confirm)) {
const step = this.createConfirmStep(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1

+ 1
- 1
src/commands/git/cherry-pick.ts ファイルの表示

@ -25,7 +25,7 @@ interface State {
export class CherryPickGitCommand extends QuickCommandBase<State> {
constructor() {
super('cherry-pick', 'Cherry Pick', { description: 'via Terminal' });
super('cherry-pick', 'cherry-pick', 'Cherry Pick', false, { description: 'via Terminal' });
}
execute(state: State) {

+ 9
- 16
src/commands/git/fetch.ts ファイルの表示

@ -12,16 +12,16 @@ interface State {
flags: string[];
}
export interface CommandArgs {
export interface FetchGitCommandArgs {
readonly command: 'fetch';
state?: Partial<State>;
skipConfirmation?: boolean;
confirm?: boolean;
}
export class FetchGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('fetch', 'Fetch');
constructor(args?: FetchGitCommandArgs) {
super('fetch', 'fetch', 'Fetch');
if (args === undefined || args.state === undefined) return;
@ -30,16 +30,9 @@ export class FetchGitCommand extends QuickCommandBase {
counter++;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(this.label)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 0 && args.skipConfirmation,
confirm: args.confirm,
...args.state
};
}
@ -94,10 +87,7 @@ export class FetchGitCommand extends QuickCommandBase {
}
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
if (this.confirm(state.confirm)) {
const step = this.createConfirmStep<QuickPickItemPlus<string[]>>(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1
@ -159,6 +149,9 @@ export class FetchGitCommand extends QuickCommandBase {
state.flags = selection[0].item;
}
else {
state.flags = [];
}
this.execute(state as State);
break;

+ 1
- 1
src/commands/git/merge.ts ファイルの表示

@ -24,7 +24,7 @@ interface State {
export class MergeGitCommand extends QuickCommandBase<State> {
constructor() {
super('merge', 'Merge', { description: 'via Terminal' });
super('merge', 'merge', 'Merge', false, { description: 'via Terminal' });
}
execute(state: State) {

+ 45
- 45
src/commands/git/pull.ts ファイルの表示

@ -12,16 +12,16 @@ interface State {
flags: string[];
}
export interface CommandArgs {
export interface PullGitCommandArgs {
readonly command: 'pull';
state?: Partial<State>;
skipConfirmation?: boolean;
confirm?: boolean;
}
export class PullGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('pull', 'Pull');
constructor(args?: PullGitCommandArgs) {
super('pull', 'pull', 'Pull');
if (args === undefined || args.state === undefined) return;
@ -30,16 +30,9 @@ export class PullGitCommand extends QuickCommandBase {
counter++;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(this.label)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 0 && args.skipConfirmation,
confirm: args.confirm,
...args.state
};
}
@ -91,44 +84,51 @@ export class PullGitCommand extends QuickCommandBase {
}
}
const step = this.createConfirmStep<QuickPickItemPlus<string[]>>(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1 ? state.repos[0].formattedName : `${state.repos.length} repositories`
}`,
[
{
label: this.title,
description: '',
detail: `Will pull ${
state.repos.length === 1
? state.repos[0].formattedName
: `${state.repos.length} repositories`
}`,
item: []
},
{
label: `${this.title} with Rebase`,
description: '--rebase',
detail: `Will pull with rebase ${
state.repos.length === 1
? state.repos[0].formattedName
: `${state.repos.length} repositories`
}`,
item: ['--rebase']
if (this.confirm(state.confirm)) {
const step = this.createConfirmStep<QuickPickItemPlus<string[]>>(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1
? state.repos[0].formattedName
: `${state.repos.length} repositories`
}`,
[
{
label: this.title,
description: '',
detail: `Will pull ${
state.repos.length === 1
? state.repos[0].formattedName
: `${state.repos.length} repositories`
}`,
item: []
},
{
label: `${this.title} with Rebase`,
description: '--rebase',
detail: `Will pull with rebase ${
state.repos.length === 1
? state.repos[0].formattedName
: `${state.repos.length} repositories`
}`,
item: ['--rebase']
}
]
);
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
if (oneRepo) {
break;
}
]
);
const selection = yield step;
if (!this.canMoveNext(step, state, selection)) {
if (oneRepo) {
break;
continue;
}
continue;
state.flags = selection[0].item;
}
else {
state.flags = [];
}
state.flags = selection[0].item;
this.execute(state as State);
break;

+ 9
- 16
src/commands/git/push.ts ファイルの表示

@ -12,16 +12,16 @@ interface State {
flags: string[];
}
export interface CommandArgs {
export interface PushGitCommandArgs {
readonly command: 'push';
state?: Partial<State>;
skipConfirmation?: boolean;
confirm?: boolean;
}
export class PushGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('push', 'Push');
constructor(args?: PushGitCommandArgs) {
super('push', 'push', 'Push');
if (args === undefined || args.state === undefined) return;
@ -30,16 +30,9 @@ export class PushGitCommand extends QuickCommandBase {
counter++;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(this.label)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 0 && args.skipConfirmation,
confirm: args.confirm,
...args.state
};
}
@ -91,10 +84,7 @@ export class PushGitCommand extends QuickCommandBase {
}
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
if (this.confirm(state.confirm)) {
const step = this.createConfirmStep(
`Confirm ${this.title}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repos.length === 1
@ -136,6 +126,9 @@ export class PushGitCommand extends QuickCommandBase {
state.flags = selection[0].item;
}
else {
state.flags = [];
}
this.execute(state as State);
break;

+ 1
- 1
src/commands/git/rebase.ts ファイルの表示

@ -24,7 +24,7 @@ interface State {
export class RebaseGitCommand extends QuickCommandBase<State> {
constructor() {
super('rebase', 'Rebase', { description: 'via Terminal' });
super('rebase', 'rebase', 'Rebase', false, { description: 'via Terminal' });
}
execute(state: State) {

+ 34
- 29
src/commands/git/stash.ts ファイルの表示

@ -1,5 +1,5 @@
'use strict';
import { QuickPickItem, Uri, window } from 'vscode';
import { QuickInputButtons, QuickPickItem, Uri, window } from 'vscode';
import { Container } from '../../container';
import { GitStashCommit, GitUri, Repository } from '../../git/gitService';
import { BreakQuickCommand, QuickCommandBase, QuickInputStep, QuickPickStep, StepState } from '../quickCommand';
@ -45,18 +45,20 @@ interface PushState {
}
type State = ApplyState | DropState | PopState | PushState;
type StashStepState<T> = Partial<T> & { counter: number; repo: Repository; skipConfirmation?: boolean };
type StashStepState<T> = StepState<T> & { repo: Repository };
export interface CommandArgs {
export interface StashGitCommandArgs {
readonly command: 'stash';
state?: Partial<State>;
skipConfirmation?: boolean;
confirm?: boolean;
}
export class StashGitCommand extends QuickCommandBase<State> {
constructor(args?: CommandArgs) {
super('stash', 'Stash');
private _subcommand: string | undefined;
constructor(args?: StashGitCommandArgs) {
super('stash', 'stash', 'Stash');
if (args === undefined || args.state === undefined) return;
@ -86,20 +88,21 @@ export class StashGitCommand extends QuickCommandBase {
break;
}
if (
args.skipConfirmation === undefined &&
Container.config.gitCommands.skipConfirmations.includes(`${this.label}-${args.state.subcommand}`)
) {
args.skipConfirmation = true;
}
this._initialState = {
counter: counter,
skipConfirmation: counter > 0 && args.skipConfirmation,
confirm: args.confirm,
...args.state
};
}
get canSkipConfirm(): boolean {
return this._subcommand === 'drop' ? false : super.canSkipConfirm;
}
get confirmationKey() {
return this._subcommand === undefined ? undefined : `${super.confirmationKey}-${this._subcommand}`;
}
protected async *steps(): AsyncIterableIterator<QuickPickStep | QuickInputStep> {
const state: StepState<State> = this._initialState === undefined ? { counter: 0 } : this._initialState;
let oneRepo = false;
@ -107,6 +110,8 @@ export class StashGitCommand extends QuickCommandBase {
while (true) {
try {
if (state.subcommand === undefined || state.counter < 1) {
this._subcommand = undefined;
const step = this.createPickStep<QuickPickItemPlus<State['subcommand']>>({
title: this.title,
placeholder: `Choose a ${this.label} command`,
@ -131,7 +136,8 @@ export class StashGitCommand extends QuickCommandBase {
picked: state.subcommand === 'push',
item: 'push'
}
]
],
buttons: [QuickInputButtons.Back]
});
const selection = yield step;
@ -142,6 +148,8 @@ export class StashGitCommand extends QuickCommandBase {
state.subcommand = selection[0].item;
}
this._subcommand = state.subcommand;
if (state.repo === undefined || state.counter < 2) {
const repos = [...(await Container.git.getOrderedRepositories())];
@ -289,10 +297,7 @@ export class StashGitCommand extends QuickCommandBase {
state.stash = selection[0].item;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
if (this.confirm(state.confirm)) {
const message =
state.stash.message.length > 80
? `${state.stash.message.substring(0, 80)}${GlyphChars.Ellipsis}`
@ -344,6 +349,9 @@ export class StashGitCommand extends QuickCommandBase {
state.subcommand = selection[0].command;
state.flags = selection[0].item;
}
else {
state.flags = [];
}
void Container.git.stashApply(state.repo.path, state.stash!.stashName, state.subcommand === 'pop');
@ -380,7 +388,7 @@ export class StashGitCommand extends QuickCommandBase {
}
)
)
]
]
});
const selection = yield step;
@ -391,10 +399,7 @@ export class StashGitCommand extends QuickCommandBase {
state.stash = selection[0].item;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
// if (this.confirm(state.confirm)) {
const message =
state.stash.message.length > 80
? `${state.stash.message.substring(0, 80)}${GlyphChars.Ellipsis}`
@ -418,7 +423,7 @@ export class StashGitCommand extends QuickCommandBase {
if (!this.canMoveNext(step, state, selection)) {
break;
}
}
// }
void Container.git.stashDelete(state.repo.path, state.stash.stashName);
@ -448,10 +453,7 @@ export class StashGitCommand extends QuickCommandBase {
state.message = value;
}
if (state.skipConfirmation) {
state.flags = [];
}
else {
if (this.confirm(state.confirm)) {
const step = this.createConfirmStep<QuickPickItemPlus<string[]>>(
`Confirm ${this.title} ${state.subcommand}${Strings.pad(GlyphChars.Dot, 2, 2)}${
state.repo.formattedName
@ -499,6 +501,9 @@ export class StashGitCommand extends QuickCommandBase {
state.flags = selection[0].item;
}
else {
state.flags = [];
}
void Container.git.stashSave(state.repo.path, state.message, state.uris, {
includeUntracked: state.flags.includes('--include-untracked'),

+ 120
- 19
src/commands/gitCommands.ts ファイルの表示

@ -1,26 +1,28 @@
'use strict';
import { Disposable, InputBox, QuickInputButtons, QuickPick, QuickPickItem, window } from 'vscode';
import { Disposable, InputBox, QuickInputButton, QuickInputButtons, QuickPick, QuickPickItem, window } from 'vscode';
import { command, Command, Commands } from './common';
import { log } from '../system';
import { isQuickInputStep, isQuickPickStep, QuickCommandBase, QuickInputStep, QuickPickStep } from './quickCommand';
import { Directive, DirectiveQuickPickItem } from '../quickpicks';
import { CommandArgs as CheckoutCommandArgs, CheckoutGitCommand } from './git/checkout';
import { CheckoutGitCommand, CheckoutGitCommandArgs } from './git/checkout';
import { CherryPickGitCommand } from './git/cherry-pick';
import { CommandArgs as FetchCommandArgs, FetchGitCommand } from './git/fetch';
import { FetchGitCommand, FetchGitCommandArgs } from './git/fetch';
import { MergeGitCommand } from './git/merge';
import { CommandArgs as PullCommandArgs, PullGitCommand } from './git/pull';
import { CommandArgs as PushCommandArgs, PushGitCommand } from './git/push';
import { PullGitCommand, PullGitCommandArgs } from './git/pull';
import { PushGitCommand, PushGitCommandArgs } from './git/push';
import { RebaseGitCommand } from './git/rebase';
import { CommandArgs as StashCommandArgs, StashGitCommand } from './git/stash';
import { StashGitCommand, StashGitCommandArgs } from './git/stash';
import { Container } from '../container';
import { configuration } from '../configuration';
const sanitizeLabel = /\$\(.+?\)|\W/g;
export type GitCommandsCommandArgs =
| CheckoutCommandArgs
| FetchCommandArgs
| PullCommandArgs
| PushCommandArgs
| StashCommandArgs;
| CheckoutGitCommandArgs
| FetchGitCommandArgs
| PullGitCommandArgs
| PushGitCommandArgs
| StashGitCommandArgs;
class PickCommandStep implements QuickPickStep {
readonly buttons = [];
@ -65,6 +67,32 @@ class PickCommandStep implements QuickPickStep {
@command()
export class GitCommandsCommand extends Command {
private readonly GitQuickInputButtons = class {
static readonly WillConfirm: QuickInputButton = {
iconPath: {
dark: Container.context.asAbsolutePath('images/dark/icon-check.svg') as any,
light: Container.context.asAbsolutePath('images/light/icon-check.svg') as any
},
tooltip: 'Will ask for confirmation at the end (click to toggle)'
};
static readonly WillConfirmForced: QuickInputButton = {
iconPath: {
dark: Container.context.asAbsolutePath('images/dark/icon-check.svg') as any,
light: Container.context.asAbsolutePath('images/light/icon-check.svg') as any
},
tooltip: 'Will ask for confirmation at the end (cannot be changed)'
};
static readonly WillSkipConfirm: QuickInputButton = {
iconPath: {
dark: Container.context.asAbsolutePath('images/dark/icon-no-check.svg') as any,
light: Container.context.asAbsolutePath('images/light/icon-no-check.svg') as any
},
tooltip: 'Will NOT ask for confirmation at the end (click to toggle)'
};
};
constructor() {
super(Commands.GitCommands);
}
@ -123,11 +151,21 @@ export class GitCommandsCommand extends Command {
return;
}
const step = commandsStep.command && commandsStep.command.value;
if (step === undefined || !isQuickInputStep(step) || step.onDidClickButton === undefined)
if (e === this.GitQuickInputButtons.WillConfirmForced) return;
if (
e === this.GitQuickInputButtons.WillConfirm ||
e === this.GitQuickInputButtons.WillSkipConfirm
) {
await this.toggleConfirmation(input, commandsStep.command);
return;
}
step.onDidClickButton(input, e);
const step = commandsStep.command && commandsStep.command.value;
if (step !== undefined && isQuickInputStep(step) && step.onDidClickButton !== undefined) {
step.onDidClickButton(input, e);
input.buttons = this.getButtons(step, commandsStep.command);
}
}),
input.onDidChangeValue(async e => {
if (step.validate === undefined) return;
@ -140,7 +178,7 @@ export class GitCommandsCommand extends Command {
})
);
input.buttons = step.buttons || [QuickInputButtons.Back];
input.buttons = this.getButtons(step, commandsStep.command);
input.title = step.title;
input.placeholder = step.placeholder;
if (step.value !== undefined) {
@ -184,10 +222,21 @@ export class GitCommandsCommand extends Command {
return;
}
const step = commandsStep.command && commandsStep.command.value;
if (step === undefined || !isQuickPickStep(step) || step.onDidClickButton === undefined) return;
if (e === this.GitQuickInputButtons.WillConfirmForced) return;
if (
e === this.GitQuickInputButtons.WillConfirm ||
e === this.GitQuickInputButtons.WillSkipConfirm
) {
await this.toggleConfirmation(quickpick, commandsStep.command);
step.onDidClickButton(quickpick, e);
return;
}
const step = commandsStep.command && commandsStep.command.value;
if (step !== undefined && isQuickPickStep(step) && step.onDidClickButton !== undefined) {
step.onDidClickButton(quickpick, e);
quickpick.buttons = this.getButtons(step, commandsStep.command);
}
}),
quickpick.onDidChangeValue(async e => {
if (!overrideItems) {
@ -308,7 +357,6 @@ export class GitCommandsCommand extends Command {
})
);
quickpick.buttons = step.buttons || [QuickInputButtons.Back];
quickpick.title = step.title;
quickpick.placeholder = step.placeholder;
quickpick.matchOnDescription = Boolean(step.matchOnDescription);
@ -330,6 +378,9 @@ export class GitCommandsCommand extends Command {
commandsStep.command = undefined;
}
// Needs to be after we reset the command
quickpick.buttons = this.getButtons(step, commandsStep.command);
if (step.value !== undefined) {
quickpick.value = step.value;
}
@ -343,6 +394,31 @@ export class GitCommandsCommand extends Command {
}
}
private getButtons(step: QuickInputStep | QuickPickStep, command?: QuickCommandBase) {
if (command === undefined) return [];
if (step.buttons !== undefined) return step.buttons;
const buttons = [QuickInputButtons.Back];
if (step.additionalButtons !== undefined) {
buttons.push(...step.additionalButtons);
}
if (command.canSkipConfirm) {
if (command.confirmationKey === undefined) return buttons;
buttons.push(
command.confirm() ? this.GitQuickInputButtons.WillConfirm : this.GitQuickInputButtons.WillSkipConfirm
);
}
else {
buttons.push(this.GitQuickInputButtons.WillConfirmForced);
}
return buttons;
}
private async nextStep(
quickInput: QuickPick<QuickPickItem> | InputBox,
command: QuickCommandBase,
@ -357,4 +433,29 @@ export class GitCommandsCommand extends Command {
quickInput.value = '';
return next.value;
}
private async toggleConfirmation(
input: InputBox | QuickPick<QuickPickItem>,
command: QuickCommandBase | undefined
) {
if (command === undefined || command.confirmationKey === undefined) return;
const section = configuration.name('gitCommands')('skipConfirmations').value;
const skipConfirmations = configuration.get<string[]>(section) || [];
const index = skipConfirmations.indexOf(command.confirmationKey);
if (index !== -1) {
skipConfirmations.splice(index, 1);
}
else {
skipConfirmations.push(command.confirmationKey);
}
void (await configuration.updateEffective(
configuration.name('gitCommands')('skipConfirmations').value,
skipConfirmations
));
input.buttons = this.getButtons(command.value!, command);
}
}

+ 26
- 4
src/commands/quickCommand.ts ファイルの表示

@ -1,7 +1,8 @@
'use strict';
import { InputBox, QuickInputButton, QuickPick, QuickPickItem } from 'vscode';
import { InputBox, QuickInputButton, QuickInputButtons, QuickPick, QuickPickItem } from 'vscode';
import { Promises } from '../system';
import { Directive, DirectiveQuickPickItem } from '../quickpicks';
import { Container } from '../container';
export * from './quickCommand.helpers';
@ -12,6 +13,7 @@ export class BreakQuickCommand extends Error {
}
export interface QuickInputStep {
additionalButtons?: QuickInputButton[];
buttons?: QuickInputButton[];
placeholder?: string;
title?: string;
@ -26,6 +28,7 @@ export function isQuickInputStep(item: QuickPickStep | QuickInputStep): item is
}
export interface QuickPickStep<T extends QuickPickItem = any> {
additionalButtons?: QuickInputButton[];
buttons?: QuickInputButton[];
selectedItems?: QuickPickItem[];
items: (DirectiveQuickPickItem | T)[] | DirectiveQuickPickItem[];
@ -46,7 +49,7 @@ export function isQuickPickStep(item: QuickPickStep | QuickInputStep): item is Q
return (item as QuickPickStep).items !== undefined;
}
export type StepState<T> = Partial<T> & { counter: number; skipConfirmation?: boolean };
export type StepState<T> = Partial<T> & { counter: number; confirm?: boolean };
export abstract class QuickCommandBase<T = any> implements QuickPickItem {
static is(item: QuickPickItem): item is QuickCommandBase {
@ -56,12 +59,16 @@ export abstract class QuickCommandBase implements QuickPickItem {
readonly description?: string;
readonly detail?: string;
protected _initialState?: StepState<T>;
private _current: QuickPickStep | QuickInputStep | undefined;
private _stepsIterator: AsyncIterableIterator<QuickPickStep | QuickInputStep> | undefined;
constructor(
public readonly key: string,
public readonly label: string,
public readonly title: string,
private readonly _canSkipConfirm: boolean = true,
options: {
description?: string;
detail?: string;
@ -71,6 +78,14 @@ export abstract class QuickCommandBase implements QuickPickItem {
this.detail = options.detail;
}
get canSkipConfirm(): boolean {
return this._canSkipConfirm;
}
get confirmationKey(): string | undefined {
return this.key;
}
private _picked: boolean = false;
get picked() {
return this._picked;
@ -79,7 +94,13 @@ export abstract class QuickCommandBase implements QuickPickItem {
this._picked = value;
}
protected _initialState?: StepState<T>;
confirm(override?: boolean) {
if (!this.canSkipConfirm || this.confirmationKey === undefined) return true;
return override !== undefined
? override
: !Container.config.gitCommands.skipConfirmations.includes(this.confirmationKey);
}
protected abstract steps(): AsyncIterableIterator<QuickPickStep | QuickInputStep>;
@ -122,7 +143,8 @@ export abstract class QuickCommandBase implements QuickPickItem {
placeholder: placeholder || `Confirm ${this.title}`,
title: title,
items: [...confirmations, cancel || DirectiveQuickPickItem.create(Directive.Cancel)],
selectedItems: [confirmations[0]]
selectedItems: [confirmations[0]],
buttons: [QuickInputButtons.Back]
});
}

読み込み中…
キャンセル
保存