diff --git a/package.json b/package.json index 91f30b6..29b7bb2 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "url": "https://github.com/eamodio/vscode-gitlens.git" }, "engines": { - "vscode": "^1.51.0" + "vscode": "^1.52.0" }, "main": "./dist/gitlens", "icon": "images/gitlens-icon.png", @@ -7022,7 +7022,7 @@ }, { "command": "gitlens.views.openPullRequest", - "when": "gitlens:action:openPullRequest && viewItem =~ /gitlens:pullrequest\\b/", + "when": "gitlens:action:openPullRequest > 1 && viewItem =~ /gitlens:pullrequest\\b/", "group": "inline@1" }, { @@ -8356,10 +8356,10 @@ }, "devDependencies": { "@types/chroma-js": "2.1.2", - "@types/lodash-es": "4.17.3", + "@types/lodash-es": "4.17.4", "@types/node": "12.12.70", "@types/sortablejs": "1.10.6", - "@types/vscode": "1.51.0", + "@types/vscode": "1.52.0", "@typescript-eslint/eslint-plugin": "4.9.1", "@typescript-eslint/parser": "4.9.1", "circular-dependency-plugin": "5.2.2", diff --git a/src/api/actionRunners.ts b/src/api/actionRunners.ts index c53ac43..d007167 100644 --- a/src/api/actionRunners.ts +++ b/src/api/actionRunners.ts @@ -1,5 +1,6 @@ 'use strict'; import { commands, Disposable, QuickPickItem, window } from 'vscode'; +import { Commands } from '../commands/common'; import { ContextKeys, setContext } from '../constants'; import { getQuickPickIgnoreFocusOut } from '../quickpicks'; import { Action, ActionContext, ActionRunner } from './gitlens'; @@ -7,12 +8,18 @@ import { Action, ActionContext, ActionRunner } from './gitlens'; type Actions = ActionContext['type']; const actions: Actions[] = ['createPullRequest', 'openPullRequest']; +export const defaultActionRunnerName = 'Built In'; + export class ActionRunnerQuickPickItem implements QuickPickItem { - constructor(public readonly runner: ActionRunner) {} + constructor(public readonly runner: RegisteredActionRunner) {} get label(): string { return this.runner.label; } + + get detail(): string | undefined { + return this.runner.name; + } } class RegisteredActionRunner implements ActionRunner, Disposable { @@ -22,6 +29,10 @@ class RegisteredActionRunner implements ActionRunner, Disposable { this.unregister(); } + get name(): string { + return this.runner.name; + } + get label(): string { return this.runner.label; } @@ -39,7 +50,9 @@ export class ActionRunners implements Disposable { for (const action of actions) { subscriptions.push( - commands.registerCommand(`gitlens.action.${action}`, (context: ActionContext) => this.run(context)), + commands.registerCommand(`${Commands.ActionPrefix}${action}`, (context: ActionContext) => + this.run(context), + ), ); } @@ -57,8 +70,12 @@ export class ActionRunners implements Disposable { this._actionRunners.clear(); } + count(action: Actions): number { + return this._actionRunners.get(action)?.length ?? 0; + } + has(action: Actions): boolean { - return (this._actionRunners.get(action)?.length ?? 0) > 0; + return this.count(action) > 0; } register(action: Action, runner: ActionRunner): Disposable { @@ -90,6 +107,10 @@ export class ActionRunners implements Disposable { }; } + registerDefault(action: Action, runner: Omit): Disposable { + return this.register(action, { ...runner, name: defaultActionRunnerName }); + } + async run(context: T) { const runners = this._actionRunners.get(context.type); if (runners == null || runners.length === 0) return; @@ -120,11 +141,11 @@ export class ActionRunners implements Disposable { switch (context.type) { case 'createPullRequest': title = 'Create Pull Request'; - placeholder = 'Choose which provider to use to create a pull request'; + placeholder = 'Choose how to create a pull request'; break; case 'openPullRequest': title = 'Open Pull Request'; - placeholder = 'Choose which provider to use to open the pull request'; + placeholder = 'Choose how to open the pull request'; break; } @@ -150,6 +171,6 @@ export class ActionRunners implements Disposable { } private async _updateContextKeys(action: Actions) { - await setContext(`${ContextKeys.ActionPrefix}${action}`, this.has(action)); + await setContext(`${ContextKeys.ActionPrefix}${action}`, this.count(action)); } } diff --git a/src/api/api.ts b/src/api/api.ts index 56f7691..b656692 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -2,6 +2,7 @@ import { Disposable } from 'vscode'; import { Container } from '../container'; import { Logger } from '../logger'; +import { defaultActionRunnerName } from './actionRunners'; import { Action, ActionContext, ActionRunner, GitLensApi } from './gitlens'; const emptyDisposable = Object.freeze({ @@ -12,6 +13,9 @@ const emptyDisposable = Object.freeze({ export class Api implements GitLensApi { @preview() registerActionRunner(action: Action, runner: ActionRunner): Disposable { + if (runner.name === defaultActionRunnerName) { + throw new Error(`Cannot use the reserved name '${defaultActionRunnerName}'`); + } return Container.actionRunners.register(action, runner); } diff --git a/src/api/gitlens.d.ts b/src/api/gitlens.d.ts index 6eada64..10e8cd9 100644 --- a/src/api/gitlens.d.ts +++ b/src/api/gitlens.d.ts @@ -30,6 +30,7 @@ export type ActionContext = CreatePullRequestActionContext | OpenPullRequestActi export type Action = T['type']; export interface ActionRunner { + readonly name: string; readonly label: string; run(context: ActionContext): void | Promise; diff --git a/src/commands/common.ts b/src/commands/common.ts index 1c157e2..11e1106 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -173,6 +173,13 @@ export function executeActionCommand(action: Action, return commands.executeCommand(`${Commands.ActionPrefix}${action}`, { ...args, type: action }); } +export function getMarkdownActionCommand(action: Action, args: Omit): string { + return Command.getMarkdownCommandArgsCore(`${Commands.ActionPrefix}${action}` as Commands, { + ...args, + type: action, + }); +} + export function executeCommand(command: Commands, args: T) { return commands.executeCommand(command, args); } diff --git a/src/extension.ts b/src/extension.ts index e675b0f..d8fbdd5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,9 +1,9 @@ 'use strict'; import * as paths from 'path'; import { commands, ExtensionContext, extensions, window, workspace } from 'vscode'; +import { GitLensApi } from '../src/api/gitlens'; import { Api } from './api/api'; -import { GitLensApi } from './api/gitlens'; -import { Commands, registerCommands } from './commands'; +import { Commands, executeCommand, OpenPullRequestOnRemoteCommandArgs, registerCommands } from './commands'; import { configuration, Configuration } from './configuration'; import { ContextKeys, GlobalState, GlyphChars, setContext, SyncedState } from './constants'; import { Container } from './container'; @@ -135,6 +135,7 @@ export async function activate(context: ExtensionContext): Promise { + if (ctx.type !== 'openPullRequest') return; + + void (await executeCommand(Commands.OpenPullRequestOnRemote, { + pr: { url: ctx.pullRequest.url }, + })); + }, + }), + ); +} + async function showWelcomeOrWhatsNew(version: string, previousVersion: string | undefined) { if (previousVersion == null) { Logger.log('GitLens first-time install'); diff --git a/src/git/formatters/commitFormatter.ts b/src/git/formatters/commitFormatter.ts index c62f3e1..c0402bc 100644 --- a/src/git/formatters/commitFormatter.ts +++ b/src/git/formatters/commitFormatter.ts @@ -4,6 +4,7 @@ import { Commands, ConnectRemoteProviderCommand, DiffWithCommand, + getMarkdownActionCommand, InviteToLiveShareCommand, OpenCommitOnRemoteCommand, OpenFileAtRevisionCommand, @@ -302,8 +303,10 @@ export class CommitFormatter extends Formatter { const { pullRequestOrRemote: pr } = this._options; if (pr != null) { if (PullRequest.is(pr)) { - commands += `[$(git-pull-request) PR #${pr.id}](${pr.url} "Open Pull Request \\#${pr.id} on ${ - pr.provider + commands += `[$(git-pull-request) PR #${pr.id}](${getMarkdownActionCommand('openPullRequest', { + pullRequest: { id: pr.id, provider: pr.provider, repoPath: this._item.repoPath, url: pr.url }, + })} "Open Pull Request \\#${pr.id}${ + Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider}` : '' }\n${GlyphChars.Dash.repeat(2)}\n${pr.title}\n${pr.state}, ${pr.formatDateFromNow()}")${separator}`; } else if (pr instanceof Promises.CancellationError) { commands += `[$(git-pull-request) PR $(sync~spin)](command:${Commands.RefreshHover} "Searching for a Pull Request (if any) that introduced this commit...")${separator}`; @@ -456,8 +459,10 @@ export class CommitFormatter extends Formatter { let text; if (PullRequest.is(pr)) { if (this._options.markdown) { - text = `[PR #${pr.id}](${pr.url} "Open Pull Request \\#${pr.id} on ${ - pr.provider + text = `[PR #${pr.id}](${getMarkdownActionCommand('openPullRequest', { + pullRequest: { id: pr.id, provider: pr.provider, repoPath: this._item.repoPath, url: pr.url }, + })} "Open Pull Request \\#${pr.id}${ + Container.actionRunners.count('openPullRequest') == 1 ? ` on ${pr.provider}` : '' }\n${GlyphChars.Dash.repeat(2)}\n${pr.title}\n${pr.state}, ${pr.formatDateFromNow()}")`; } else if (this._options.footnotes != null) { const index = this._options.footnotes.size + 1; diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index e885e1c..95cefd4 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -7,6 +7,7 @@ import { Disposable, Event, EventEmitter, + MarkdownString, MessageItem, TreeDataProvider, TreeItem, @@ -115,15 +116,22 @@ export abstract class ViewBase< const item = await getTreeItem.apply(this, [node]); const parent = node.getParent(); - if (parent != null) { - item.tooltip = `${ - item.tooltip ?? item.label - }\n\nDBG:\nnode: ${node.toString()}\nparent: ${parent.toString()}\ncontext: ${item.contextValue}`; - } else { - item.tooltip = `${item.tooltip ?? item.label}\n\nDBG:\nnode: ${node.toString()}\ncontext: ${ - item.contextValue - }`; + let tooltip = item.tooltip; + + if (tooltip == null) { + item.tooltip = tooltip = new MarkdownString( + item.label != null && typeof item.label !== 'string' ? item.label.label : item.label ?? '', + ); + } else if (typeof tooltip === 'string') { + item.tooltip = tooltip = new MarkdownString(tooltip); } + + tooltip.appendMarkdown( + `\n\n---\n\ncontext: \`${item.contextValue}\`\n\nnode: \`${node.toString()}\`${ + parent != null ? `\n\nparent: \`${parent.toString()}\`` : '' + }`, + ); + return item; }; } diff --git a/yarn.lock b/yarn.lock index f233b86..9631e20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -191,10 +191,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash-es@4.17.3": - version "4.17.3" - resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.3.tgz#87eb0b3673b076b8ee655f1890260a136af09a2d" - integrity sha512-iHI0i7ZAL1qepz1Y7f3EKg/zUMDwDfTzitx+AlHhJJvXwenP682ZyGbgPSc5Ej3eEAKVbNWKFuwOadCj5vBbYQ== +"@types/lodash-es@4.17.4": + version "4.17.4" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.4.tgz#b2e440d2bf8a93584a9fd798452ec497986c9b97" + integrity sha512-BBz79DCJbD2CVYZH67MBeHZRX++HF+5p8Mo5MzjZi64Wac39S3diedJYHZtScbRVf4DjZyN6LzA0SB0zy+HSSQ== dependencies: "@types/lodash" "*" @@ -209,9 +209,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*", "@types/node@>= 8": - version "14.14.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.12.tgz#0b1d86f8c40141091285dea02e4940df73bba43f" - integrity sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g== + version "14.14.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" + integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== "@types/node@12.12.70": version "12.12.70" @@ -245,10 +245,10 @@ dependencies: source-map "^0.6.1" -"@types/vscode@1.51.0": - version "1.51.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.51.0.tgz#f2e324d8385db95030b1454fd1f043618e08097d" - integrity sha512-C/jZ35OT5k/rsJyAK8mS1kM++vMcm89oSWegkzxRCvHllIq0cToZAkIDs6eCY4SKrvik3nrhELizyLcM0onbQA== +"@types/vscode@1.52.0": + version "1.52.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.52.0.tgz#61917968dd403932127fc4004a21fd8d69e4f61c" + integrity sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA== "@types/webpack-sources@*": version "2.1.0" @@ -1019,9 +1019,9 @@ cancellationtoken@^2.0.1: integrity sha512-uF4sHE5uh2VdEZtIRJKGoXAD9jm7bFY0tDRCzH4iLp262TOJ2lrtNHjMG2zc8H+GICOpELIpM7CGW5JeWnb3Hg== caniuse-lite@^1.0.30001165: - version "1.0.30001165" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz#32955490d2f60290bb186bb754f2981917fa744f" - integrity sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA== + version "1.0.30001166" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001166.tgz#ca73e8747acfd16a4fd6c4b784f1b995f9698cf8" + integrity sha512-nCL4LzYK7F4mL0TjEMeYavafOGnBa98vTudH5c8lW9izUjnB99InG6pmC1ElAI1p0GlyZajv4ltUdFXvOHIl1A== caseless@~0.12.0: version "0.12.0" @@ -1220,9 +1220,9 @@ commander@^4.1.1: integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== commander@^6.1.0, commander@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" - integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== commondir@^1.0.1: version "1.0.1" @@ -1746,9 +1746,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.621: - version "1.3.624" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.624.tgz#0ed5d2930ab37c4ec977ab58def2cda9c446e1c3" - integrity sha512-o7j+CRwsXXj3180EeT6fnOOCyhdeBCmcjQLfe0ObpSH1N0PSylrQ3ibBfAZ39Y28YQ+gKKmU2xSSggdARpjuDg== + version "1.3.626" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.626.tgz#48acdf322be07feb2c1330ba05e4bf6327f721a3" + integrity sha512-7CanEvJx74EnvjHu1X8gf93KieyxvFLnqOXAH/ddjWD4RrUZYqdg3pykrQ/7t6SLI7DTsp4tfQXEfzeK5t6oAw== emoji-regex@^7.0.1: version "7.0.3"