Browse Source

Improves quick pick buttons -- moves many to items

Uses new quick pick buttons api
main
Eric Amodio 3 years ago
parent
commit
32fad7f91f
16 changed files with 756 additions and 636 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +0
    -22
      src/@types/timeline.d.ts
  3. +0
    -0
      src/@types/vscode.git.d.ts
  4. +7
    -0
      src/@types/vscode.git.timeline.d.ts
  5. +21
    -0
      src/@types/vscode.proposed.quickPickItemButtons.d.ts
  6. +15
    -0
      src/@types/vscode.proposed.quickPickKeepScrollPosition.d.ts
  7. +162
    -0
      src/@types/vscode.proposed.timeline.d.ts
  8. +4
    -1
      src/commands/gitCommands.ts
  9. +1
    -19
      src/commands/openOnRemote.ts
  10. +163
    -201
      src/commands/quickCommand.steps.ts
  11. +1
    -0
      src/commands/quickCommand.ts
  12. +2
    -1
      src/git/gitService.ts
  13. +23
    -4
      src/quickpicks/gitQuickPickItems.ts
  14. +16
    -1
      src/quickpicks/referencePicker.ts
  15. +35
    -82
      src/quickpicks/remoteProviderPicker.ts

+ 1
- 0
CHANGELOG.md View File

