Skip to content

Commit a81685c

Browse files
committed
Adds Open tag on remote option to tagView
1 parent b779add commit a81685c

20 files changed

+270
-20
lines changed

package.json

+74-1
Original file line numberDiff line numberDiff line change
@@ -3668,7 +3668,8 @@
36683668
"fileInCommit",
36693669
"fileInBranch",
36703670
"fileLine",
3671-
"fileRange"
3671+
"fileRange",
3672+
"tag"
36723673
],
36733674
"properties": {
36743675
"repository": {
@@ -3706,6 +3707,10 @@
37063707
"fileRange": {
37073708
"type": "string",
37083709
"markdownDescription": "Specifies the format of a range in a file URL for the custom remote service\n\nAvailable tokens\\\n`${start}` — starting line\\\n`${end}` — ending line"
3710+
},
3711+
"tag": {
3712+
"type": "string",
3713+
"markdownDescription": "Specifies the format of a tag URL for the custom remote service\n\nAvailable tokens\\\n`${repo}` — repository path\\\n`${tagName}` — name of the tag"
37093714
}
37103715
},
37113716
"additionalProperties": false
@@ -6757,6 +6762,22 @@
67576762
"title": "Open Commits on Remote",
67586763
"icon": "$(globe)"
67596764
},
6765+
{
6766+
"command": "gitlens.openTagOnRemote",
6767+
"title": "Open Tag on Remote",
6768+
"category": "GitLens",
6769+
"icon": "$(globe)"
6770+
},
6771+
{
6772+
"command": "gitlens.views.openTagOnRemote",
6773+
"title": "Open Tag on Remote",
6774+
"icon": "$(globe)"
6775+
},
6776+
{
6777+
"command": "gitlens.views.openTagOnRemote.multi",
6778+
"title": "Open Tags on Remote",
6779+
"icon": "$(globe)"
6780+
},
67606781
{
67616782
"command": "gitlens.copyRemoteCommitUrl",
67626783
"title": "Copy Remote Commit URL",
@@ -6773,6 +6794,22 @@
67736794
"title": "Copy Remote Commit URLs",
67746795
"icon": "$(copy)"
67756796
},
6797+
{
6798+
"command": "gitlens.copyRemoteTagUrl",
6799+
"title": "Copy Remote Tag URL",
6800+
"category": "GitLens",
6801+
"icon": "$(copy)"
6802+
},
6803+
{
6804+
"command": "gitlens.views.copyRemoteTagUrl",
6805+
"title": "Copy Remote Tag URL",
6806+
"icon": "$(copy)"
6807+
},
6808+
{
6809+
"command": "gitlens.views.copyRemoteTagUrl.multi",
6810+
"title": "Copy Remote Tag URLs",
6811+
"icon": "$(copy)"
6812+
},
67766813
{
67776814
"command": "gitlens.openComparisonOnRemote",
67786815
"title": "Open Comparison on Remote",
@@ -10560,6 +10597,18 @@
1056010597
"command": "gitlens.views.openCommitOnRemote.multi",
1056110598
"when": "false"
1056210599
},
10600+
{
10601+
"command": "gitlens.openTagOnRemote",
10602+
"when": "gitlens:repos:withRemotes"
10603+
},
10604+
{
10605+
"command": "gitlens.views.openTagOnRemote",
10606+
"when": "false"
10607+
},
10608+
{
10609+
"command": "gitlens.views.openTagOnRemote.multi",
10610+
"when": "false"
10611+
},
1056310612
{
1056410613
"command": "gitlens.copyRemoteCommitUrl",
1056510614
"when": "gitlens:repos:withRemotes"
@@ -10572,6 +10621,14 @@
1057210621
"command": "gitlens.views.copyRemoteCommitUrl.multi",
1057310622
"when": "false"
1057410623
},
10624+
{
10625+
"command": "gitlens.views.copyRemoteTagUrl",
10626+
"when": "false"
10627+
},
10628+
{
10629+
"command": "gitlens.views.copyRemoteTagUrl.multi",
10630+
"when": "false"
10631+
},
1057510632
{
1057610633
"command": "gitlens.openComparisonOnRemote",
1057710634
"when": "false"
@@ -14868,6 +14925,12 @@
1486814925
"group": "inline@99",
1486914926
"alt": "gitlens.views.copyRemoteCommitUrl"
1487014927
},
14928+
{
14929+
"command": "gitlens.views.openTagOnRemote",
14930+
"when": "gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
14931+
"group": "inline@99",
14932+
"alt": "gitlens.views.copyRemoteTagUrl"
14933+
},
1487114934
{
1487214935
"command": "gitlens.views.cherryPick",
1487314936
"when": "!listMultiSelection && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:commit\\b(?!.*?\\b\\+(current|rebase)\\b)/",
@@ -14984,6 +15047,16 @@
1498415047
"when": "listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:commit\\b/",
1498515048
"group": "3_gitlens_explore@2"
1498615049
},
15050+
{
15051+
"command": "gitlens.views.openTagOnRemote",
15052+
"when": "!listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
15053+
"group": "3_gitlens_explore@2"
15054+
},
15055+
{
15056+
"command": "gitlens.views.openTagOnRemote.multi",
15057+
"when": "listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
15058+
"group": "3_gitlens_explore@2"
15059+
},
1498715060
{
1498815061
"submenu": "gitlens/share",
1498915062
"when": "viewItem =~ /gitlens:(branch|commit|compare:(branch(?=.*?\\b\\+comparing\\b)|results(:commits(?!:)|(?!:)))|remote|repo-folder|repository|stash|status:upstream|tag|workspace|file\\b(?=.*?\\b\\+committed\\b))\\b/",

src/commands.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import './commands/openBranchOnRemote';
3333
import './commands/openCurrentBranchOnRemote';
3434
import './commands/openChangedFiles';
3535
import './commands/openCommitOnRemote';
36+
import './commands/openTagOnRemote';
3637
import './commands/openComparisonOnRemote';
3738
import './commands/openFileFromRemote';
3839
import './commands/openFileOnRemote';

src/commands/openOnRemote.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,13 @@ export class OpenOnRemoteCommand extends Command {
196196
break;
197197
}
198198

199-
// case RemoteResourceType.Tag: {
200-
// title = getTitlePrefix('Tag');
201-
// if (resources.length === 1) {
202-
// title += `${pad(GlyphChars.Dot, 2, 2)}${args.resource.tag}`;
203-
// }
204-
// break;
205-
// }
199+
case RemoteResourceType.Tag: {
200+
title = getTitlePrefix('Tag');
201+
if (resources.length === 1) {
202+
title += `${pad(GlyphChars.Dot, 2, 2)}${resource.tag}`;
203+
}
204+
break;
205+
}
206206
}
207207

