Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 60 additions & 36 deletions src/service/providers/ImageLinkProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,43 +51,67 @@ export class ImageLinkProvider extends ProviderBase<DocumentLinkParams & Extende
}

private static getLinkForImage(image: string, imageTypes: Set<string>): { uri: string, start: number, length: number } | undefined {
let match: RegExpExecArray | null;
let namespace: string | undefined;
let imageName: string | undefined;

if ((match = dockerHubImageRegex.exec(image)) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('dockerHub');

return {
uri: `https://hub.docker.com/_/${imageName}`,
start: match.index,
length: imageName.length
};
} else if ((match = dockerHubNamespacedImageRegex.exec(image)) &&
(namespace = match.groups?.namespace) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('dockerHubNamespaced');

return {
uri: `https://hub.docker.com/r/${namespace}/${imageName}`,
start: match.index,
length: namespace.length + 1 + imageName.length // 1 is the length of the '/' after namespace
};
} else if ((match = mcrImageRegex.exec(image)) &&
(namespace = match.groups?.namespace?.replace(/\/$/, '')) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('mcr');

return {
uri: `https://hub.docker.com/_/microsoft-${namespace.replace('/', '-')}-${imageName}`,
start: match.index,
length: 18 + namespace.length + 1 + imageName.length // 18 is the length of 'mcr.microsoft.com/', 1 is the length of the '/' after namespace
};
const parts = image.split('/');

if (parts.length === 1) {
let match: RegExpExecArray | null;
let imageName: string | undefined;
if ((match = dockerHubImageRegex.exec(image)) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('dockerHub');

return {
uri: `https://hub.docker.com/_/${imageName}`,
start: match.index,
length: imageName.length
};
}
return undefined;
}

const firstPart = parts[0];
const isRegistry = firstPart.includes('.') || firstPart.includes(':') || firstPart === 'localhost';

if (isRegistry) {
if (firstPart === 'mcr.microsoft.com') {
let match: RegExpExecArray | null;
let namespace: string | undefined;
let imageName: string | undefined;
if ((match = mcrImageRegex.exec(image)) &&
(namespace = match.groups?.namespace?.replace(/\/$/, '')) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('mcr');

return {
uri: `https://hub.docker.com/_/microsoft-${namespace.replace('/', '-')}-${imageName}`,
start: match.index,
length: 18 + namespace.length + 1 + imageName.length // 18 is the length of 'mcr.microsoft.com/', 1 is the length of the '/' after namespace
};
}
}
return undefined;
}

if (parts.length === 2) {
let match: RegExpExecArray | null;
let namespace: string | undefined;
let imageName: string | undefined;
if ((match = dockerHubNamespacedImageRegex.exec(image)) &&
(namespace = match.groups?.namespace) &&
(imageName = match.groups?.imageName)) {

imageTypes.add('dockerHubNamespaced');

return {
uri: `https://hub.docker.com/r/${namespace}/${imageName}`,
start: match.index,
length: namespace.length + 1 + imageName.length // 1 is the length of the '/' after namespace
};
}
}

return undefined;
}
}
6 changes: 6 additions & 0 deletions src/test/providers/ImageLinkProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ services:
b: {
image: 'foo:1234/alpine'
},
c: {
image: 'localhost/alpine'
},
d: {
image: 'nrt.vultrcr.com/wulicoco/code-sync'
},
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/vscode/AlternateYamlLanguageServiceClientFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class AlternateYamlLanguageServiceClientFeature implements StaticFeature,
basicCompletions: redhat || docker,
advancedCompletions: false, // The other extensions do not have advanced completions for Compose docs
hover: redhat || docker, // Compose spec has descriptions
imageLinks: docker, // Docker's extension supports Docker Hub, GHCR, MAR, and Quay.io
imageLinks: false, // Keep Compose image links local so private registries aren't treated as Docker Hub images
serviceStartupCodeLens: false, // The other extensions do not provide any code lens
formatting: false, // The other extensions do support formatting, but we enable it regardless so that an explicitly-chosen formatter always works
};
Expand Down