@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Adds commit message autolinking of merged pull requests for Azure Repos — closes [#1486](https://github.com/eamodio/vscode-gitlens/issues/1486) thanks to [PR #1487](https://github.com/eamodio/vscode-gitlens/pull/1487) by Mark Molinaro ([@markjm](https://github.com/markjm))
- Adds a new `AzureDevOps` type to `gitlens.remotes` to better support Azure DevOps remote matching — thanks to [PR #1487](https://github.com/eamodio/vscode-gitlens/pull/1487) by Dmitry Gurovich ([@yrtimiD](https://github.com/yrtimiD))
- Adds functional groupings to all GitLens settings when using the VS Code settings UI. Groups will be displayed in the table of contents in the settings UI — thanks to @rzhao271 for adding grouping support to VS Code settings UI
- Adds new action buttons on many quick pick menu options, including in the _Git Command Palette_ — thanks to @tylerLeonhardt for the VS Code API support
### Changed

+ 0
- 22
src/@types/timeline.d.ts View File

@ -1,22 +0,0 @@
import { AccessibilityInformation, Command, ThemeIcon, Uri } from 'vscode';
declare module 'vscode' {
export interface TimelineItem {
readonly timestamp: number;
readonly label: string;
readonly id?: string;
readonly iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
readonly description?: string;
readonly detail?: string;
readonly command?: Command;
readonly contextValue?: string;
readonly accessibilityInformation?: AccessibilityInformation;
}
export interface GitTimelineItem extends TimelineItem {
readonly ref: string;
readonly previousRef: string;
readonly message: string;
}
}

src/@types/git.d.ts → src/@types/vscode.git.d.ts View File


+ 7
- 0
src/@types/vscode.git.timeline.d.ts View File

@ -0,0 +1,7 @@
declare module 'vscode' {
export interface GitTimelineItem extends TimelineItem {
readonly ref: string;
readonly previousRef: string;
readonly message: string;
}
}

+ 21
- 0
src/@types/vscode.proposed.quickPickItemButtons.d.ts View File

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// https://github.com/microsoft/vscode/issues/88716
declare module 'vscode' {
export interface QuickPickItem {
buttons?: QuickInputButton[];
}
export interface QuickPick<T extends QuickPickItem> extends QuickInput {
readonly onDidTriggerItemButton: Event<QuickPickItemButtonEvent<T>>;
}
export interface QuickPickItemButtonEvent<T extends QuickPickItem> {
button: QuickInputButton;
item: T;
}
}

+ 15
- 0
src/@types/vscode.proposed.quickPickKeepScrollPosition.d.ts View File

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// https://github.com/microsoft/vscode/issues/132068
declare module 'vscode' {
export interface QuickPick<T extends QuickPickItem> extends QuickInput {
/*
* An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false.
*/
keepScrollPosition?: boolean;
}
}

+ 162
- 0
src/@types/vscode.proposed.timeline.d.ts View File

@ -0,0 +1,162 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module 'vscode' {
// https://github.com/microsoft/vscode/issues/84297
export class TimelineItem {
/**
* A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred.
*/
timestamp: number;
/**
* A human-readable string describing the timeline item.
*/
label: string;
/**
* Optional id for the timeline item. It must be unique across all the timeline items provided by this source.
*
* If not provided, an id is generated using the timeline item's timestamp.
*/
id?: string;
/**
* The icon path or {@link ThemeIcon} for the timeline item.
*/
iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
/**
* A human readable string describing less prominent details of the timeline item.
*/
description?: string;
/**
* The tooltip text when you hover over the timeline item.
*/
detail?: string;
/**
* The {@link Command} that should be executed when the timeline item is selected.
*/
command?: Command;
/**
* Context value of the timeline item. This can be used to contribute specific actions to the item.
* For example, a timeline item is given a context value as `commit`. When contributing actions to `timeline/item/context`
* using `menus` extension point, you can specify context value for key `timelineItem` in `when` expression like `timelineItem == commit`.
* ```
* "contributes": {
* "menus": {
* "timeline/item/context": [
* {
* "command": "extension.copyCommitId",
* "when": "timelineItem == commit"
* }
* ]
* }
* }
* ```
* This will show the `extension.copyCommitId` action only for items where `contextValue` is `commit`.
*/
contextValue?: string;
/**
* Accessibility information used when screen reader interacts with this timeline item.
*/
accessibilityInformation?: AccessibilityInformation;
/**
* @param label A human-readable string describing the timeline item
* @param timestamp A timestamp (in milliseconds since 1 January 1970 00:00:00) for when the timeline item occurred
*/
constructor(label: string, timestamp: number);
}
export interface TimelineChangeEvent {
/**
* The {@link Uri} of the resource for which the timeline changed.
*/
uri: Uri;
/**
* A flag which indicates whether the entire timeline should be reset.
*/
reset?: boolean;
}
export interface Timeline {
readonly paging?: {
/**
* A provider-defined cursor specifying the starting point of timeline items which are after the ones returned.
* Use `undefined` to signal that there are no more items to be returned.
*/
readonly cursor: string | undefined;
};
/**
* An array of {@link TimelineItem timeline items}.
*/
readonly items: readonly TimelineItem[];
}
export interface TimelineOptions {
/**
* A provider-defined cursor specifying the starting point of the timeline items that should be returned.
*/
cursor?: string;
/**
* An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned.
* If `undefined` all timeline items should be returned.
*/
limit?: number | { timestamp: number; id?: string };
}
export interface TimelineProvider {
/**
* An optional event to signal that the timeline for a source has changed.
* To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`.
*/
onDidChange?: Event<TimelineChangeEvent | undefined>;
/**
* An identifier of the source of the timeline items. This can be used to filter sources.
*/
readonly id: string;
/**
* A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources.
*/
readonly label: string;
/**
* Provide {@link TimelineItem timeline items} for a {@link Uri}.
*
* @param uri The {@link Uri} of the file to provide the timeline for.
* @param options A set of options to determine how results should be returned.
* @param token A cancellation token.
* @return The {@link TimelineResult timeline result} or a thenable that resolves to such. The lack of a result
* can be signaled by returning `undefined`, `null`, or an empty array.
*/
provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult<Timeline>;
}
export namespace workspace {
/**
* Register a timeline provider.
*
* Multiple providers can be registered. In that case, providers are asked in
* parallel and the results are merged. A failing provider (rejected promise or exception) will
* not cause a failure of the whole operation.
*
* @param scheme A scheme or schemes that defines which documents this provider is applicable to. Can be `*` to target all documents.
* @param provider A timeline provider.
* @return A {@link Disposable} that unregisters this provider when being disposed.
*/
export function registerTimelineProvider(scheme: string | string[], provider: TimelineProvider): Disposable;
}
}

+ 4
- 1
src/commands/gitCommands.ts View File

@ -403,6 +403,7 @@ export class GitCommandsCommand extends Command {
const originalStepIgnoreFocusOut = step.ignoreFocusOut;
const quickpick = window.createQuickPick();
(quickpick as any).enableProposedApi = true;
quickpick.ignoreFocusOut = originalIgnoreFocusOut;
const disposables: Disposable[] = [];
@ -467,7 +468,9 @@ export class GitCommandsCommand extends Command {
disposables.push(
scope,
quickpick.onDidHide(() => resolve(undefined)),
quickpick.onDidTriggerItemButton(async e =>
step.onDidClickItemButton?.(quickpick, e.button, e.item),
),
quickpick.onDidTriggerButton(async e => {
if (e === QuickInputButtons.Back) {
void goBack();

+ 1
- 19
src/commands/openOnRemote.ts View File

@ -4,11 +4,7 @@ import { Container } from '../container';
import { GitRemote, GitRevision, RemoteProvider, RemoteResource, RemoteResourceType } from '../git/git';
import { Logger } from '../logger';
import { Messages } from '../messages';
import {
CopyOrOpenRemoteCommandQuickPickItem,
RemoteProviderPicker,
SetADefaultRemoteCommandQuickPickItem,
} from '../quickpicks';
import { RemoteProviderPicker } from '../quickpicks';
import { Strings } from '../system';
import { Command, command, Commands } from './common';
@ -157,20 +153,6 @@ export class OpenOnRemoteCommand extends Command {
}
const pick = await RemoteProviderPicker.show(title, placeHolder, args.resource, remotes, options);
if (pick instanceof SetADefaultRemoteCommandQuickPickItem) {
const remote = await pick.execute();
if (remote != null) {
void (await new CopyOrOpenRemoteCommandQuickPickItem(
remote,
args.resource,
args.clipboard,
).execute());
}
return;
}
void (await pick?.execute());
} catch (ex) {
Logger.error(ex, 'OpenOnRemoteCommand');

+ 163
- 201
src/commands/quickCommand.steps.ts View File

@ -109,9 +109,15 @@ export function appendReposToTitle<
export async function getBranches(
repos: Repository | Repository[],
options: { filter?: (b: GitBranch) => boolean; picked?: string | string[]; sort?: BranchSortOptions } = {},
options: {
buttons?: QuickInputButton[];
filter?: (b: GitBranch) => boolean;
picked?: string | string[];
sort?: BranchSortOptions;
},
): Promise<BranchQuickPickItem[]> {
return getBranchesAndOrTags(repos, ['branches'], {
buttons: options?.buttons,
filter: options?.filter != null ? { branches: options.filter } : undefined,
picked: options?.picked,
sort: options?.sort != null ? { branches: options.sort } : true,
@ -120,9 +126,15 @@ export async function getBranches(
export async function getTags(
repos: Repository | Repository[],
options?: { filter?: (t: GitTag) => boolean; picked?: string | string[]; sort?: TagSortOptions },
options?: {
buttons?: QuickInputButton[];
filter?: (t: GitTag) => boolean;
picked?: string | string[];
sort?: TagSortOptions;
},
): Promise<TagQuickPickItem[]> {
return getBranchesAndOrTags(repos, ['tags'], {
buttons: options?.buttons,
filter: options?.filter != null ? { tags: options.filter } : undefined,
picked: options?.picked,
sort: options?.sort != null ? { tags: options.sort } : true,
@ -133,10 +145,12 @@ export async function getBranchesAndOrTags(
repos: Repository | Repository[],
include: ('tags' | 'branches')[],
{
buttons,
filter,
picked,
sort,
}: {
buttons?: QuickInputButton[];
filter?: { branches?: (b: GitBranch) => boolean; tags?: (t: GitTag) => boolean };
picked?: string | string[];
sort?: boolean | { branches?: BranchSortOptions; tags?: TagSortOptions };
@ -200,6 +214,7 @@ export async function getBranchesAndOrTags(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
buttons: buttons,
current: singleRepo ? 'checkmark' : false,
ref: singleRepo,
status: singleRepo,
@ -217,6 +232,7 @@ export async function getBranchesAndOrTags(
t,
picked != null && (typeof picked === 'string' ? t.ref === picked : picked.includes(t.ref)),
{
buttons: buttons,
message: false, //singleRepo,
ref: singleRepo,
},
@ -233,6 +249,7 @@ export async function getBranchesAndOrTags(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
buttons: buttons,
current: singleRepo ? 'checkmark' : false,
ref: singleRepo,
status: singleRepo,
@ -244,6 +261,7 @@ export async function getBranchesAndOrTags(
t,
picked != null && (typeof picked === 'string' ? t.ref === picked : picked.includes(t.ref)),
{
buttons: buttons,
message: false, //singleRepo,
ref: singleRepo,
type: true,
@ -257,6 +275,7 @@ export async function getBranchesAndOrTags(
b,
picked != null && (typeof picked === 'string' ? b.ref === picked : picked.includes(b.ref)),
{
buttons: buttons,
current: singleRepo ? 'checkmark' : false,
ref: singleRepo,
status: singleRepo,
@ -267,7 +286,10 @@ export async function getBranchesAndOrTags(
]);
}
export function getValidateGitReferenceFn(repos: Repository | Repository[], options?: { ranges?: boolean }) {
export function getValidateGitReferenceFn(
repos: Repository | Repository[],
options?: { buttons?: QuickInputButton[]; ranges?: boolean },
) {
return async (quickpick: QuickPick<any>, value: string) => {
let inRefMode = false;
if (value.startsWith('#')) {
@ -283,7 +305,12 @@ export function getValidateGitReferenceFn(repos: Repository | Repository[], opti
if (inRefMode && options?.ranges && GitRevision.isRange(value)) {
quickpick.items = [
RefQuickPickItem.create(value, repos.path, true, { alwaysShow: true, ref: false, icon: false }),
RefQuickPickItem.create(value, repos.path, true, {
alwaysShow: true,
buttons: options?.buttons,
ref: false,
icon: false,
}),
];
return true;
}
@ -312,7 +339,14 @@ export function getValidateGitReferenceFn(repos: Repository | Repository[], opti
}
const commit = await Container.git.getCommit(repos.path, value);
quickpick.items = [CommitQuickPickItem.create(commit!, true, { alwaysShow: true, compact: true, icon: true })];
quickpick.items = [
CommitQuickPickItem.create(commit!, true, {
alwaysShow: true,
buttons: options?.buttons,
compact: true,
icon: true,
}),
];
return true;
};
}
@ -406,6 +440,7 @@ export async function* pickBranchStep<
},
): AsyncStepResultGenerator<GitBranchReference> {
const branches = await getBranches(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
});
@ -418,19 +453,9 @@ export async function* pickBranchStep<
branches.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: branches,
additionalButtons: [QuickCommandButtons.RevealInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
void Container.repositoriesView.revealBranches(state.repo.path, {
select: true,
expand: true,
});
return;
}
void GitActions.Branch.reveal(quickpick.activeItems[0].item, {
void GitActions.Branch.reveal(item, {
select: true,
expand: true,
});
@ -472,6 +497,7 @@ export async function* pickBranchesStep<
},
): AsyncStepResultGenerator<GitBranchReference[]> {
const branches = await getBranches(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
sort: sort,
@ -486,19 +512,9 @@ export async function* pickBranchesStep<
branches.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: branches,
additionalButtons: [QuickCommandButtons.RevealInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
void Container.repositoriesView.revealBranches(state.repo.path, {
select: true,
expand: true,
});
return;
}
void GitActions.Branch.reveal(quickpick.activeItems[0].item, {
void GitActions.Branch.reveal(item, {
select: true,
expand: true,
});
@ -549,6 +565,7 @@ export async function* pickBranchOrTagStep<
const getBranchesAndOrTagsFn = async () => {
return getBranchesAndOrTags(state.repo, context.showTags ? ['branches', 'tags'] : ['branches'], {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
sort: true,
@ -571,7 +588,18 @@ export async function* pickBranchOrTagStep<
branchesAndOrTags.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: branchesAndOrTags,
additionalButtons: [QuickCommandButtons.RevealInSideBar, ...(additionalButtons ?? []), showTagsButton],
additionalButtons: [...(additionalButtons ?? []), showTagsButton],
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (GitReference.isBranch(item)) {
void GitActions.Branch.reveal(item, { select: true, expand: true });
} else if (GitReference.isTag(item)) {
void GitActions.Tag.reveal(item, { select: true, expand: true });
} else if (GitReference.isRevision(item)) {
void GitActions.Commit.reveal(item, { select: true, expand: true });
}
}
},
onDidClickButton: async (quickpick, button) => {
if (button === showTagsButton) {
quickpick.busy = true;
@ -593,28 +621,6 @@ export async function* pickBranchOrTagStep<
quickpick.busy = false;
quickpick.enabled = true;
}
return;
}
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
void Container.repositoriesView.revealBranches(state.repo.path, {
select: true,
expand: true,
});
return;
}
const item = quickpick.activeItems[0].item;
if (GitReference.isBranch(item)) {
void GitActions.Branch.reveal(item, { select: true, expand: true });
} else if (GitReference.isTag(item)) {
void GitActions.Tag.reveal(item, { select: true, expand: true });
} else if (GitReference.isRevision(item)) {
void GitActions.Commit.reveal(item, { select: true, expand: true });
}
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
@ -662,6 +668,7 @@ export async function* pickBranchOrTagStepMultiRepo<
const getBranchesAndOrTagsFn = () => {
return getBranchesAndOrTags(state.repos, context.showTags ? ['branches', 'tags'] : ['branches'], {
buttons: [QuickCommandButtons.RevealInSideBar],
// Filter out remote branches if we are going to affect multiple repos
filter: { branches: state.repos.length === 1 ? undefined : b => !b.remote, ...filter },
picked: picked ?? state.reference?.ref,
@ -685,7 +692,18 @@ export async function* pickBranchOrTagStepMultiRepo<
branchesAndOrTags.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: branchesAndOrTags,
additionalButtons: [QuickCommandButtons.RevealInSideBar, showTagsButton],
additionalButtons: [showTagsButton],
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (GitReference.isBranch(item)) {
void GitActions.Branch.reveal(item, { select: true, expand: true });
} else if (GitReference.isTag(item)) {
void GitActions.Tag.reveal(item, { select: true, expand: true });
} else if (GitReference.isRevision(item)) {
void GitActions.Commit.reveal(item, { select: true, expand: true });
}
}
},
onDidClickButton: async (quickpick, button) => {
if (button === showTagsButton) {
quickpick.busy = true;
@ -708,28 +726,6 @@ export async function* pickBranchOrTagStepMultiRepo<
quickpick.enabled = true;
}
}
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
if (state.repos.length === 1) {
void Container.repositoriesView.revealBranches(state.repos[0].path, {
select: true,
expand: true,
});
}
return;
}
const item = quickpick.activeItems[0].item;
if (GitReference.isBranch(item)) {
void GitActions.Branch.reveal(item, { select: true, expand: true });
} else if (GitReference.isTag(item)) {
void GitActions.Tag.reveal(item, { select: true, expand: true });
} else if (GitReference.isRevision(item)) {
void GitActions.Commit.reveal(item, { select: true, expand: true });
}
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
onDidPressKey: quickpick => {
@ -789,7 +785,11 @@ export async function* pickCommitStep<
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),
{ compact: true, icon: true },
{
buttons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar],
compact: true,
icon: true,
},
),
),
...(log?.hasMore ? [DirectiveQuickPickItem.create(Directive.LoadMore)] : []),
@ -805,6 +805,7 @@ export async function* pickCommitStep<
value: typeof picked === 'string' && log?.count === 0 ? picked : undefined,
items: showInSideBarCommand != null ? [showInSideBarCommand, ...getItems(log)] : getItems(log),
onDidLoadMore: async quickpick => {
quickpick.keepScrollPosition = true;
log = await log?.more?.(configuration.get('advanced.maxListItems'));
onDidLoadMore?.(log);
if (typeof placeholder !== 'string') {
@ -813,10 +814,39 @@ export async function* pickCommitStep<
return getItems(log);
},
additionalButtons: [
QuickCommandButtons.RevealInSideBar,
showInSideBar?.button ?? QuickCommandButtons.SearchInSideBar,
...(showInSideBar?.button != null ? [showInSideBar?.button] : []),
...(log?.hasMore ? [QuickCommandButtons.LoadMore] : []),
],
onDidClickItemButton: (quickpick, button, item) => {
if (CommandQuickPickItem.is(item)) return;
switch (button) {
case QuickCommandButtons.RevealInSideBar:
void GitActions.Commit.reveal(item.item, {
select: true,
focus: false,
expand: true,
});
break;
case QuickCommandButtons.SearchInSideBar:
void Container.searchAndCompareView.search(
state.repo.path,
{ pattern: SearchPattern.fromCommit(item.item.ref) },
{
label: {
label: `for ${GitReference.toString(item.item, { icon: false })}`,
},
reveal: {
select: true,
focus: false,
expand: true,
},
},
);
break;
}
},
onDidClickButton: (quickpick, button) => {
if (log == null) return;
@ -826,37 +856,6 @@ export async function* pickCommitStep<
if (button === showInSideBar?.button) {
showInSideBar.onDidClick(items);
return;
}
if (items.length === 0 || log == null) return;
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Commit.reveal(items[0].item, {
select: true,
focus: false,
expand: true,
});
return;
}
if (button === QuickCommandButtons.SearchInSideBar) {
void Container.searchAndCompareView.search(
state.repo.path,
{ pattern: SearchPattern.fromCommit(items[0].item.ref) },
{
label: {
label: `for ${GitReference.toString(items[0].item, { icon: false })}`,
},
reveal: {
select: true,
focus: false,
expand: true,
},
},
);
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
@ -889,7 +888,9 @@ export async function* pickCommitStep<
);
}
},
onValidateValue: getValidateGitReferenceFn(state.repo),
onValidateValue: getValidateGitReferenceFn(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar],
}),
});
const selection: StepSelection<typeof step> = yield step;
if (!QuickCommand.canPickStepContinue(step, state, selection)) return StepResult.Break;
@ -933,10 +934,14 @@ export function* pickCommitsStep<
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),
{ compact: true, icon: true },
{
buttons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar],
compact: true,
icon: true,
},
),
),
// Since this is multi-select, we can have a "Load more" item
// Since this is multi-select, we can't have a "Load more" item
// ...(log?.hasMore ? [DirectiveQuickPickItem.create(Directive.LoadMore)] : []),
];
}
@ -949,6 +954,7 @@ export function* pickCommitsStep<
matchOnDetail: true,
items: getItems(log),
onDidLoadMore: async quickpick => {
quickpick.keepScrollPosition = true;
log = await log?.more?.(configuration.get('advanced.maxListItems'));
onDidLoadMore?.(log);
if (typeof placeholder !== 'string') {
@ -956,39 +962,33 @@ export function* pickCommitsStep<
}
return getItems(log);
},
additionalButtons: [
QuickCommandButtons.RevealInSideBar,
QuickCommandButtons.SearchInSideBar,
...(log?.hasMore ? [QuickCommandButtons.LoadMore] : []),
],
onDidClickButton: (quickpick, button) => {
if (quickpick.activeItems.length === 0 || log == null) return;
if (button === QuickCommandButtons.RevealInSideBar) {
void GitActions.Commit.reveal(quickpick.activeItems[0].item, {
select: true,
focus: false,
expand: true,
});
return;
}
additionalButtons: [...(log?.hasMore ? [QuickCommandButtons.LoadMore] : [])],
onDidClickItemButton: (quickpick, button, { item }) => {
switch (button) {
case QuickCommandButtons.RevealInSideBar:
void GitActions.Commit.reveal(item, {
select: true,
focus: false,
expand: true,
});
break;
if (button === QuickCommandButtons.SearchInSideBar) {
void Container.searchAndCompareView.search(
state.repo.path,
{ pattern: SearchPattern.fromCommit(quickpick.activeItems[0].item.ref) },
{
label: {
label: `for ${GitReference.toString(quickpick.activeItems[0].item, { icon: false })}`,
},
reveal: {
select: true,
focus: false,
expand: true,
case QuickCommandButtons.SearchInSideBar:
void Container.searchAndCompareView.search(
state.repo.path,
{ pattern: SearchPattern.fromCommit(item.ref) },
{
label: {
label: `for ${GitReference.toString(item, { icon: false })}`,
},
reveal: {
select: true,
focus: false,
expand: true,
},
},
},
);
);
break;
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
@ -1066,17 +1066,15 @@ export async function* pickRepositoryStep<
context.repos.map(r =>
RepositoryQuickPickItem.create(r, r.id === active?.id, {
branch: true,
buttons: [QuickCommandButtons.RevealInSideBar],
fetched: true,
status: true,
}),
),
),
additionalButtons: [QuickCommandButtons.RevealInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) return;
void Container.repositoriesView.revealRepository(quickpick.activeItems[0].item.path, {
void Container.repositoriesView.revealRepository(item.path, {
select: true,
expand: true,
});
@ -1136,18 +1134,16 @@ export async function* pickRepositoriesStep<
actives.some(r => r.id === repo.id),
{
branch: true,
buttons: [QuickCommandButtons.RevealInSideBar],
fetched: true,
status: true,
},
),
),
),
additionalButtons: [QuickCommandButtons.RevealInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) return;
void Container.repositoriesView.revealRepository(quickpick.activeItems[0].item.path, {
void Container.repositoriesView.revealRepository(item.path, {
select: true,
expand: true,
});
@ -1203,46 +1199,21 @@ export function* pickStashStep<
commit,
picked != null &&
(typeof picked === 'string' ? commit.ref === picked : picked.includes(commit.ref)),
{ compact: true, icon: true },
{
buttons: [QuickCommandButtons.RevealInSideBar],
compact: true,
icon: true,
},
),
),
],
additionalButtons: [QuickCommandButtons.RevealInSideBar, QuickCommandButtons.SearchInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (_quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
void Container.repositoriesView.revealStashes(state.repo.path, {
select: true,
expand: true,
});
} else {
void GitActions.Stash.reveal(quickpick.activeItems[0].item, {
select: true,
focus: false,
expand: true,
});
}
return;
}
if (button === QuickCommandButtons.SearchInSideBar) {
if (quickpick.activeItems.length === 0) return;
void Container.searchAndCompareView.search(
state.repo.path,
{ pattern: SearchPattern.fromCommit(quickpick.activeItems[0].item.stashName) },
{
label: {
label: `for ${GitReference.toString(quickpick.activeItems[0].item, { icon: false })}`,
},
reveal: {
select: true,
focus: false,
expand: true,
},
},
);
void GitActions.Stash.reveal(item, {
select: true,
focus: false,
expand: true,
});
}
},
keys: ['right', 'alt+right', 'ctrl+right'],
@ -1279,6 +1250,7 @@ export async function* pickTagsStep<
},
): AsyncStepResultGenerator<GitTagReference[]> {
const tags = await getTags(state.repo, {
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
});
@ -1292,19 +1264,9 @@ export async function* pickTagsStep<
tags.length === 0
? [DirectiveQuickPickItem.create(Directive.Back, true), DirectiveQuickPickItem.create(Directive.Cancel)]
: tags,
additionalButtons: [QuickCommandButtons.RevealInSideBar],
onDidClickButton: (quickpick, button) => {
onDidClickItemButton: (quickpick, button, { item }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (quickpick.activeItems.length === 0) {
void Container.repositoriesView.revealTags(state.repo.path, {
select: true,
expand: true,
});
return;
}
void GitActions.Tag.reveal(quickpick.activeItems[0].item, {
void GitActions.Tag.reveal(item, {
select: true,
expand: true,
});

+ 1
- 0
src/commands/quickCommand.ts View File

@ -46,6 +46,7 @@ export interface QuickPickStep {
onDidAccept?(quickpick: QuickPick<T>): boolean | Promise<boolean>;
onDidChangeValue?(quickpick: QuickPick<T>): boolean | Promise<boolean>;
onDidClickButton?(quickpick: QuickPick<T>, button: QuickInputButton): boolean | void | Promise<boolean | void>;
onDidClickItemButton?(quickpick: QuickPick<T>, button: QuickInputButton, item: T): void | Promise<void>;
onDidLoadMore?(quickpick: QuickPick<T>): (DirectiveQuickPickItem | T)[] | Promise<(DirectiveQuickPickItem | T)[]>;
onDidPressKey?(quickpick: QuickPick<T>, key: Keys): void | Promise<void>;
onValidateValue?(quickpick: QuickPick<T>, value: string, items: T[]): boolean | Promise<boolean>;

+ 2
- 1
src/git/gitService.ts View File

@ -19,7 +19,8 @@ import {
WorkspaceFolder,
WorkspaceFoldersChangeEvent,
} from 'vscode';
import type { API as BuiltInGitApi, Repository as BuiltInGitRepository, GitExtension } from '../@types/git';
// eslint-disable-next-line import/extensions
import type { API as BuiltInGitApi, Repository as BuiltInGitRepository, GitExtension } from '../@types/vscode.git';
import { resetAvatarCache } from '../avatars';
import { configuration } from '../configuration';
import { BuiltInGitConfiguration, ContextKeys, DocumentSchemes, GlyphChars, setContext } from '../constants';

+ 23
- 4
src/quickpicks/gitQuickPickItems.ts View File

@ -1,5 +1,5 @@
'use strict';
import { QuickPickItem } from 'vscode';
import { QuickInputButton, QuickPickItem } from 'vscode';
import { Commands, GitCommandsCommand, GitCommandsCommandArgs } from '../commands';
import { GlyphChars } from '../constants';
import { emojify } from '../emojis';
@ -41,6 +41,7 @@ export namespace BranchQuickPickItem {
picked?: boolean,
options: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
current?: boolean | 'checkmark';
checked?: boolean;
ref?: boolean;
@ -123,6 +124,7 @@ export namespace BranchQuickPickItem {
}`,
description: description,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked ?? branch.current,
item: branch,
current: branch.current,
@ -145,7 +147,7 @@ export namespace CommitQuickPickItem {
export function create<T extends GitLogCommit = GitLogCommit>(
commit: T,
picked?: boolean,
options: { alwaysShow?: boolean; compact?: boolean; icon?: boolean } = {},
options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; compact?: boolean; icon?: boolean } = {},
) {
if (GitStashCommit.is(commit)) {
const number = commit.number == null ? '' : `${commit.number}: `;
@ -159,6 +161,7 @@ export namespace CommitQuickPickItem {
2,
)}${commit.getFormattedDiffStatus({ compact: true })}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
@ -175,6 +178,7 @@ export namespace CommitQuickPickItem {
2,
)}${commit.getFormattedDiffStatus({ compact: true })}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
@ -189,6 +193,7 @@ export namespace CommitQuickPickItem {
commit.shortSha
}${Strings.pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ compact: true })}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
@ -206,6 +211,7 @@ export namespace CommitQuickPickItem {
compact: true,
})}`,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: commit,
};
@ -243,13 +249,14 @@ export namespace RefQuickPickItem {
ref: string,
repoPath: string,
picked?: boolean,
options: { alwaysShow?: boolean; ref?: boolean; icon?: boolean } = {},
options: { alwaysShow?: boolean; buttons?: QuickInputButton[]; ref?: boolean; icon?: boolean } = {},
): RefQuickPickItem {
if (ref === '') {
return {
label: `${options.icon ? Strings.pad('$(file-directory)', 0, 2) : ''}Working Tree`,
description: '',
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'Working Tree' }),
current: false,
@ -263,6 +270,7 @@ export namespace RefQuickPickItem {
label: `${options.icon ? Strings.pad('$(git-branch)', 0, 2) : ''}HEAD`,
description: '',
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: GitReference.create(ref, repoPath, { refType: 'revision', name: 'HEAD' }),
current: false,
@ -278,6 +286,7 @@ export namespace RefQuickPickItem {
label: `Range ${gitRef.name}`,
description: '',
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: gitRef,
current: false,
@ -290,6 +299,7 @@ export namespace RefQuickPickItem {
label: `Commit ${gitRef.name}`,
description: options.ref ? `$(git-commit) ${ref}` : '',
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: gitRef,
current: false,
@ -309,7 +319,13 @@ export namespace RepositoryQuickPickItem {
export async function create(
repository: Repository,
picked?: boolean,
options: { alwaysShow?: boolean; branch?: boolean; fetched?: boolean; status?: boolean } = {},
options: {
alwaysShow?: boolean;
branch?: boolean;
buttons?: QuickInputButton[];
fetched?: boolean;
status?: boolean;
} = {},
) {
let repoStatus;
if (options.branch || options.status) {
@ -354,6 +370,7 @@ export namespace RepositoryQuickPickItem {
label: repository.formattedName,
description: description,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: repository,
repoPath: repository.path,
@ -375,6 +392,7 @@ export namespace TagQuickPickItem {
picked?: boolean,
options: {
alwaysShow?: boolean;
buttons?: QuickInputButton[];
message?: boolean;
checked?: boolean;
ref?: boolean;
@ -407,6 +425,7 @@ export namespace TagQuickPickItem {
}`,
description: description,
alwaysShow: options.alwaysShow,
buttons: options.buttons,
picked: picked,
item: tag,
current: false,

+ 16
- 1
src/quickpicks/referencePicker.ts View File

@ -1,6 +1,7 @@
'use strict';
import { CancellationTokenSource, Disposable, QuickPick, window } from 'vscode';
import { getBranchesAndOrTags, getValidateGitReferenceFn } from '../commands/quickCommand';
import { GitActions } from '../commands';
import { getBranchesAndOrTags, getValidateGitReferenceFn, QuickCommandButtons } from '../commands/quickCommand';
import { GlyphChars } from '../constants';
import { Container } from '../container';
import { BranchSortOptions, GitBranch, GitReference, GitTag, TagSortOptions } from '../git/git';
@ -37,6 +38,7 @@ export namespace ReferencePicker {
options: ReferencesQuickPickOptions = {},
): Promise<GitReference | undefined> {
const quickpick = window.createQuickPick<ReferencesQuickPickItem>();
(quickpick as any).enableProposedApi = true;
quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut();
quickpick.title = title;
@ -88,6 +90,7 @@ export namespace ReferencePicker {
quickpick.show();
const getValidateGitReference = getValidateGitReferenceFn((await Container.git.getRepository(repoPath))!, {
buttons: [QuickCommandButtons.RevealInSideBar],
ranges:
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
options?.allowEnteringRefs && typeof options.allowEnteringRefs !== 'boolean'
@ -127,6 +130,17 @@ export namespace ReferencePicker {
await scope.resume();
}
}),
quickpick.onDidTriggerItemButton(({ button, item: { item } }) => {
if (button === QuickCommandButtons.RevealInSideBar) {
if (GitReference.isBranch(item)) {
void GitActions.Branch.reveal(item, { select: true, expand: true });
} else if (GitReference.isTag(item)) {
void GitActions.Tag.reveal(item, { select: true, expand: true });
} else if (GitReference.isRevision(item)) {
void GitActions.Commit.reveal(item, { select: true, expand: true });
}
}
}),
);
});
if (pick == null && autoPick != null) {
@ -157,6 +171,7 @@ export namespace ReferencePicker {
? ['tags']
: [],
{
buttons: [QuickCommandButtons.RevealInSideBar],
filter: filter,
picked: picked,
sort: sort ?? { branches: { current: false }, tags: {} },

+ 35
- 82
src/quickpicks/remoteProviderPicker.ts View File

@ -1,5 +1,5 @@
'use strict';
import { Disposable, env, Uri, window } from 'vscode';
import { Disposable, env, QuickInputButton, ThemeIcon, Uri, window } from 'vscode';
import { Commands, OpenOnRemoteCommandArgs } from '../commands';
import { GlyphChars } from '../constants';
import { Container } from '../container';
@ -31,10 +31,12 @@ export class CopyOrOpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
private readonly remote: GitRemote<RemoteProvider>,
private readonly resource: RemoteResource,
private readonly clipboard?: boolean,
buttons?: QuickInputButton[],
) {
super({
label: `$(repo) ${remote.provider.path}`,
description: remote.name,
buttons: buttons,
});
}
@ -86,6 +88,10 @@ export class CopyOrOpenRemoteCommandQuickPickItem extends CommandQuickPickItem {
void (await (this.clipboard ? this.remote.provider.copy(resource) : this.remote.provider.open(resource)));
}
setAsDefault(): Promise<void> {
return this.remote.setAsDefault(true);
}
}
export class CopyRemoteResourceCommandQuickPickItem extends CommandQuickPickItem {
@ -131,28 +137,11 @@ export class OpenRemoteResourceCommandQuickPickItem extends CommandQuickPickItem
}
}
export class SetADefaultRemoteCommandQuickPickItem extends CommandQuickPickItem {
constructor(private readonly remotes: GitRemote<RemoteProvider>[]) {
super({ label: 'Set a Default Remote...' });
}
override async execute(): Promise<GitRemote<RemoteProvider> | undefined> {
return RemoteProviderPicker.setADefaultRemote(this.remotes);
}
}
export class SetRemoteAsDefaultCommandQuickPickItem extends CommandQuickPickItem {
constructor(private readonly remote: GitRemote<RemoteProvider>) {
super({
label: remote.provider.name,
detail: `$(repo) ${remote.provider.path}`,
});
}
override async execute(): Promise<GitRemote<RemoteProvider>> {
void (await this.remote.setAsDefault(true));
return this.remote;
}
namespace QuickCommandButtons {
export const SetRemoteAsDefault: QuickInputButton = {
iconPath: new ThemeIcon('settings-gear'),
tooltip: 'Set as Default Remote',
};
}
export namespace RemoteProviderPicker {
@ -162,19 +151,10 @@ export namespace RemoteProviderPicker {
resource: RemoteResource,
remotes: GitRemote<RemoteProvider>[],
options?: { autoPick?: 'default' | boolean; clipboard?: boolean; setDefault?: boolean },
): Promise<
| ConfigureCustomRemoteProviderCommandQuickPickItem
| CopyOrOpenRemoteCommandQuickPickItem
| SetADefaultRemoteCommandQuickPickItem
| undefined
> {
): Promise<ConfigureCustomRemoteProviderCommandQuickPickItem | CopyOrOpenRemoteCommandQuickPickItem | undefined> {
const { autoPick, clipboard, setDefault } = { autoPick: false, clipboard: false, setDefault: true, ...options };
let items: (
| ConfigureCustomRemoteProviderCommandQuickPickItem
| CopyOrOpenRemoteCommandQuickPickItem
| SetADefaultRemoteCommandQuickPickItem
)[];
let items: (ConfigureCustomRemoteProviderCommandQuickPickItem | CopyOrOpenRemoteCommandQuickPickItem)[];
if (remotes.length === 0) {
items = [new ConfigureCustomRemoteProviderCommandQuickPickItem()];
placeHolder = 'No auto-detected or configured remote providers found';
@ -187,30 +167,31 @@ export namespace RemoteProviderPicker {
}
}
items = remotes.map(r => new CopyOrOpenRemoteCommandQuickPickItem(r, resource, clipboard));
if (setDefault) {
items.push(new SetADefaultRemoteCommandQuickPickItem(remotes));
}
items = remotes.map(
r =>
new CopyOrOpenRemoteCommandQuickPickItem(
r,
resource,
clipboard,
setDefault ? [QuickCommandButtons.SetRemoteAsDefault] : undefined,
),
);
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (autoPick && remotes.length === 1) return items[0];
const quickpick = window.createQuickPick<
| ConfigureCustomRemoteProviderCommandQuickPickItem
| CopyOrOpenRemoteCommandQuickPickItem
| SetADefaultRemoteCommandQuickPickItem
ConfigureCustomRemoteProviderCommandQuickPickItem | CopyOrOpenRemoteCommandQuickPickItem
>();
(quickpick as any).enableProposedApi = true;
quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut();
const disposables: Disposable[] = [];
try {
const pick = await new Promise<
| ConfigureCustomRemoteProviderCommandQuickPickItem
| CopyOrOpenRemoteCommandQuickPickItem
| SetADefaultRemoteCommandQuickPickItem
| undefined
ConfigureCustomRemoteProviderCommandQuickPickItem | CopyOrOpenRemoteCommandQuickPickItem | undefined
>(resolve => {
disposables.push(
quickpick.onDidHide(() => resolve(undefined)),
@ -219,6 +200,15 @@ export namespace RemoteProviderPicker {
resolve(quickpick.activeItems[0]);
}
}),
quickpick.onDidTriggerItemButton(async e => {
if (
e.button === QuickCommandButtons.SetRemoteAsDefault &&
e.item instanceof CopyOrOpenRemoteCommandQuickPickItem
) {
void (await e.item.setAsDefault());
resolve(e.item);
}
}),
);
quickpick.title = title;
@ -236,41 +226,4 @@ export namespace RemoteProviderPicker {
disposables.forEach(d => d.dispose());
}
}
export async function setADefaultRemote(
remotes: GitRemote<RemoteProvider>[],
): Promise<GitRemote<RemoteProvider> | undefined> {
const items = remotes.map(r => new SetRemoteAsDefaultCommandQuickPickItem(r));
const quickpick = window.createQuickPick<SetRemoteAsDefaultCommandQuickPickItem>();
quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut();
const disposables: Disposable[] = [];
try {
const pick = await new Promise<SetRemoteAsDefaultCommandQuickPickItem | undefined>(resolve => {
disposables.push(
quickpick.onDidHide(() => resolve(undefined)),
quickpick.onDidAccept(() => {
if (quickpick.activeItems.length !== 0) {
resolve(quickpick.activeItems[0]);
}
}),
);
quickpick.title = 'Set a Default Remote';
quickpick.placeholder = 'Choose which remote to set as the default';
quickpick.matchOnDetail = true;
quickpick.items = items;
quickpick.show();
});
if (pick == null) return undefined;
return await pick.execute();
} finally {
quickpick.dispose();
disposables.forEach(d => d.dispose());
}
}
}

Loading…
Cancel
Save