208208
const pick = await showRemoteProviderPicker(title, placeholder, resources, remotes, options);

src/commands/openTagOnRemote.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type { TextEditor, Uri } from 'vscode';
2+
import { Commands } from '../constants.commands';
3+
import type { Container } from '../container';
4+
import { GitUri } from '../git/gitUri';
5+
// import { getTagNameWithoutRemote, getRemoteNameFromTagName } from '../git/models/tag';
6+
import { RemoteResourceType } from '../git/models/remoteResource';
7+
import { showGenericErrorMessage } from '../messages';
8+
import { CommandQuickPickItem } from '../quickpicks/items/common';
9+
import { ReferencesQuickPickIncludes, showReferencePicker } from '../quickpicks/referencePicker';
10+
import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
11+
import { Logger } from '../system/logger';
12+
import { command, executeCommand } from '../system/vscode/command';
13+
import type { CommandContext } from './base';
14+
import { ActiveEditorCommand, getCommandUri, isCommandContextViewNodeHasTag } from './base';
15+
import type { OpenOnRemoteCommandArgs } from './openOnRemote';
16+
17+
export interface OpenTagOnRemoteCommandArgs {
18+
tag?: string;
19+
clipboard?: boolean;
20+
remote?: string;
21+
}
22+
23+
@command()
24+
export class OpenTagOnRemoteCommand extends ActiveEditorCommand {
25+
constructor(private readonly container: Container) {
26+
super([Commands.OpenTagOnRemote, Commands.CopyRemoteTagUrl]);
27+
}
28+
29+
protected override preExecute(context: CommandContext, args?: OpenTagOnRemoteCommandArgs) {
30+
if (isCommandContextViewNodeHasTag(context)) {
31+
args = {
32+
...args,
33+
tag: context.node.tag.name,
34+
remote: context.node.tag.name,
35+
};
36+
}
37+
38+
if (context.command === Commands.CopyRemoteTagUrl) {
39+
args = { ...args, clipboard: true };
40+
}
41+
42+
return this.execute(context.editor, context.uri, args);
43+
}
44+
45+
async execute(editor?: TextEditor, uri?: Uri, args?: OpenTagOnRemoteCommandArgs) {
46+
uri = getCommandUri(uri, editor);
47+
48+
const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
49+
50+
const repoPath = (
51+
await getBestRepositoryOrShowPicker(
52+
gitUri,
53+
editor,
54+
args?.clipboard ? 'Copy Remote Tag URL' : 'Open Tag On Remote',
55+
)
56+
)?.path;
57+
if (!repoPath) return;
58+
59+
args = { ...args };
60+
61+
try {
62+
if (args.tag == null) {
63+
const pick = await showReferencePicker(
64+
repoPath,
65+
args.clipboard ? 'Copy Remote Tag URL' : 'Open Tag On Remote',
66+
args.clipboard ? 'Choose a Tag to copy the URL from' : 'Choose a Tag to open',
67+
{
68+
autoPick: true,
69+
filter: { tags: () => true, branches: () => false },
70+
include: ReferencesQuickPickIncludes.Tags,
71+
sort: { tags: { current: true } },
72+
},
73+
);
74+
if (pick == null || pick instanceof CommandQuickPickItem) return;
75+
76+
if (pick.refType === 'tag') {
77+
args.tag = pick.name;
78+
} else {
79+
args.tag = pick.ref;
80+
}
81+
}
82+
83+
void (await executeCommand<OpenOnRemoteCommandArgs>(Commands.OpenOnRemote, {
84+
resource: {
85+
type: RemoteResourceType.Tag,
86+
tag: args.tag,
87+
},
88+
repoPath: repoPath,
89+
remote: args.remote,
90+
clipboard: args.clipboard,
91+
}));
92+
} catch (ex) {
93+
Logger.error(ex, 'OpenTagOnRemoteCommand');
94+
void showGenericErrorMessage('Unable to open Tag on remote provider');
95+
}
96+
}
97+
}

