WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 1885ea2

Browse files
committed
Use command for opening issues on remote providers.
It lets us track the source of event. (#4764, #4765, PLG-139)
1 parent b1835a8 commit 1885ea2

File tree

11 files changed

+152
-6
lines changed

11 files changed

+152
-6
lines changed

contributions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4090,6 +4090,10 @@
40904090
]
40914091
}
40924092
},
4093+
"gitlens.openIssueOnRemote": {
4094+
"label": "Open Issue on Remote",
4095+
"icon": "$(globe)"
4096+
},
40934097
"gitlens.openOnlyChangedFiles": {
40944098
"label": "Open Changed & Close Unchanged Files",
40954099
"commandPalette": "gitlens:enabled",

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7822,6 +7822,11 @@
78227822
"title": "Open Folder History in Commit Graph",
78237823
"icon": "$(gitlens-graph)"
78247824
},
7825+
{
7826+
"command": "gitlens.openIssueOnRemote",
7827+
"title": "Open Issue on Remote",
7828+
"icon": "$(globe)"
7829+
},
78257830
{
78267831
"command": "gitlens.openOnlyChangedFiles",
78277832
"title": "Open Changed & Close Unchanged Files",
@@ -12800,6 +12805,10 @@
1280012805
"command": "gitlens.openFolderHistoryInGraph:scm",
1280112806
"when": "false"
1280212807
},
12808+
{
12809+
"command": "gitlens.openIssueOnRemote",
12810+
"when": "false"
12811+
},
1280312812
{
1280412813
"command": "gitlens.openOnlyChangedFiles",
1280512814
"when": "gitlens:enabled"

src/api/actionRunners.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { sortCompare } from '../system/string';
1212
import type { Action, ActionContext, ActionRunner } from './gitlens';
1313

1414
type Actions = ActionContext['type'];
15-
const actions: Actions[] = ['createPullRequest', 'openPullRequest', 'hover.commands'];
15+
const actions: Actions[] = ['createPullRequest', 'openPullRequest', 'openIssue', 'hover.commands'];
1616

1717
// The order here determines the sorting of these actions when shown to the user
1818
export const enum ActionRunnerType {
@@ -288,6 +288,10 @@ export class ActionRunners implements Disposable {
288288
title = 'Open Pull Request';
289289
placeholder = 'Choose how to open the pull request';
290290
break;
291+
case 'openIssue':
292+
title = 'Open Issue';
293+
placeholder = 'Choose how to open the issue';
294+
break;
291295
case 'hover.commands':
292296
title = 'Need Help or Want to Collaborate?';
293297
placeholder = 'Choose what you would like to do';

src/api/gitlens.d.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ export interface OpenPullRequestActionContext {
4141
readonly source?: Source;
4242
}
4343

44+
export interface OpenIssueActionContext {
45+
readonly type: 'openIssue';
46+
readonly provider: RemoteProvider | undefined;
47+
readonly issue: {
48+
readonly url: string;
49+
};
50+
readonly source?: Source;
51+
}
52+
4453
export interface HoverCommandsActionContext {
4554
readonly type: 'hover.commands';
4655

@@ -62,7 +71,11 @@ export interface HoverCommandsActionContext {
6271
readonly source?: Source;
6372
}
6473

65-
export type ActionContext = CreatePullRequestActionContext | OpenPullRequestActionContext | HoverCommandsActionContext;
74+
export type ActionContext =
75+
| CreatePullRequestActionContext
76+
| OpenPullRequestActionContext
77+
| OpenIssueActionContext
78+
| HoverCommandsActionContext;
6679
export type Action<T extends ActionContext> = T['type'];
6780

6881
export interface ActionRunner<T extends ActionContext = ActionContext> {

src/autolinks/autolinksProvider.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { ConfigurationChangeEvent } from 'vscode';
22
import { Disposable } from 'vscode';
3+
import type { OpenIssueActionContext } from '../api/gitlens';
4+
import { OpenIssueOnRemoteCommand } from '../commands/openIssueOnRemote';
35
import { GlyphChars } from '../constants';
46
import type { IntegrationIds } from '../constants.integrations';
7+
import type { Source } from '../constants.telemetry';
58
import type { Container } from '../container';
69
import type { GitRemote } from '../git/models/remote';
710
import type { RemoteProvider, RemoteProviderId } from '../git/remotes/remoteProvider';
@@ -279,6 +282,7 @@ export class AutolinksProvider implements Disposable {
279282
enrichedAutolinks?: Map<string, MaybeEnrichedAutolink>,
280283
prs?: Set<string>,
281284
footnotes?: Map<number, string>,
285+
source?: Source,
282286
): string {
283287
const includeFootnotesInText = outputFormat === 'plaintext' && footnotes == null;
284288
if (includeFootnotesInText) {
@@ -291,15 +295,31 @@ export class AutolinksProvider implements Disposable {
291295
for (const [, [, link]] of enrichedAutolinks) {
292296
if (this.ensureAutolinkCached(link)) {
293297
if (link.tokenize != null) {
294-
text = link.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, prs, footnotes);
298+
text = link.tokenize(
299+
text,
300+
outputFormat,
301+
tokenMapping,
302+
enrichedAutolinks,
303+
prs,
304+
footnotes,
305+
source,
306+
);
295307
}
296308
}
297309
}
298310
} else {
299311
for (const ref of this._references) {
300312
if (this.ensureAutolinkCached(ref)) {
301313
if (ref.tokenize != null) {
302-
text = ref.tokenize(text, outputFormat, tokenMapping, enrichedAutolinks, prs, footnotes);
314+
text = ref.tokenize(
315+
text,
316+
outputFormat,
317+
tokenMapping,
318+
enrichedAutolinks,
319+
prs,
320+
footnotes,
321+
source,
322+
);
303323
}
304324
}
305325
}
@@ -323,6 +343,7 @@ export class AutolinksProvider implements Disposable {
323343
enrichedAutolinks,
324344
prs,
325345
footnotes,
346+
source,
326347
);
327348
}
328349
}
@@ -361,6 +382,7 @@ export class AutolinksProvider implements Disposable {
361382
enrichedAutolinks?: Map<string, MaybeEnrichedAutolink>,
362383
prs?: Set<string>,
363384
footnotes?: Map<number, string>,
385+
source?: Source,
364386
) => {
365387
let footnoteIndex: number;
366388

@@ -370,7 +392,15 @@ export class AutolinksProvider implements Disposable {
370392
return text.replace(
371393
ref.messageMarkdownRegex,
372394
(_: string, prefix: string, linkText: string, num: string) => {
373-
const url = encodeUrl(ref.url.replace(numRegex, num));
395+
const rawUrl = encodeUrl(ref.url.replace(numRegex, num));
396+
const footnoteSource = source && { ...source, detail: 'footnote' };
397+
const urlCommandContext: {
398+
provider: undefined | OpenIssueActionContext['provider'];
399+
issue: { url: string };
400+
} = {
401+
provider: undefined,
402+
issue: { url: rawUrl },
403+
};
374404

375405
let title = '';
376406
if (ref.title) {
@@ -380,6 +410,10 @@ export class AutolinksProvider implements Disposable {
380410
if (issueResult?.value != null) {
381411
if (issueResult.paused) {
382412
if (footnotes != null && !prs?.has(num)) {
413+
const url = OpenIssueOnRemoteCommand.createMarkdownCommandLink({
414+
...urlCommandContext,
415+
source: footnoteSource,
416+
});
383417
const name =
384418
ref.description?.replace(numRegex, num) ??
385419
`Custom Autolink ${ref.prefix}${num}`;
@@ -396,6 +430,16 @@ export class AutolinksProvider implements Disposable {
396430
const issueTitle = escapeMarkdown(issue.title.trim());
397431
const issueTitleQuoteEscaped = issueTitle.replace(/"/g, '\\"');
398432

433+
urlCommandContext.provider = issue.provider && {
434+
id: issue.provider.id,
435+
name: issue.provider.name,
436+
domain: issue.provider.domain,
437+
};
438+
const url = OpenIssueOnRemoteCommand.createMarkdownCommandLink({
439+
...urlCommandContext,
440+
source: footnoteSource,
441+
});
442+
399443
if (footnotes != null && !prs?.has(num)) {
400444
footnoteIndex = footnotes.size + 1;
401445
footnotes.set(
@@ -417,6 +461,10 @@ export class AutolinksProvider implements Disposable {
417461
)}`;
418462
}
419463
} else if (footnotes != null && !prs?.has(num)) {
464+
const url = OpenIssueOnRemoteCommand.createMarkdownCommandLink({
465+
...urlCommandContext,
466+
source: footnoteSource,
467+
});
420468
const name =
421469
ref.description?.replace(numRegex, num) ??
422470
`Custom Autolink ${ref.prefix}${num}`;
@@ -429,6 +477,10 @@ export class AutolinksProvider implements Disposable {
429477
title += '"';
430478
}
431479

480+
const url = OpenIssueOnRemoteCommand.createMarkdownCommandLink({
481+
...urlCommandContext,
482+
source: source,
483+
});
432484
const token = `\x00${tokenMapping.size}\x00`;
433485
tokenMapping.set(token, `[${linkText}](${url}${title})`);
434486
return `${prefix}${token}`;

src/autolinks/models/autolinks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Source } from '../../constants.telemetry';
12
import type { IssueOrPullRequest } from '../../git/models/issueOrPullRequest';
23
import type { ProviderReference } from '../../git/models/remoteProvider';
34
import type { ResourceDescriptor } from '../../git/models/resourceDescriptor';
@@ -49,6 +50,7 @@ export interface CacheableAutolinkReference extends AutolinkReference {
4950
enrichedAutolinks?: Map<string, MaybeEnrichedAutolink>,
5051
prs?: Set<string>,
5152
footnotes?: Map<number, string>,
53+
source?: Source,
5254
) => string)
5355
| null;
5456

@@ -67,6 +69,7 @@ export interface DynamicAutolinkReference {
6769
enrichedAutolinks?: Map<string, MaybeEnrichedAutolink>,
6870
prs?: Set<string>,
6971
footnotes?: Map<number, string>,
72+
source?: Source,
7073
) => string)
7174
| null;
7275
parse: (text: string, autolinks: Map<string, Autolink>) => void;

src/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import './commands/openFileOnRemote';
4747
import './commands/openFileAtRevision';
4848
import './commands/openFileAtRevisionFrom';
4949
import './commands/openOnRemote';
50+
import './commands/openIssueOnRemote';
5051
import './commands/openPullRequestOnRemote';
5152
import './commands/openRepoOnRemote';
5253
import './commands/openRevisionFile';

src/commands/openIssueOnRemote.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { env, window } from 'vscode';
2+
import type { OpenIssueActionContext } from '../api/gitlens';
3+
import { actionCommandPrefix } from '../constants.commands';
4+
import { command } from '../system/-webview/command';
5+
import { openUrl } from '../system/-webview/vscode/uris';
6+
import { createMarkdownCommandLink } from '../system/commands';
7+
import { Logger } from '../system/logger';
8+
import { getLogScope } from '../system/logger.scope';
9+
import { GlCommandBase } from './commandBase';
10+
11+
export interface OpenIssueOnRemoteCommandArgs {
12+
clipboard?: boolean;
13+
issue?: { url: string };
14+
}
15+
16+
@command()
17+
export class OpenIssueOnRemoteCommand extends GlCommandBase {
18+
static createMarkdownCommandLink(args: Omit<OpenIssueActionContext, 'type'>): string {
19+
return createMarkdownCommandLink(`${actionCommandPrefix}openIssue`, {
20+
...args,
21+
type: 'openIssue',
22+
});
23+
}
24+
25+
constructor() {
26+
super('gitlens.openIssueOnRemote');
27+
}
28+
29+
async execute(args?: OpenIssueOnRemoteCommandArgs): Promise<void> {
30+
if (args?.issue == null) {
31+
void window.showInformationMessage('No issue provided');
32+
Logger.warn(getLogScope(), 'No issue provided in OpenIssueOnRemoteCommand', args);
33+
return;
34+
}
35+
36+
if (args.clipboard) {
37+
await env.clipboard.writeText(args.issue.url);
38+
} else {
39+
void openUrl(args.issue.url);
40+
}
41+
}
42+
}

src/constants.commands.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,4 +1077,5 @@ export type ContributedKeybindingCommands =
10771077
export type ContributedOrphansOrInternalCommands =
10781078
| 'gitlens.graph.pushWithForce'
10791079
| 'gitlens.openFolderHistoryInGraph'
1080+
| 'gitlens.openIssueOnRemote'
10801081
| 'gitlens.plus.cloudIntegrations.connect';

src/extension.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ import { hrtime } from '@env/hrtime';
44
import { loggingJsonReplacer } from '@env/json';
55
import { isWeb } from '@env/platform';
66
import { Api } from './api/api';
7-
import type { CreatePullRequestActionContext, GitLensApi, OpenPullRequestActionContext } from './api/gitlens';
7+
import type {
8+
CreatePullRequestActionContext,
9+
GitLensApi,
10+
OpenIssueActionContext,
11+
OpenPullRequestActionContext,
12+
} from './api/gitlens';
813
import type { CreatePullRequestOnRemoteCommandArgs } from './commands/createPullRequestOnRemote';
14+
import type { OpenIssueOnRemoteCommandArgs } from './commands/openIssueOnRemote';
915
import type { OpenPullRequestOnRemoteCommandArgs } from './commands/openPullRequestOnRemote';
1016
import { fromOutputLevel } from './config';
1117
import { trackableSchemes } from './constants';
@@ -338,6 +344,16 @@ function registerBuiltInActionRunners(container: Container): void {
338344
}));
339345
},
340346
}),
347+
container.actionRunners.registerBuiltIn<OpenIssueActionContext>('openIssue', {
348+
label: ctx => `Open Issue on ${ctx.provider?.name ?? 'Remote'}`,
349+
run: async ctx => {
350+
if (ctx.type !== 'openIssue') return;
351+
352+
void (await executeCommand<OpenIssueOnRemoteCommandArgs>('gitlens.openIssueOnRemote', {
353+
issue: { url: ctx.issue.url },
354+
}));
355+
},
356+
}),
341357
);
342358
}
343359

0 commit comments

Comments
 (0)