Browse Source

Adds partner api support

main
Eric Amodio 3 years ago
parent
commit
24d872a2fa
11 changed files with 429 additions and 181 deletions
  1. +28
    -3
      package.json
  2. +232
    -59
      src/api/actionRunners.ts
  3. +7
    -3
      src/api/api.ts
  4. +36
    -29
      src/api/gitlens.d.ts
  5. +7
    -0
      src/config.ts
  6. +7
    -5
      src/extension.ts
  7. +31
    -29
      src/git/formatters/commitFormatter.ts
  8. +15
    -19
      src/git/models/repository.ts
  9. +4
    -1
      src/hovers/hovers.ts
  10. +42
    -0
      src/partners.ts
  11. +20
    -33
      src/views/viewCommands.ts

+ 28
- 3
package.json View File

@ -232,7 +232,8 @@
"description": "Specifies whether case should be ignored when matching the prefix",
"default": false
}
}
},
"additionalProperties": false
},
"uniqueItems": true,
"markdownDescription": "Specifies autolinks to external resources in commit messages. Use `<num>` as the variable for the reference number",
@ -1164,7 +1165,8 @@
}
]
}
}
},
"additionalProperties": false
}
],
"default": {
@ -1398,6 +1400,27 @@
"markdownDescription": "Specifies how much (if any) output will be sent to the GitLens output channel",
"scope": "window"
},
"gitlens.partners": {
"type": [
"object",
"null"
],
"additionalProperties": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"description": "Specifies whether the partner integration should be shown"
}
},
"additionalProperties": true,
"description": "Specifies the configuration of a partner integration"
},
"default": null,
"description": "Specifies the configuration of a partner integration",
"scope": "window"
},
"gitlens.remotes": {
"type": [
"array",
@ -1488,7 +1511,8 @@
}
},
"description": "Specifies the url formats of the custom remote service"
}
},
"additionalProperties": false
},
"uniqueItems": true,
"markdownDescription": "Specifies user-defined remote (code-hosting) services or custom domains for built-in remote services",
@ -2474,6 +2498,7 @@
"default": false
}
},
"additionalProperties": false,
"markdownDescription": "Specifies which messages should be suppressed",
"scope": "window"
},

+ 232
- 59
src/api/actionRunners.ts View File