src/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -580,12 +580,14 @@ export interface RemotesUrlsConfig {
580580
readonly fileInCommit: string;
581581
readonly fileLine: string;
582582
readonly fileRange: string;
583+
readonly tag: string;
583584
}
584585

585586
// NOTE: Must be kept in sync with `gitlens.advanced.messages` setting in the package.json
586587
export type SuppressedMessages =
587588
| 'suppressCommitHasNoPreviousCommitWarning'
588589
| 'suppressCommitNotFoundWarning'
590+
| 'suppressTagNotFoundWarning'
589591
| 'suppressCreatePullRequestPrompt'
590592
| 'suppressDebugLoggingWarning'
591593
| 'suppressFileNotUnderSourceControlWarning'

src/constants.commands.ts

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const enum Commands {
2929
CopyRemoteBranchesUrl = 'gitlens.copyRemoteBranchesUrl',
3030
CopyRemoteBranchUrl = 'gitlens.copyRemoteBranchUrl',
3131
CopyRemoteCommitUrl = 'gitlens.copyRemoteCommitUrl',
32+
CopyRemoteTagUrl = 'gitlens.copyRemoteTagUrl',
3233
CopyRemoteComparisonUrl = 'gitlens.copyRemoteComparisonUrl',
3334
CopyRemoteFileUrl = 'gitlens.copyRemoteFileUrlToClipboard',
3435
CopyRemoteFileUrlWithoutRange = 'gitlens.copyRemoteFileUrlWithoutRange',
@@ -81,6 +82,7 @@ export const enum Commands {
8182
OpenCurrentBranchOnRemote = 'gitlens.openCurrentBranchOnRemote',
8283
OpenChangedFiles = 'gitlens.openChangedFiles',
8384
OpenCommitOnRemote = 'gitlens.openCommitOnRemote',
85+
OpenTagOnRemote = 'gitlens.openTagOnRemote',
8486
OpenComparisonOnRemote = 'gitlens.openComparisonOnRemote',
8587
OpenFileHistory = 'gitlens.openFileHistory',
8688
OpenFileFromRemote = 'gitlens.openFileFromRemote',

src/git/models/remoteResource.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const enum RemoteResourceType {
1010
File = 'file',
1111
Repo = 'repo',
1212
Revision = 'revision',
13-
// Tag = 'tag',
13+
Tag = 'tag',
1414
}
1515

1616
export type RemoteResource =
@@ -58,11 +58,11 @@ export type RemoteResource =
5858
fileName: string;
5959
range?: Range;
6060
sha?: string;
61+
}
62+
| {
63+
type: RemoteResourceType.Tag;
64+
tag: string;
6165
};
62-
// | {
63-
// type: RemoteResourceType.Tag;
64-
// tag: string;
65-
// };
6666

6767
export function getNameFromRemoteResource(resource: RemoteResource) {
6868
switch (resource.type) {

src/git/remotes/azure-devops.ts

+4
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,8 @@ export class AzureDevOpsRemote extends RemoteProvider {
201201
if (branch) return this.encodeUrl(`${this.baseUrl}/?path=/${fileName}&version=GB${branch}&_a=contents${line}`);
202202
return this.encodeUrl(`${this.baseUrl}?path=/${fileName}${line}`);
203203
}
204+
205+
protected override getUrlForTag(tag: string): string {
206+
return this.encodeUrl(`${this.baseUrl}?version=GT${tag}`);
207+
}
204208
}

src/git/remotes/bitbucket-server.ts

+4
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,8 @@ export class BitbucketServerRemote extends RemoteProvider {
171171
if (branch) return `${this.encodeUrl(`${this.baseUrl}/browse/${fileName}?at=${branch}`)}${line}`;
172172
return `${this.encodeUrl(`${this.baseUrl}/browse/${fileName}`)}${line}`;
173173
}
174+
175+
protected override getUrlForTag(tag: string): string {
176+
return this.encodeUrl(`${this.baseUrl}/commits/tag/${tag}`);
177+
}
174178
}

src/git/remotes/bitbucket.ts

+4
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,8 @@ export class BitbucketRemote extends RemoteProvider {
157157
if (branch) return `${this.encodeUrl(`${this.baseUrl}/src/${branch}/${fileName}`)}${line}`;
158158
return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
159159
}
160+
161+
protected override getUrlForTag(tag: string): string {
162+
return this.encodeUrl(`${this.baseUrl}/commits/tag/${tag}`);
163+
}
160164
}

