src/vs/platform/extensions/common/extensions.ts
570 LOC · 530 covered · 40 uncovered · 46 ranges · 3596 concepts · 17 introducers · 2063 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
/*---------------------------------------------------------------------------------------------
extensions.ts ×24
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Severity from '../../../base/common/severity.js';
import * as strings from '../../../base/common/strings.js';
import { URI } from '../../../base/common/uri.js';
import { ILocalizedString } from '../../action/common/action.js';
import { ExtensionKind } from '../../environment/common/environment.js';
import { createDecorator } from '../../instantiation/common/instantiation.js';
import { getRemoteName } from '../../remote/common/remoteHosts.js';
export const USER_MANIFEST_CACHE_FILE = 'extensions.user.cache';
export const BUILTIN_MANIFEST_CACHE_FILE = 'extensions.builtin.cache';
export const UNDEFINED_PUBLISHER = 'undefined_publisher';
export interface ICommand {
command: string;
title: string | ILocalizedString;
category?: string | ILocalizedString;
}
export interface IDebugger {
label?: string;
type: string;
runtime?: string;
}
export interface IGrammar {
language?: string;
}
export interface IJSONValidation {
fileMatch: string | string[];
url: string;
}
export interface IJSONValidationRegistry {
url: string;
}
export interface IKeyBinding {
command: string;
key: string;
when?: string;
mac?: string;
linux?: string;
win?: string;
}
export interface ILanguage {
id: string;
extensions: string[];
aliases: string[];
}
export interface IMenu {
command: string;
alt?: string;
when?: string;
group?: string;
}
export interface ISnippet {
language: string;
}
export interface ITheme {
label: string;
}
export interface IViewContainer {
id: string;
title: string;
}
export interface IView {
id: string;
name: string;
}
export interface IColor {
id: string;
description: string;
defaults: { light: string; dark: string; highContrast: string };
}
interface IWebviewEditor {
readonly viewType: string;
readonly priority: string;
readonly selector: readonly {
readonly filenamePattern?: string;
}[];
}
export interface ICodeActionContributionAction {
readonly kind: string;
readonly title: string;
readonly description?: string;
}
export interface ICodeActionContribution {
readonly languages: readonly string[];
readonly actions: readonly ICodeActionContributionAction[];
}
export interface IAuthenticationContribution {
readonly id: string;
readonly label: string;
readonly authorizationServerGlobs?: string[];
}
export interface IWalkthroughStep {
readonly id: string;
readonly title: string;
readonly description: string | undefined;
readonly media:
| { image: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; svg?: never; video?: never }
| { markdown: string; image?: never; svg?: never; video?: never }
| { svg: string; altText: string; markdown?: never; image?: never; video?: never }
| { video: string | { dark: string; light: string; hc: string }; poster: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; image?: never; svg?: never };
readonly completionEvents?: string[];
/** @deprecated use `completionEvents: 'onCommand:...'` */
readonly doneOn?: { command: string };
readonly when?: string;
}
export interface IWalkthrough {
readonly id: string;
readonly title: string;
readonly icon?: string;
readonly description: string;
readonly steps: IWalkthroughStep[];
readonly featuredFor: string[] | undefined;
readonly when?: string;
}
export interface IStartEntry {
readonly title: string;
readonly description: string;
readonly command: string;
readonly when?: string;
readonly category: 'file' | 'folder' | 'notebook';
}
export interface INotebookEntry {
readonly type: string;
readonly displayName: string;
}
export interface INotebookRendererContribution {
readonly id: string;
readonly displayName: string;
readonly mimeTypes: string[];
}
export interface IDebugVisualizationContribution {
readonly id: string;
readonly when: string;
}
export interface ITranslation {
id: string;
path: string;
}
export interface ILocalizationContribution {
languageId: string;
languageName?: string;
localizedLanguageName?: string;
translations: ITranslation[];
minimalTranslations?: { [key: string]: string };
}
export interface IChatParticipantContribution {
id: string;
name: string;
fullName: string;
description?: string;
isDefault?: boolean;
commands?: { name: string }[];
}
export interface IToolContribution {
name: string;
displayName: string;
modelDescription: string;
userDescription?: string;
}
export interface IToolSetContribution {
name: string;
referenceName: string;
description: string;
icon?: string;
tools: string[];
}
export interface IMcpCollectionContribution {
readonly id: string;
readonly label: string;
readonly when?: string;
}
export interface IChatFileContribution {
readonly path: string;
readonly name?: string;
readonly description?: string;
readonly when?: string;
readonly sessionTypes?: readonly string[];
}
export interface IExtensionContributions {
commands?: ICommand[];
configuration?: any;
configurationDefaults?: any;
debuggers?: IDebugger[];
grammars?: IGrammar[];
jsonValidation?: IJSONValidation[];
jsonValidationRegistry?: IJSONValidationRegistry[];
keybindings?: IKeyBinding[];
languages?: ILanguage[];
menus?: { [context: string]: IMenu[] };
snippets?: ISnippet[];
themes?: ITheme[];
iconThemes?: ITheme[];
productIconThemes?: ITheme[];
viewsContainers?: { [location: string]: IViewContainer[] };
views?: { [location: string]: IView[] };
colors?: IColor[];
localizations?: ILocalizationContribution[];
readonly customEditors?: readonly IWebviewEditor[];
readonly codeActions?: readonly ICodeActionContribution[];
authentication?: IAuthenticationContribution[];
walkthroughs?: IWalkthrough[];
startEntries?: IStartEntry[];
readonly notebooks?: INotebookEntry[];
readonly notebookRenderer?: INotebookRendererContribution[];
readonly debugVisualizers?: IDebugVisualizationContribution[];
readonly chatParticipants?: ReadonlyArray<IChatParticipantContribution>;
readonly chatPromptFiles?: ReadonlyArray<IChatFileContribution>;
readonly chatInstructions?: ReadonlyArray<IChatFileContribution>;
readonly chatAgents?: ReadonlyArray<IChatFileContribution>;
readonly chatSkills?: ReadonlyArray<IChatFileContribution>;
readonly chatPlugins?: ReadonlyArray<IChatFileContribution>;
readonly languageModelTools?: ReadonlyArray<IToolContribution>;
readonly languageModelToolSets?: ReadonlyArray<IToolSetContribution>;
readonly mcpServerDefinitionProviders?: ReadonlyArray<IMcpCollectionContribution>;
}
export interface IExtensionCapabilities {
readonly virtualWorkspaces?: ExtensionVirtualWorkspaceSupport;
readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;
}
export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web'];
export type LimitedWorkspaceSupportType = 'limited';
export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
export type ExtensionUntrustedWorkspaceSupport = { supported: true } | { supported: false; description: string } | { supported: LimitedWorkspaceSupportType; description: string; restrictedConfigurations?: string[] };
export type ExtensionVirtualWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;
export type ExtensionVirtualWorkspaceSupport = boolean | { supported: true } | { supported: false | LimitedWorkspaceSupportType; description: string };
export function getWorkspaceSupportTypeMessage(supportType: ExtensionUntrustedWorkspaceSupport | ExtensionVirtualWorkspaceSupport | undefined): string | undefined {
if (typeof supportType === 'object' && supportType !== null) {
if (supportType.supported !== true) {
return supportType.description;
}
}
return undefined;
}
export interface IExtensionIdentifier {
id: string;
uuid?: string;
}
export const EXTENSION_CATEGORIES = [
'AI',
'Azure',
'Chat',
'Data Science',
'Debuggers',
'Extension Packs',
'Education',
'Formatters',
'Keymaps',
'Language Packs',
'Linters',
'Machine Learning',
'Notebooks',
'Programming Languages',
'SCM Providers',
'Snippets',
'Testing',
'Themes',
'Visualization',
'Other',
];
export interface IRelaxedExtensionManifest {
name: string;
displayName?: string;
publisher: string;
version: string;
engines: { readonly vscode: string };
description?: string;
main?: string;
type?: string;
browser?: string;
preview?: boolean;
// For now this only supports pointing to l10n bundle files
// but it will be used for package.l10n.json files in the future
l10n?: string;
icon?: string;
categories?: string[];
keywords?: string[];
activationEvents?: readonly string[];
extensionDependencies?: string[];
extensionAffinity?: string[];
extensionPack?: string[];
extensionKind?: ExtensionKind | ExtensionKind[];
contributes?: IExtensionContributions;
repository?: { url: string };
bugs?: { url: string };
originalEnabledApiProposals?: readonly string[];
enabledApiProposals?: readonly string[];
api?: string;
scripts?: { [key: string]: string };
capabilities?: IExtensionCapabilities;
}
export type IExtensionManifest = Readonly<IRelaxedExtensionManifest>;
export const enum ExtensionType {
System,
User
}
export const enum TargetPlatform {
WIN32_X64 = 'win32-x64',
WIN32_ARM64 = 'win32-arm64',
LINUX_X64 = 'linux-x64',
LINUX_ARM64 = 'linux-arm64',
LINUX_ARMHF = 'linux-armhf',
ALPINE_X64 = 'alpine-x64',
ALPINE_ARM64 = 'alpine-arm64',
DARWIN_X64 = 'darwin-x64',
DARWIN_ARM64 = 'darwin-arm64',
WEB = 'web',
UNIVERSAL = 'universal',
UNKNOWN = 'unknown',
UNDEFINED = 'undefined',
}
export interface IExtension {
readonly type: ExtensionType;
readonly isBuiltin: boolean;
readonly identifier: IExtensionIdentifier;
readonly manifest: IExtensionManifest;
readonly location: URI;
readonly targetPlatform: TargetPlatform;
readonly publisherDisplayName?: string;
readonly readmeUrl?: URI;
readonly changelogUrl?: URI;
readonly isValid: boolean;
readonly validations: readonly [Severity, string][];
readonly preRelease: boolean;
}
/**
* **!Do not construct directly!**
*
* **!Only static methods because it gets serialized!**
*
* This represents the "canonical" version for an extension identifier. Extension ids
* have to be case-insensitive (due to the marketplace), but we must ensure case
* preservation because the extension API is already public at this time.
*
* For example, given an extension with the publisher `"Hello"` and the name `"World"`,
* its canonical extension identifier is `"Hello.World"`. This extension could be
* referenced in some other extension's dependencies using the string `"hello.world"`.
*
* To make matters more complicated, an extension can optionally have an UUID. When two
* extensions have the same UUID, they are considered equal even if their identifier is different.
*/
export class ExtensionIdentifier {
public readonly value: string;
/**
* Do not use directly. This is public to avoid mangling and thus
* allow compatibility between running from source and a built version.
*/
readonly _lower: string;
constructor(value: string) {
this._lower = value.toLowerCase();
}
public static equals(a: ExtensionIdentifier | string | null | undefined, b: ExtensionIdentifier | string | null | undefined) {
return (typeof b === 'undefined' || b === null);
}
return false;
}
// so we have to use the string comparison which ignores case.
const aValue = (typeof a === 'string' ? a : a.value);
const bValue = (typeof b === 'string' ? b : b.value);
return strings.equalsIgnoreCase(aValue, bValue);
}
// Now we know both arguments are ExtensionIdentifier
return (a._lower === b._lower);
/**
* Gives the value by which to index (for equality).
*/
public static toKey(id: ExtensionIdentifier | string): string {
}
export class ExtensionIdentifierSet {
private readonly _set = new Set<string>();
public get size(): number {
return this._set.size;
}
constructor(iterable?: Iterable<ExtensionIdentifier | string>) {
for (const value of iterable) {
this.add(value);
}
}
}
public add(id: ExtensionIdentifier | string): void {
}
public delete(extensionId: ExtensionIdentifier): boolean {
return this._set.delete(ExtensionIdentifier.toKey(extensionId));
}
public has(id: ExtensionIdentifier | string): boolean {
}
export class ExtensionIdentifierMap<T> {
private readonly _map = new Map<string, T>();
public clear(): void {
this._map.clear();
}
public delete(id: ExtensionIdentifier | string): void {
}
public get(id: ExtensionIdentifier | string): T | undefined {
}
public has(id: ExtensionIdentifier | string): boolean {
}
public set(id: ExtensionIdentifier | string, value: T): void {
}
public values(): IterableIterator<T> {
}
forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void): void {
this._map.forEach(callbackfn);
}
[Symbol.iterator](): IterableIterator<[string, T]> {
}
/**
* An error that is clearly from an extension, identified by the `ExtensionIdentifier`
*/
export class ExtensionError extends Error {
readonly extension: ExtensionIdentifier;
constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) {
const detail = message && cause?.message ? `${message}: ${cause.message}` : (message ?? cause?.message);
super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${detail}`, { cause });
this.name = 'ExtensionError';
this.extension = extensionIdentifier;
// Adopt the underlying cause's stack so error telemetry buckets by the real
// failure site inside the extension instead of the generic event-dispatch
// frames. Extension attribution relies on `this.extension`, not this stack.
if (cause?.stack) {
this.stack = cause.stack;
}
}
export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest {
id?: string;
identifier: ExtensionIdentifier;
uuid?: string;
publisherDisplayName?: string;
targetPlatform: TargetPlatform;
isBuiltin: boolean;
isUserBuiltin: boolean;
isUnderDevelopment: boolean;
extensionLocation: URI;
preRelease: boolean;
}
export type IExtensionDescription = Readonly<IRelaxedExtensionDescription>;
export function isApplicationScopedExtension(manifest: IExtensionManifest): boolean {
return isLanguagePackExtension(manifest);
}
export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
}
export function isAuthenticationProviderExtension(manifest: IExtensionManifest): boolean {
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
}
export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {
if (remoteAuthority) {
const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;
return !!manifest.activationEvents?.includes(activationEvent);
}
return false;
}
export function parseEnabledApiProposalNames(enabledApiProposals: string[]): string[] {
}
export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');
export interface IBuiltinExtensionsScannerService {
readonly _serviceBrand: undefined;
scanBuiltinExtensions(): Promise<IExtension[]>;
}