diff --git a/CHANGELOG.md b/CHANGELOG.md index 712ba34..4459ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Overhauls the _Git Commands_ (`gitlens.gitCommands`) quick pick menus - Adds many more options - Adds improved titles for better clarity, context, and flow + - Removes the _Keep Open_ toggle button to the quick pick menu toolbar — the behavior is now automatically determined (unless overridden by the `gitlens.gitCommands.closeOnFocusOut` setting) - Changes avatars in the blame file annotations to new be displayed as part of the annotations rather than in the gutter - Changes the _Git Commands' push_ command to honor and reflect the `git.useForcePushWithLease` setting - Changes to use VS Code's built-in icons (codicons) where possible — closes [#985](https://github.com/eamodio/vscode-gitlens/issues/985) diff --git a/package.json b/package.json index 481ac4c..a701592 100644 --- a/package.json +++ b/package.json @@ -536,7 +536,7 @@ }, "gitlens.gitCommands.closeOnFocusOut": { "type": "boolean", - "default": false, + "default": true, "markdownDescription": "Specifies whether to dismiss the Git Commands menu when focus is lost (if not, press `ESC` to dismiss)", "scope": "window" }, diff --git a/src/commands/git/log.ts b/src/commands/git/log.ts index be1b2c7..94e3462 100644 --- a/src/commands/git/log.ts +++ b/src/commands/git/log.ts @@ -153,6 +153,7 @@ export class LogGitCommand extends QuickCommand { } const result = yield* pickCommitStep(state as LogStepState, context, { + ignoreFocusOut: true, log: await log, onDidLoadMore: log => context.cache.set(ref, Promise.resolve(log)), placeholder: (context, log) => diff --git a/src/commands/git/merge.ts b/src/commands/git/merge.ts index d4de3c8..b33b7db 100644 --- a/src/commands/git/merge.ts +++ b/src/commands/git/merge.ts @@ -159,6 +159,7 @@ export class MergeGitCommand extends QuickCommand { } const result: StepResult = yield* pickCommitStep(state as MergeStepState, context, { + ignoreFocusOut: true, log: await log, onDidLoadMore: log => context.cache.set(ref, Promise.resolve(log)), placeholder: (context, log) => diff --git a/src/commands/git/rebase.ts b/src/commands/git/rebase.ts index ab3549f..c503380 100644 --- a/src/commands/git/rebase.ts +++ b/src/commands/git/rebase.ts @@ -160,6 +160,7 @@ export class RebaseGitCommand extends QuickCommand { } const result: StepResult = yield* pickCommitStep(state as RebaseStepState, context, { + ignoreFocusOut: true, log: await log, onDidLoadMore: log => context.cache.set(ref, Promise.resolve(log)), placeholder: (context, log) => diff --git a/src/commands/git/search.ts b/src/commands/git/search.ts index 24c9b4f..5cae397 100644 --- a/src/commands/git/search.ts +++ b/src/commands/git/search.ts @@ -175,6 +175,7 @@ export class SearchGitCommand extends QuickCommand { if (state.counter < 4 || context.commit == null) { const repoPath = state.repo.path; const result = yield* pickCommitStep(state as SearchStepState, context, { + ignoreFocusOut: true, log: await context.resultsPromise, onDidLoadMore: log => (context.resultsPromise = Promise.resolve(log)), placeholder: (context, log) => @@ -316,6 +317,10 @@ export class SearchGitCommand extends QuickCommand { if (quickpick.value.length === 0) { quickpick.items = items; } else { + // If something was typed/selected, keep the quick pick open on focus lossrop + quickpick.ignoreFocusOut = true; + step.ignoreFocusOut = true; + quickpick.items = [ { label: 'Search for', diff --git a/src/commands/gitCommands.ts b/src/commands/gitCommands.ts index 4e14463..7a91420 100644 --- a/src/commands/gitCommands.ts +++ b/src/commands/gitCommands.ts @@ -87,6 +87,8 @@ export class GitCommandsCommand extends Command { const command = args?.command != null ? commandsStep.find(args.command) : undefined; this.startedWith = command != null ? 'command' : 'menu'; + let ignoreFocusOut; + let step = command == null ? commandsStep : await this.getCommandStep(command, commandsStep); while (step != null) { // If we are trying to back up to the menu and have a starting command, then just reset to the starting command @@ -95,13 +97,25 @@ export class GitCommandsCommand extends Command { continue; } + if (ignoreFocusOut && step.ignoreFocusOut == null) { + step.ignoreFocusOut = true; + } + if (isQuickPickStep(step)) { step = await this.showPickStep(step, commandsStep); + if (step?.ignoreFocusOut === true) { + ignoreFocusOut = true; + } + continue; } if (isQuickInputStep(step)) { step = await this.showInputStep(step, commandsStep); + if (step?.ignoreFocusOut === true) { + ignoreFocusOut = true; + } + continue; } @@ -114,7 +128,7 @@ export class GitCommandsCommand extends Command { if (step != null) { if (step.buttons != null) { - buttons.push(...step.buttons, new QuickCommandButtons.KeepOpenToggle()); + buttons.push(...step.buttons); return buttons; } @@ -147,8 +161,6 @@ export class GitCommandsCommand extends Command { } } - buttons.push(new QuickCommandButtons.KeepOpenToggle()); - return buttons; } @@ -178,7 +190,9 @@ export class GitCommandsCommand extends Command { private async showInputStep(step: QuickInputStep, commandsStep: PickCommandStep) { const input = window.createInputBox(); - input.ignoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut'); + input.ignoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut') + ? true + : step.ignoreFocusOut ?? false; const disposables: Disposable[] = []; @@ -290,8 +304,13 @@ export class GitCommandsCommand extends Command { } private async showPickStep(step: QuickPickStep, commandsStep: PickCommandStep) { + const originalIgnoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut') + ? true + : step.ignoreFocusOut ?? false; + const originalStepIgnoreFocusOut = step.ignoreFocusOut; + const quickpick = window.createQuickPick(); - quickpick.ignoreFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut'); + quickpick.ignoreFocusOut = originalIgnoreFocusOut; const disposables: Disposable[] = []; @@ -423,6 +442,17 @@ export class GitCommandsCommand extends Command { if (cancel) return; } + // If something was typed, keep the quick pick open on focus loss + if (e.length !== 0 && !quickpick.ignoreFocusOut) { + quickpick.ignoreFocusOut = true; + step.ignoreFocusOut = true; + } + // If something typed was cleared, and we changed the behavior, then allow the quick pick close on focus loss + else if (e.length === 0 && quickpick.ignoreFocusOut && !originalIgnoreFocusOut) { + quickpick.ignoreFocusOut = originalIgnoreFocusOut; + step.ignoreFocusOut = originalStepIgnoreFocusOut; + } + if (!overrideItems) { if (quickpick.canSelectMany && e === ' ') { quickpick.value = ''; @@ -485,6 +515,20 @@ export class GitCommandsCommand extends Command { quickpick.buttons = this.getButtons(undefined, command); }), + quickpick.onDidChangeSelection(e => { + if (!quickpick.canSelectMany) return; + + // If something was selected, keep the quick pick open on focus loss + if (e.length !== 0 && !quickpick.ignoreFocusOut) { + quickpick.ignoreFocusOut = true; + step.ignoreFocusOut = true; + } + // If the selection was cleared, and we changed the behavior, then allow the quick pick close on focus loss + else if (e?.length === 0 && quickpick.ignoreFocusOut && !originalIgnoreFocusOut) { + quickpick.ignoreFocusOut = originalIgnoreFocusOut; + step.ignoreFocusOut = originalStepIgnoreFocusOut; + } + }), quickpick.onDidAccept(async () => { let items = quickpick.selectedItems; if (items.length === 0) { @@ -600,6 +644,7 @@ export class GitCommandsCommand extends Command { class PickCommandStep implements QuickPickStep { readonly buttons = []; private readonly hiddenItems: QuickCommand[]; + ignoreFocusOut = false; readonly items: QuickCommand[]; readonly matchOnDescription = true; readonly placeholder = 'Choose a git command'; diff --git a/src/commands/quickCommand.buttons.ts b/src/commands/quickCommand.buttons.ts index e0fb67a..876ad49 100644 --- a/src/commands/quickCommand.buttons.ts +++ b/src/commands/quickCommand.buttons.ts @@ -1,7 +1,6 @@ 'use strict'; import { QuickInput, QuickInputButton, ThemeIcon, Uri } from 'vscode'; import { Container } from '../container'; -import { configuration } from '../configuration'; export class ToggleQuickInputButton implements QuickInputButton { constructor( @@ -61,26 +60,6 @@ export namespace QuickCommandButtons { tooltip: 'Fetch', }; - export const KeepOpenToggle = class extends ToggleQuickInputButton { - constructor() { - super( - () => ({ - on: { tooltip: 'Keep Open', icon: new ThemeIcon('pinned') }, - off: { tooltip: 'Keep Open', icon: new ThemeIcon('pin') }, - }), - !configuration.get('gitCommands', 'closeOnFocusOut'), - ); - - this.onDidClick = async input => { - const closeOnFocusOut = !configuration.get('gitCommands', 'closeOnFocusOut'); - this.on = !closeOnFocusOut; - - input.ignoreFocusOut = !closeOnFocusOut; - void (await configuration.updateEffective('gitCommands', 'closeOnFocusOut', closeOnFocusOut)); - }; - } - }; - export const LoadMore: QuickInputButton = { iconPath: new ThemeIcon('refresh'), tooltip: 'Load More', diff --git a/src/commands/quickCommand.steps.ts b/src/commands/quickCommand.steps.ts index 7a40125..0285860 100644 --- a/src/commands/quickCommand.steps.ts +++ b/src/commands/quickCommand.steps.ts @@ -709,6 +709,7 @@ export function* pickCommitStep< state: State, context: Context, { + ignoreFocusOut, log, onDidLoadMore, picked, @@ -716,6 +717,7 @@ export function* pickCommitStep< showInViewButton: showInView, titleContext, }: { + ignoreFocusOut?: boolean; log: GitLog | undefined; onDidLoadMore?: (log: GitLog | undefined) => void; picked?: string | string[] | undefined; @@ -746,6 +748,7 @@ export function* pickCommitStep< const step = QuickCommand.createPickStep({ title: appendReposToTitle(`${context.title}${titleContext ?? ''}`, state, context), placeholder: typeof placeholder === 'string' ? placeholder : placeholder(context, log), + ignoreFocusOut: ignoreFocusOut, matchOnDescription: true, matchOnDetail: true, value: typeof picked === 'string' && log?.count === 0 ? picked : undefined, @@ -1093,11 +1096,13 @@ export function* pickStashStep< state: State, context: Context, { + ignoreFocusOut, stash, picked, placeholder, titleContext, }: { + ignoreFocusOut?: boolean; stash: GitStash | undefined; picked: string | string[] | undefined; placeholder: string | ((context: Context, stash: GitStash | undefined) => string); @@ -1107,6 +1112,7 @@ export function* pickStashStep< const step = QuickCommand.createPickStep>({ title: appendReposToTitle(`${context.title}${titleContext ?? ''}`, state, context), placeholder: typeof placeholder === 'string' ? placeholder : placeholder(context, stash), + ignoreFocusOut: ignoreFocusOut, matchOnDescription: true, matchOnDetail: true, items: @@ -1259,6 +1265,7 @@ export async function* showCommitOrStashStep< context, ), placeholder: GitReference.toString(state.reference, { capitalize: true, icon: false }), + ignoreFocusOut: true, items: await getShowCommitOrStashStepItems(state), additionalButtons: GitReference.isStash(state.reference) ? [QuickCommandButtons.RevealInView] @@ -1461,6 +1468,7 @@ export async function* showCommitOrStashFilesStep< context, ), placeholder: GitReference.toString(state.reference, { capitalize: true, icon: false }), + ignoreFocusOut: true, items: [ new CommitFilesQuickPickItem(state.reference, state.fileName == null), ...state.reference.files.map( @@ -1537,6 +1545,7 @@ export async function* showCommitOrStashFileStep< })} in ${GitReference.toString(state.reference, { icon: false, })}`, + ignoreFocusOut: true, items: await getShowCommitOrStashFileStepItems(state), matchOnDescription: true, additionalButtons: [QuickCommandButtons.RevealInView, QuickCommandButtons.ShowInView], @@ -1728,6 +1737,7 @@ export function* showRepositoryStatusStep< const step: QuickPickStep = QuickCommand.createPickStep({ title: appendReposToTitle(context.title, state, context), placeholder: `${upstream ? `${upstream}, ${working}` : working}`, //'Changes to be committed', + ignoreFocusOut: true, items: getShowRepositoryStatusStepItems(state, context), keys: ['right', 'alt+right', 'ctrl+right'], onDidPressKey: async (quickpick, key) => { diff --git a/src/commands/quickCommand.ts b/src/commands/quickCommand.ts index b55b39c..fe116df 100644 --- a/src/commands/quickCommand.ts +++ b/src/commands/quickCommand.ts @@ -10,6 +10,7 @@ export * from './quickCommand.steps'; export interface QuickInputStep { additionalButtons?: QuickInputButton[]; buttons?: QuickInputButton[]; + ignoreFocusOut?: boolean; keys?: StepNavigationKeys[]; placeholder?: string; prompt?: string; @@ -31,6 +32,7 @@ export interface QuickPickStep { additionalButtons?: QuickInputButton[]; allowEmpty?: boolean; buttons?: QuickInputButton[]; + ignoreFocusOut?: boolean; items: (DirectiveQuickPickItem | T)[]; // | DirectiveQuickPickItem[]; keys?: StepNavigationKeys[]; matchOnDescription?: boolean; @@ -191,13 +193,7 @@ export abstract class QuickCommand implements QuickPickItem { cancel?: DirectiveQuickPickItem, options: Partial> = {}, ): QuickPickStep { - return QuickCommand.createPickStep({ - placeholder: `Confirm ${this.title}`, - title: title, - items: [...confirmations, cancel ?? DirectiveQuickPickItem.create(Directive.Cancel)], - selectedItems: [confirmations.find(c => c.picked) ?? confirmations[0]], - ...options, - }); + return QuickCommand.createConfirmStep(title, confirmations, { title: this.title }, cancel, options); } protected getStepState(limitBackNavigation: boolean): PartialStepState { @@ -283,6 +279,7 @@ export namespace QuickCommand { return createPickStep({ placeholder: `Confirm ${context.title}`, title: title, + ignoreFocusOut: true, items: [...confirmations, cancel ?? DirectiveQuickPickItem.create(Directive.Cancel)], selectedItems: [confirmations.find(c => c.picked) ?? confirmations[0]], ...options, @@ -290,6 +287,8 @@ export namespace QuickCommand { } export function createInputStep(step: QuickInputStep): QuickInputStep { + // Make sure any input steps won't close on focus loss + step.ignoreFocusOut = true; return step; }