src/git/remotes/custom.ts

+4
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ export class CustomRemote extends RemoteProvider {
100100
return url;
101101
}
102102

103+
protected override getUrlForTag(tag: string): string {
104+
return this.getUrl(this.urls.tag, this.getContext({ tag: tag }));
105+
}
106+
103107
private getUrl(template: string, context: Record<string, string>): string {
104108
const url = interpolate(template, context);
105109
const encoded = getTokensFromTemplate(template).some(t => t.key.endsWith('_encoded'));

src/git/remotes/gerrit.ts

+4
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,8 @@ export class GerritRemote extends RemoteProvider {
191191
if (branch) return `${this.encodeUrl(`${this.getUrlForBranch(branch)}/${fileName}`)}${line}`;
192192
return `${this.encodeUrl(`${this.baseUrl}/+/HEAD/${fileName}`)}${line}`;
193193
}
194+
195+
protected override getUrlForTag(): string | undefined {
196+
return undefined;
197+
}
194198
}

src/git/remotes/gitea.ts

+4
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,8 @@ export class GiteaRemote extends RemoteProvider {
155155
// this route is deprecated but there is no alternative
156156
return `${this.encodeUrl(`${this.baseUrl}/src/${fileName}`)}${line}`;
157157
}
158+
159+
protected getUrlForTag(tag: string): string {
160+
return this.encodeUrl(`${this.baseUrl}/releases/tag/${tag}`);
161+
}
158162
}

src/git/remotes/github.ts

+3
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
298298
if (branch) return `${this.encodeUrl(`${this.baseUrl}/blob/${branch}/${fileName}`)}${line}`;
299299
return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
300300
}
301+
protected override getUrlForTag(tag: string) {
302+
return this.encodeUrl(`${this.baseUrl}/releases/tag/${tag}`);
303+
}
301304
}
302305

303306
const gitHubNoReplyAddressRegex = /^(?:(\d+)\+)?([a-zA-Z\d-]{1,39})@users\.noreply\.(.*)$/i;

src/git/remotes/gitlab.ts

+4
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,8 @@ export class GitLabRemote extends RemoteProvider<GitLabRepositoryDescriptor> {
382382
if (branch) return `${this.encodeUrl(`${this.baseUrl}/-/blob/${branch}/${fileName}`)}${line}`;
383383
return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
384384
}
385+
386+
protected override getUrlForTag(tag: string) {
387+
return this.encodeUrl(`${this.baseUrl}/-/tags/${tag}`);
388+
}
385389
}

0 commit comments

Comments
 (0)