@ -1,20 +1,34 @@
'use strict';
import { commands, Disposable, QuickPickItem, window } from 'vscode';
import { commands, Disposable, Event, EventEmitter, QuickPickItem, window } from 'vscode';
import { Commands } from '../commands/common';
import { configuration } from '../configuration';
import { ContextKeys, setContext } from '../constants';
import { Container } from '../container';
import { getQuickPickIgnoreFocusOut } from '../quickpicks';
import { Action, ActionContext, ActionRunner } from './gitlens';
type Actions = ActionContext['type'];
const actions: Actions[] = ['createPullRequest', 'openPullRequest'];
const actions: Actions[] = ['createPullRequest', 'openPullRequest', 'hover.commands'];
// The order here determines the sorting of these actions when shown to the user
export const enum ActionRunnerType {
BuiltIn = 0,
BuiltInPartner = 1,
Partner = 2,
BuiltInPartnerInstaller = 3,
}
export const builtInActionRunnerName = 'Built In';
export const defaultActionRunnerName = 'Built In';
class ActionRunnerQuickPickItem implements QuickPickItem {
private readonly _label: string;
export class ActionRunnerQuickPickItem implements QuickPickItem {
constructor(public readonly runner: RegisteredActionRunner) {}
constructor(public readonly runner: RegisteredActionRunner, context: ActionContext) {
this._label = typeof runner.label === 'string' ? runner.label : runner.label(context);
}
get label(): string {
return this.runner.label;
return this._label;
}
get detail(): string | undefined {
@ -22,8 +36,39 @@ export class ActionRunnerQuickPickItem implements QuickPickItem {
}
}
class RegisteredActionRunner implements ActionRunner, Disposable {
constructor(private readonly runner: ActionRunner, private readonly unregister: () => void) {}
class NoActionRunnersQuickPickItem implements QuickPickItem {
public readonly runner: RegisteredActionRunner | undefined;
get label(): string {
return 'No actions were found';
}
get detail(): string | undefined {
return undefined;
}
}
let runnerId = 0;
function nextRunnerId() {
if (runnerId === Number.MAX_SAFE_INTEGER) {
runnerId = 1;
} else {
runnerId++;
}
return runnerId;
}
class RegisteredActionRunner<T extends ActionContext = ActionContext> implements ActionRunner<T>, Disposable {
readonly id: number;
constructor(
public readonly type: ActionRunnerType,
private readonly runner: ActionRunner<T>,
private readonly unregister: () => void,
) {
this.id = nextRunnerId();
}
dispose() {
this.unregister();
@ -33,25 +78,70 @@ class RegisteredActionRunner implements ActionRunner, Disposable {
return this.runner.name;
}
get label(): string {
get label(): string | ((context: T) => string) {
return this.runner.label;
}
run(context: ActionContext): void | Promise<void> {
get order(): number {
switch (this.type) {
case ActionRunnerType.BuiltIn:
return 0;
case ActionRunnerType.BuiltInPartner:
return 1;
case ActionRunnerType.Partner:
// Sort built-in partners and partners with ids the same
return this.partnerId ? 1 : 2;
case ActionRunnerType.BuiltInPartnerInstaller:
return 3;
default:
return 100;
}
}
get partnerId(): string {
return this.runner.partnerId;
}
run(context: T): void | Promise<void> {
return this.runner.run(context);
}
// when(context: ActionContext): boolean {
// try {
// return this.runner.when?.(context) ?? true;
// } catch {
// return false;
// }
// }
}
export class ActionRunners implements Disposable {
private readonly _actionRunners = new Map<Actions, RegisteredActionRunner[]>();
private _onDidChange = new EventEmitter<Actions | undefined>();
get onDidChange(): Event<Actions | undefined> {
return this._onDidChange.event;
}
private readonly _actionRunners = new Map<Actions, RegisteredActionRunner<any>[]>();
private readonly _disposable: Disposable;
constructor() {
const subscriptions: Disposable[] = [];
const subscriptions: Disposable[] = [
configuration.onDidChange(e => {
if (!configuration.changed(e, 'partners')) return;
void this._updateAllContextKeys();
}),
];
for (const action of actions) {
subscriptions.push(
commands.registerCommand(`${Commands.ActionPrefix}${action}`, (context: ActionContext) =>
this.run(context),
commands.registerCommand(
`${Commands.ActionPrefix}${action}`,
(context: ActionContext, runnerId?: number) => this.run(context, runnerId),
),
);
}
@ -71,26 +161,39 @@ export class ActionRunners implements Disposable {
}
count(action: Actions): number {
return this._actionRunners.get(action)?.length ?? 0;
return this.get(action)?.length ?? 0;
}
get(action: Actions): RegisteredActionRunner[] | undefined {
return filterOnlyEnabledRunners(this._actionRunners.get(action));
}
has(action: Actions): boolean {
return this.count(action) > 0;
}
register<T extends ActionContext>(action: Action<T>, runner: ActionRunner): Disposable {
register<T extends ActionContext>(
action: Action<T>,
runner: ActionRunner<T>,
type: ActionRunnerType = ActionRunnerType.Partner,
): Disposable {
let runners = this._actionRunners.get(action);
if (runners == null) {
runners = [];
this._actionRunners.set(action, runners);
}
const onChanged = (action: Actions) => {
void this._updateContextKeys(action);
this._onDidChange.fire(action);
};
const runnersMap = this._actionRunners;
const updateContextKeys = this._updateContextKeys.bind(this);
const registeredRunner = new RegisteredActionRunner(runner, function (this: RegisteredActionRunner) {
const registeredRunner = new RegisteredActionRunner(type, runner, function (this: RegisteredActionRunner) {
if (runners!.length === 1) {
runnersMap.delete(action);
void updateContextKeys(action);
onChanged(action);
} else {
const index = runners!.indexOf(this);
if (index !== -1) {
@ -98,64 +201,117 @@ export class ActionRunners implements Disposable {
}
}
});
runners.push(registeredRunner);
void this._updateContextKeys(action);
runners.push(registeredRunner);
onChanged(action);
return {
dispose: () => registeredRunner.dispose(),
};
}
registerDefault<T extends ActionContext>(action: Action<T>, runner: Omit<ActionRunner, 'name'>): Disposable {
return this.register(action, { ...runner, name: defaultActionRunnerName });
registerBuiltIn<T extends ActionContext>(
action: Action<T>,
runner: Omit<ActionRunner<T>, 'partnerId' | 'name'>,
): Disposable {
return this.register(
action,
{ ...runner, partnerId: undefined!, name: builtInActionRunnerName },
ActionRunnerType.BuiltIn,
);
}
registerBuiltInPartner<T extends ActionContext>(
partnerId: string,
action: Action<T>,
runner: Omit<ActionRunner<T>, 'partnerId'>,
): Disposable {
return this.register(action, { ...runner, partnerId: partnerId }, ActionRunnerType.BuiltInPartner);
}
async run<T extends ActionContext>(context: T) {
const runners = this._actionRunners.get(context.type);
registerBuiltInPartnerInstaller<T extends ActionContext>(
partnerId: string,
action: Action<T>,
runner: Omit<ActionRunner<T>, 'partnerId'>,
): Disposable {
return this.register(
action,
{ ...runner, partnerId: partnerId, name: `${runner.name} (Not Installed)` },
ActionRunnerType.BuiltInPartnerInstaller,
);
}
async run<T extends ActionContext>(context: T, runnerId?: number) {
let runners = this.get(context.type);
if (runners == null || runners.length === 0) return;
if (runnerId != null) {
runners = runners.filter(r => r.id === runnerId);
}
if (runners.length === 0) return;
let runner;
if (runners.length > 1) {
const items = runners.map(r => new ActionRunnerQuickPickItem(r));
if (runners.length > 1 || runners.every(r => r.type !== ActionRunnerType.BuiltIn)) {
const items: (ActionRunnerQuickPickItem | NoActionRunnersQuickPickItem)[] = runners
// .filter(r => r.when(context))
.sort(
(a, b) =>
a.order - b.order ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }),
)
.map(r => new ActionRunnerQuickPickItem(r, context));
if (items.length === 0) {
items.push(new NoActionRunnersQuickPickItem());
}
const quickpick = window.createQuickPick<ActionRunnerQuickPickItem>();
const quickpick = window.createQuickPick<ActionRunnerQuickPickItem | NoActionRunnersQuickPickItem>();
quickpick.ignoreFocusOut = getQuickPickIgnoreFocusOut();
const disposables: Disposable[] = [];
try {
const pick = await new Promise<ActionRunnerQuickPickItem | undefined>(resolve => {
disposables.push(
quickpick.onDidHide(() => resolve(undefined)),
quickpick.onDidAccept(() => {
if (quickpick.activeItems.length !== 0) {
resolve(quickpick.activeItems[0]);
}
}),
);
let title;
let placeholder;
switch (context.type) {
case 'createPullRequest':
title = 'Create Pull Request';
placeholder = 'Choose how to create a pull request';
break;
case 'openPullRequest':
title = 'Open Pull Request';
placeholder = 'Choose how to open the pull request';
break;
}
quickpick.title = title;
quickpick.placeholder = placeholder;
quickpick.matchOnDetail = true;
quickpick.items = items;
quickpick.show();
});
const pick = await new Promise<ActionRunnerQuickPickItem | NoActionRunnersQuickPickItem | undefined>(
resolve => {
disposables.push(
quickpick.onDidHide(() => resolve(undefined)),
quickpick.onDidAccept(() => {
if (quickpick.activeItems.length !== 0) {
resolve(quickpick.activeItems[0]);
}
}),
);
let title;
let placeholder;
switch (context.type) {
case 'createPullRequest':
title = 'Create Pull Request';
placeholder = 'Choose how to create a pull request';
break;
case 'openPullRequest':
title = 'Open Pull Request';
placeholder = 'Choose how to open the pull request';
break;
case 'hover.commands':
title = 'Want to Discuss or Collaborate? Have Comments, Questions, or Need Help?';
placeholder = 'Choose what you would like to do';
break;
default:
// eslint-disable-next-line no-debugger
debugger;
break;
}
quickpick.title = title;
quickpick.placeholder = placeholder;
quickpick.matchOnDetail = true;
quickpick.items = items;
quickpick.show();
},
);
if (pick == null) return;
runner = pick.runner;
@ -167,10 +323,27 @@ export class ActionRunners implements Disposable {
[runner] = runners;
}
await runner.run(context);
await runner?.run(context);
}
private async _updateContextKeys(action: Actions) {
await setContext(`${ContextKeys.ActionPrefix}${action}`, this.count(action));
}
private async _updateAllContextKeys() {
for (const action of actions) {
await this._updateContextKeys(action);
}
}
}
function filterOnlyEnabledRunners(runners: RegisteredActionRunner[] | undefined) {
if (runners == null || runners.length === 0) return undefined;
const partners = Container.config.partners;
if (partners == null) return runners;
return runners.filter(
r => r.partnerId == null || (r.partnerId != null && partners[r.partnerId]?.enabled !== false),
);
}

+ 7
- 3
src/api/api.ts View File

@ -2,7 +2,7 @@
import { Disposable } from 'vscode';
import { Container } from '../container';
import { Logger } from '../logger';
import { defaultActionRunnerName } from './actionRunners';
import { builtInActionRunnerName } from './actionRunners';
import { Action, ActionContext, ActionRunner, GitLensApi } from './gitlens';
const emptyDisposable = Object.freeze({
@ -13,8 +13,12 @@ const emptyDisposable = Object.freeze({
export class Api implements GitLensApi {
registerActionRunner<T extends ActionContext>(action: Action<T>, runner: ActionRunner): Disposable {
if (runner.name === defaultActionRunnerName) {
throw new Error(`Cannot use the reserved name '${defaultActionRunnerName}'`);
if (runner.name === builtInActionRunnerName) {
throw new Error(`Cannot use the reserved name '${builtInActionRunnerName}'`);
}
if ((action as string) === 'hover.commandHelp') {
action = 'hover.commands';
}
return Container.actionRunners.register(action, runner);
}

+ 36
- 29
src/api/gitlens.d.ts View File

@ -11,28 +11,12 @@ export interface RemoteProvider {
export interface CreatePullRequestActionContext {
readonly type: 'createPullRequest';
readonly runnerId?: number;
readonly repoPath: string;
readonly branch: {
readonly name: string;
readonly upstream: string | undefined;
readonly isRemote: boolean;
/**
* @deprecated Use the root [repoPath](#CreatePullRequestActionContext.repoPath) property instead
*/
readonly repoPath: string;
/**
* @deprecated Use the root [remote](#CreatePullRequestActionContext.remote) property instead
*/
readonly remote:
| {
readonly name: string;
readonly provider?: RemoteProvider;
readonly url?: string;
}
| undefined;
};
readonly remote:
| {
@ -45,33 +29,56 @@ export interface CreatePullRequestActionContext {
export interface OpenPullRequestActionContext {
readonly type: 'openPullRequest';
readonly runnerId?: number;
readonly repoPath: string;
readonly provider: RemoteProvider | undefined;
readonly pullRequest: {
readonly id: string;
readonly url: string;
};
}
/**
* @deprecated Use the root [repoPath](#OpenPullRequestActionContext.repoPath) property instead
*/
readonly repoPath: string;
/**
* @deprecated Use the root [provider](#OpenPullRequestActionContext.provider) property instead
*/
readonly provider: RemoteProvider | undefined;
export interface HoverCommandsActionContext {
readonly type: 'hover.commands';
readonly repoPath: string;
readonly commit: {
sha: string;
author: {
name: string;
email: string | undefined;
[key: string]: any;
};
};
readonly file:
| {
uri: string;
line: number | undefined;
}
| undefined;
}
export type ActionContext = CreatePullRequestActionContext | OpenPullRequestActionContext;
export type ActionContext = CreatePullRequestActionContext | OpenPullRequestActionContext | HoverCommandsActionContext;
export type Action<T extends ActionContext> = T['type'];
export interface ActionRunner {
export interface ActionRunner<T extends ActionContext = ActionContext> {
/*
* A unique key to identify the extension/product/company to which the runner belongs
*/
readonly partnerId: string;
/*
* A user-friendly name to which the runner belongs, i.e. your extension/product/company name. Will be shown, less prominently, to the user when offering this action
*/
readonly name: string;
readonly label: string;
run(context: ActionContext): void | Promise<void>;
/*
* A user-friendly string which describes the action that will be taken. Will be shown to the user when offering this action
*/
readonly label: string | ((context: T) => string);
run(context: T): void | Promise<void>;
// when?(context: T): boolean;
}
export interface GitLensApi {

+ 7
- 0
src/config.ts View File

@ -111,6 +111,13 @@ export interface Config {
};
modes: Record<string, ModeConfig>;
outputLevel: TraceLevel;
partners: Record<
string,
{
enabled: boolean;
[key: string]: any;
}
> | null;
remotes: RemotesConfig[] | null;
showWelcomeOnInstall: boolean;
showWhatsNewAfterUpgrades: boolean;

+ 7
- 5
src/extension.ts View File

@ -1,7 +1,7 @@
'use strict';
import * as paths from 'path';
import { commands, ExtensionContext, extensions, window, workspace } from 'vscode';
import { GitLensApi } from '../src/api/gitlens';
import { GitLensApi, OpenPullRequestActionContext } from '../src/api/gitlens';
import { Api } from './api/api';
import { Commands, executeCommand, OpenPullRequestOnRemoteCommandArgs, registerCommands } from './commands';
import { configuration, Configuration } from './configuration';
@ -12,6 +12,7 @@ import { GitService } from './git/gitService';
import { GitUri } from './git/gitUri';
import { Logger } from './logger';
import { Messages } from './messages';
import { registerPartnerActionRunners } from './partners';
import { Strings, Versions } from './system';
import { ViewNode } from './views/nodes';
@ -135,7 +136,8 @@ export async function activate(context: ExtensionContext): Promise
Container.initialize(extensionId, context, cfg);
registerCommands(context);
registerDefaultActionRunners(context);
registerBuiltInActionRunners(context);
registerPartnerActionRunners(context);
const gitVersion = Git.getGitVersion();
@ -196,10 +198,10 @@ export function notifyOnUnsupportedGitVersion(version: string) {
void Messages.showGitVersionUnsupportedErrorMessage(version, '2.7.2');
}
function registerDefaultActionRunners(context: ExtensionContext): void {
function registerBuiltInActionRunners(context: ExtensionContext): void {
context.subscriptions.push(
Container.actionRunners.registerDefault('openPullRequest', {
label: 'Open on Remote',
Container.actionRunners.registerBuiltIn<OpenPullRequestActionContext>('openPullRequest', {
label: ctx => `Open Pull Request on ${ctx.provider?.name ?? 'Remote'}`,
run: async ctx => {
if (ctx.type !== 'openPullRequest') return;

+ 31
- 29
src/git/formatters/commitFormatter.ts View File

@ -1,12 +1,12 @@
'use strict';
import { OpenPullRequestActionContext } from '../../api/gitlens';
import { Uri } from 'vscode';
import { HoverCommandsActionContext, OpenPullRequestActionContext } from '../../api/gitlens';
import { getPresenceDataUri } from '../../avatars';
import {
Commands,
ConnectRemoteProviderCommand,
DiffWithCommand,
getMarkdownActionCommand,
InviteToLiveShareCommand,
OpenCommitOnRemoteCommand,
OpenFileAtRevisionCommand,
ShowQuickCommitCommand,
@ -36,9 +36,9 @@ export interface CommitFormatOptions extends FormatOptions {
autolinkedIssuesOrPullRequests?: Map<string, IssueOrPullRequest | Promises.CancellationError | undefined>;
avatarSize?: number;
dateStyle?: DateStyle;
editor?: { line: number; uri: Uri };
footnotes?: Map<number, string>;
getBranchAndTagTips?: (sha: string) => string | undefined;
line?: number;
markdown?: boolean;
messageAutolinks?: boolean;
messageIndent?: number;
@ -282,7 +282,7 @@ export class CommitFormatter extends Formatter {
uri: diffUris.current.documentUri(),
},
repoPath: this._item.repoPath,
line: this._options.line,
line: this._options.editor?.line,
})} "Open Changes")** `;
} else {
commands = `\`${this._padOrTruncate(
@ -310,15 +310,9 @@ export class CommitFormatter extends Formatter {
}](${getMarkdownActionCommand<OpenPullRequestActionContext>('openPullRequest', {
repoPath: this._item.repoPath,
provider: { id: pr.provider.id, name: pr.provider.name, domain: pr.provider.domain },
pullRequest: {
id: pr.id,
url: pr.url,
provider: { id: pr.provider.id, name: pr.provider.name, domain: pr.provider.domain },
repoPath: this._item.repoPath,
},
pullRequest: { id: pr.id, url: pr.url },
})} "Open Pull Request \\#${pr.id}${
Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider.name}` : ''
Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider.name}` : '...'
}\n${GlyphChars.Dash.repeat(2)}\n${Strings.escapeMarkdown(pr.title).replace(/"/g, '\\"')}\n${
pr.state
}, ${pr.formatDateFromNow()}")${separator}`;
@ -335,7 +329,7 @@ export class CommitFormatter extends Formatter {
commands += `[$(compare-changes)](${DiffWithCommand.getMarkdownCommandArgs(
this._item,
this._options.line,
this._options.editor?.line,
)} "Open Changes")${separator}`;
if (this._item.previousSha != null) {
@ -347,7 +341,7 @@ export class CommitFormatter extends Formatter {
commands += `[$(history)](${OpenFileAtRevisionCommand.getMarkdownCommandArgs(
uri,
FileAnnotationType.Blame,
this._options.line,
this._options.editor?.line,
)} "Blame Previous Revision")${separator}`;
}
@ -359,13 +353,27 @@ export class CommitFormatter extends Formatter {
)} "Open Commit on ${providers?.length ? providers[0].name : 'Remote'}")${separator}`;
}
if (this._item.author !== 'You') {
const presence = this._options.presence;
if (presence != null) {
commands += `[$(live-share)](${InviteToLiveShareCommand.getMarkdownCommandArgs(
this._item.email,
)} "Invite ${this._item.author} (${presence.statusText}) to a Live Share Session")${separator}`;
}
if (Container.actionRunners.count('hover.commands') > 0) {
commands += `[$(feedback) Discuss / Collab${
GlyphChars.Ellipsis
}](${getMarkdownActionCommand<HoverCommandsActionContext>('hover.commands', {
repoPath: this._item.repoPath,
commit: {
sha: this._item.sha,
author: {
name: this._item.author,
email: this._item.email,
presence: this._options.presence,
},
},
file:
this._options.editor != null
? {
uri: this._options.editor?.uri.toString(),
line: this._options.editor?.line,
}
: undefined,
})} "Want to Discuss or Collaborate? Have Comments, Questions, or Need Help?")${separator}`;
}
commands += `[$(ellipsis)](${ShowQuickCommitFileCommand.getMarkdownCommandArgs({
@ -479,15 +487,9 @@ export class CommitFormatter extends Formatter {
text = `[PR #${pr.id}](${getMarkdownActionCommand<OpenPullRequestActionContext>('openPullRequest', {
repoPath: this._item.repoPath,
provider: { id: pr.provider.id, name: pr.provider.name, domain: pr.provider.domain },
pullRequest: {
id: pr.id,
url: pr.url,
repoPath: this._item.repoPath,
provider: { id: pr.provider.id, name: pr.provider.name, domain: pr.provider.domain },
},
pullRequest: { id: pr.id, url: pr.url },
})} "Open Pull Request \\#${pr.id}${
Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider.name}` : ''
Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider.name}` : '...'
}\n${GlyphChars.Dash.repeat(2)}\n${Strings.escapeMarkdown(pr.title).replace(/"/g, '\\"')}\n${
pr.state
}, ${pr.formatDateFromNow()}")`;

+ 15
- 19
src/git/models/repository.ts View File

@ -733,32 +733,28 @@ export class Repository implements Disposable {
if (!(await Messages.showCreatePullRequestPrompt(branch.name))) return;
const remote = await this.getRemote(remoteName);
const remoteInfo =
remote != null
? {
name: remote.name,
provider:
remote.provider != null
? {
id: remote.provider.id,
name: remote.provider.name,
domain: remote.provider.domain,
}
: undefined,
url: remote.url,
}
: { name: remoteName };
void executeActionCommand<CreatePullRequestActionContext>('createPullRequest', {
repoPath: this.path,
remote: remoteInfo,
remote:
remote != null
? {
name: remote.name,
provider:
remote.provider != null
? {
id: remote.provider.id,
name: remote.provider.name,
domain: remote.provider.domain,
}
: undefined,
url: remote.url,
}
: { name: remoteName },
branch: {
name: branch.name,
isRemote: branch.remote,
upstream: branch.tracking,
remote: remoteInfo,
repoPath: this.path,
},
});
}

+ 4
- 1
src/hovers/hovers.ts View File

@ -199,7 +199,10 @@ export namespace Hovers {
const details = await CommitFormatter.fromTemplateAsync(Container.config.hovers.detailsMarkdownFormat, commit, {
autolinkedIssuesOrPullRequests: autolinkedIssuesOrPullRequests,
dateFormat: dateFormat,
line: editorLine,
editor: {
line: editorLine,
uri: uri,
},
markdown: true,
messageAutolinks: Container.config.hovers.autolinks.enabled,
pullRequestOrRemote: pr,

+ 42
- 0
src/partners.ts View File

@ -0,0 +1,42 @@
'use strict';
import { ExtensionContext } from 'vscode';
import { ActionContext, HoverCommandsActionContext } from './api/gitlens';
import { Commands, executeCommand, InviteToLiveShareCommandArgs } from './commands';
import { Container } from './container';
export function registerPartnerActionRunners(context: ExtensionContext): void {
registerLiveShare(context);
}
function registerLiveShare(context: ExtensionContext) {
context.subscriptions.push(
Container.actionRunners.registerBuiltInPartner<HoverCommandsActionContext>('liveshare', 'hover.commands', {
name: 'Live Share',
label: (context: ActionContext) => {
if (context.type === 'hover.commands') {
if (context.commit.author.name !== 'You') {
return `$(live-share) Invite ${context.commit.author.name}${
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
context.commit.author.presence?.statusText
? ` (${context.commit.author.presence?.statusText})`
: ''
} to a Live Share Session`;
}
}
return '$(live-share) Start a Live Share Session';
},
run: async (context: ActionContext) => {
if (context.type !== 'hover.commands' || context.commit.author.name === 'You') {
await executeCommand<InviteToLiveShareCommandArgs>(Commands.InviteToLiveShare, {});
return;
}
await executeCommand<InviteToLiveShareCommandArgs>(Commands.InviteToLiveShare, {
email: context.commit.author.email,
});
},
}),
);
}

+ 20
- 33
src/views/viewCommands.ts View File

@ -283,32 +283,28 @@ export class ViewCommands {
}
const remote = await node.branch.getRemote();
const remoteInfo =
remote != null
? {
name: remote.name,
provider:
remote.provider != null
? {
id: remote.provider.id,
name: remote.provider.name,
domain: remote.provider.domain,
}
: undefined,
url: remote.url,
}
: undefined;
return executeActionCommand<CreatePullRequestActionContext>('createPullRequest', {
repoPath: node.repoPath,
remote: remoteInfo,
remote:
remote != null
? {
name: remote.name,
provider:
remote.provider != null
? {
id: remote.provider.id,
name: remote.provider.name,
domain: remote.provider.domain,
}
: undefined,
url: remote.url,
}
: undefined,
branch: {
name: node.branch.name,
upstream: node.branch.tracking,
isRemote: node.branch.remote,
remote: remoteInfo,
repoPath: node.repoPath,
},
});
}
@ -420,25 +416,16 @@ export class ViewCommands {
private openPullRequest(node: PullRequestNode) {
if (!(node instanceof PullRequestNode)) return Promise.resolve();
const provider = {
id: node.pullRequest.provider.id,
name: node.pullRequest.provider.name,
domain: node.pullRequest.provider.domain,
};
return executeActionCommand<OpenPullRequestActionContext>('openPullRequest', {
repoPath: node.uri.repoPath!,
provider: provider,
provider: {
id: node.pullRequest.provider.id,
name: node.pullRequest.provider.name,
domain: node.pullRequest.provider.domain,
},
pullRequest: {
id: node.pullRequest.id,
url: node.pullRequest.url,
provider: {
id: node.pullRequest.provider.id,
name: node.pullRequest.provider.name,
domain: node.pullRequest.provider.domain,
},
repoPath: node.uri.repoPath!,
},
});
}

Loading…
Cancel
Save