Path: blob/main/src/vs/platform/extensions/common/extensions.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import Severity from '../../../base/common/severity.js';6import * as strings from '../../../base/common/strings.js';7import { URI } from '../../../base/common/uri.js';8import { ILocalizedString } from '../../action/common/action.js';9import { ExtensionKind } from '../../environment/common/environment.js';10import { createDecorator } from '../../instantiation/common/instantiation.js';11import { getRemoteName } from '../../remote/common/remoteHosts.js';1213export const USER_MANIFEST_CACHE_FILE = 'extensions.user.cache';14export const BUILTIN_MANIFEST_CACHE_FILE = 'extensions.builtin.cache';15export const UNDEFINED_PUBLISHER = 'undefined_publisher';1617export interface ICommand {18command: string;19title: string | ILocalizedString;20category?: string | ILocalizedString;21}2223export interface IDebugger {24label?: string;25type: string;26runtime?: string;27}2829export interface IGrammar {30language?: string;31}3233export interface IJSONValidation {34fileMatch: string | string[];35url: string;36}3738export interface IKeyBinding {39command: string;40key: string;41when?: string;42mac?: string;43linux?: string;44win?: string;45}4647export interface ILanguage {48id: string;49extensions: string[];50aliases: string[];51}5253export interface IMenu {54command: string;55alt?: string;56when?: string;57group?: string;58}5960export interface ISnippet {61language: string;62}6364export interface ITheme {65label: string;66}6768export interface IViewContainer {69id: string;70title: string;71}7273export interface IView {74id: string;75name: string;76}7778export interface IColor {79id: string;80description: string;81defaults: { light: string; dark: string; highContrast: string };82}8384interface IWebviewEditor {85readonly viewType: string;86readonly priority: string;87readonly selector: readonly {88readonly filenamePattern?: string;89}[];90}9192export interface ICodeActionContributionAction {93readonly kind: string;94readonly title: string;95readonly description?: string;96}9798export interface ICodeActionContribution {99readonly languages: readonly string[];100readonly actions: readonly ICodeActionContributionAction[];101}102103export interface IAuthenticationContribution {104readonly id: string;105readonly label: string;106readonly authorizationServerGlobs?: string[];107}108109export interface IWalkthroughStep {110readonly id: string;111readonly title: string;112readonly description: string | undefined;113readonly media:114| { image: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; svg?: never; video?: never }115| { markdown: string; image?: never; svg?: never; video?: never }116| { svg: string; altText: string; markdown?: never; image?: never; video?: never }117| { video: string | { dark: string; light: string; hc: string }; poster: string | { dark: string; light: string; hc: string }; altText: string; markdown?: never; image?: never; svg?: never };118readonly completionEvents?: string[];119/** @deprecated use `completionEvents: 'onCommand:...'` */120readonly doneOn?: { command: string };121readonly when?: string;122}123124export interface IWalkthrough {125readonly id: string;126readonly title: string;127readonly icon?: string;128readonly description: string;129readonly steps: IWalkthroughStep[];130readonly featuredFor: string[] | undefined;131readonly when?: string;132}133134export interface IStartEntry {135readonly title: string;136readonly description: string;137readonly command: string;138readonly when?: string;139readonly category: 'file' | 'folder' | 'notebook';140}141142export interface INotebookEntry {143readonly type: string;144readonly displayName: string;145}146147export interface INotebookRendererContribution {148readonly id: string;149readonly displayName: string;150readonly mimeTypes: string[];151}152153export interface IDebugVisualizationContribution {154readonly id: string;155readonly when: string;156}157158export interface ITranslation {159id: string;160path: string;161}162163export interface ILocalizationContribution {164languageId: string;165languageName?: string;166localizedLanguageName?: string;167translations: ITranslation[];168minimalTranslations?: { [key: string]: string };169}170171export interface IChatParticipantContribution {172id: string;173name: string;174fullName: string;175description?: string;176isDefault?: boolean;177commands?: { name: string }[];178}179180export interface IToolContribution {181name: string;182displayName: string;183modelDescription: string;184userDescription?: string;185}186187export interface IToolSetContribution {188name: string;189referenceName: string;190description: string;191icon?: string;192tools: string[];193}194195export interface IMcpCollectionContribution {196readonly id: string;197readonly label: string;198}199200export interface IExtensionContributions {201commands?: ICommand[];202configuration?: any;203debuggers?: IDebugger[];204grammars?: IGrammar[];205jsonValidation?: IJSONValidation[];206keybindings?: IKeyBinding[];207languages?: ILanguage[];208menus?: { [context: string]: IMenu[] };209snippets?: ISnippet[];210themes?: ITheme[];211iconThemes?: ITheme[];212productIconThemes?: ITheme[];213viewsContainers?: { [location: string]: IViewContainer[] };214views?: { [location: string]: IView[] };215colors?: IColor[];216localizations?: ILocalizationContribution[];217readonly customEditors?: readonly IWebviewEditor[];218readonly codeActions?: readonly ICodeActionContribution[];219authentication?: IAuthenticationContribution[];220walkthroughs?: IWalkthrough[];221startEntries?: IStartEntry[];222readonly notebooks?: INotebookEntry[];223readonly notebookRenderer?: INotebookRendererContribution[];224readonly debugVisualizers?: IDebugVisualizationContribution[];225readonly chatParticipants?: ReadonlyArray<IChatParticipantContribution>;226readonly languageModelTools?: ReadonlyArray<IToolContribution>;227readonly languageModelToolSets?: ReadonlyArray<IToolSetContribution>;228readonly mcpServerDefinitionProviders?: ReadonlyArray<IMcpCollectionContribution>;229}230231export interface IExtensionCapabilities {232readonly virtualWorkspaces?: ExtensionVirtualWorkspaceSupport;233readonly untrustedWorkspaces?: ExtensionUntrustedWorkspaceSupport;234}235236237export const ALL_EXTENSION_KINDS: readonly ExtensionKind[] = ['ui', 'workspace', 'web'];238239export type LimitedWorkspaceSupportType = 'limited';240export type ExtensionUntrustedWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;241export type ExtensionUntrustedWorkspaceSupport = { supported: true } | { supported: false; description: string } | { supported: LimitedWorkspaceSupportType; description: string; restrictedConfigurations?: string[] };242243export type ExtensionVirtualWorkspaceSupportType = boolean | LimitedWorkspaceSupportType;244export type ExtensionVirtualWorkspaceSupport = boolean | { supported: true } | { supported: false | LimitedWorkspaceSupportType; description: string };245246export function getWorkspaceSupportTypeMessage(supportType: ExtensionUntrustedWorkspaceSupport | ExtensionVirtualWorkspaceSupport | undefined): string | undefined {247if (typeof supportType === 'object' && supportType !== null) {248if (supportType.supported !== true) {249return supportType.description;250}251}252return undefined;253}254255256export interface IExtensionIdentifier {257id: string;258uuid?: string;259}260261export const EXTENSION_CATEGORIES = [262'AI',263'Azure',264'Chat',265'Data Science',266'Debuggers',267'Extension Packs',268'Education',269'Formatters',270'Keymaps',271'Language Packs',272'Linters',273'Machine Learning',274'Notebooks',275'Programming Languages',276'SCM Providers',277'Snippets',278'Testing',279'Themes',280'Visualization',281'Other',282];283284export interface IRelaxedExtensionManifest {285name: string;286displayName?: string;287publisher: string;288version: string;289engines: { readonly vscode: string };290description?: string;291main?: string;292type?: string;293browser?: string;294preview?: boolean;295// For now this only supports pointing to l10n bundle files296// but it will be used for package.l10n.json files in the future297l10n?: string;298icon?: string;299categories?: string[];300keywords?: string[];301activationEvents?: string[];302extensionDependencies?: string[];303extensionPack?: string[];304extensionKind?: ExtensionKind | ExtensionKind[];305contributes?: IExtensionContributions;306repository?: { url: string };307bugs?: { url: string };308originalEnabledApiProposals?: readonly string[];309enabledApiProposals?: readonly string[];310api?: string;311scripts?: { [key: string]: string };312capabilities?: IExtensionCapabilities;313}314315export type IExtensionManifest = Readonly<IRelaxedExtensionManifest>;316317export const enum ExtensionType {318System,319User320}321322export const enum TargetPlatform {323WIN32_X64 = 'win32-x64',324WIN32_ARM64 = 'win32-arm64',325326LINUX_X64 = 'linux-x64',327LINUX_ARM64 = 'linux-arm64',328LINUX_ARMHF = 'linux-armhf',329330ALPINE_X64 = 'alpine-x64',331ALPINE_ARM64 = 'alpine-arm64',332333DARWIN_X64 = 'darwin-x64',334DARWIN_ARM64 = 'darwin-arm64',335336WEB = 'web',337338UNIVERSAL = 'universal',339UNKNOWN = 'unknown',340UNDEFINED = 'undefined',341}342343export interface IExtension {344readonly type: ExtensionType;345readonly isBuiltin: boolean;346readonly identifier: IExtensionIdentifier;347readonly manifest: IExtensionManifest;348readonly location: URI;349readonly targetPlatform: TargetPlatform;350readonly publisherDisplayName?: string;351readonly readmeUrl?: URI;352readonly changelogUrl?: URI;353readonly isValid: boolean;354readonly validations: readonly [Severity, string][];355readonly preRelease: boolean;356}357358/**359* **!Do not construct directly!**360*361* **!Only static methods because it gets serialized!**362*363* This represents the "canonical" version for an extension identifier. Extension ids364* have to be case-insensitive (due to the marketplace), but we must ensure case365* preservation because the extension API is already public at this time.366*367* For example, given an extension with the publisher `"Hello"` and the name `"World"`,368* its canonical extension identifier is `"Hello.World"`. This extension could be369* referenced in some other extension's dependencies using the string `"hello.world"`.370*371* To make matters more complicated, an extension can optionally have an UUID. When two372* extensions have the same UUID, they are considered equal even if their identifier is different.373*/374export class ExtensionIdentifier {375public readonly value: string;376377/**378* Do not use directly. This is public to avoid mangling and thus379* allow compatibility between running from source and a built version.380*/381readonly _lower: string;382383constructor(value: string) {384this.value = value;385this._lower = value.toLowerCase();386}387388public static equals(a: ExtensionIdentifier | string | null | undefined, b: ExtensionIdentifier | string | null | undefined) {389if (typeof a === 'undefined' || a === null) {390return (typeof b === 'undefined' || b === null);391}392if (typeof b === 'undefined' || b === null) {393return false;394}395if (typeof a === 'string' || typeof b === 'string') {396// At least one of the arguments is an extension id in string form,397// so we have to use the string comparison which ignores case.398const aValue = (typeof a === 'string' ? a : a.value);399const bValue = (typeof b === 'string' ? b : b.value);400return strings.equalsIgnoreCase(aValue, bValue);401}402403// Now we know both arguments are ExtensionIdentifier404return (a._lower === b._lower);405}406407/**408* Gives the value by which to index (for equality).409*/410public static toKey(id: ExtensionIdentifier | string): string {411if (typeof id === 'string') {412return id.toLowerCase();413}414return id._lower;415}416}417418export class ExtensionIdentifierSet {419420private readonly _set = new Set<string>();421422public get size(): number {423return this._set.size;424}425426constructor(iterable?: Iterable<ExtensionIdentifier | string>) {427if (iterable) {428for (const value of iterable) {429this.add(value);430}431}432}433434public add(id: ExtensionIdentifier | string): void {435this._set.add(ExtensionIdentifier.toKey(id));436}437438public delete(extensionId: ExtensionIdentifier): boolean {439return this._set.delete(ExtensionIdentifier.toKey(extensionId));440}441442public has(id: ExtensionIdentifier | string): boolean {443return this._set.has(ExtensionIdentifier.toKey(id));444}445}446447export class ExtensionIdentifierMap<T> {448449private readonly _map = new Map<string, T>();450451public clear(): void {452this._map.clear();453}454455public delete(id: ExtensionIdentifier | string): void {456this._map.delete(ExtensionIdentifier.toKey(id));457}458459public get(id: ExtensionIdentifier | string): T | undefined {460return this._map.get(ExtensionIdentifier.toKey(id));461}462463public has(id: ExtensionIdentifier | string): boolean {464return this._map.has(ExtensionIdentifier.toKey(id));465}466467public set(id: ExtensionIdentifier | string, value: T): void {468this._map.set(ExtensionIdentifier.toKey(id), value);469}470471public values(): IterableIterator<T> {472return this._map.values();473}474475forEach(callbackfn: (value: T, key: string, map: Map<string, T>) => void): void {476this._map.forEach(callbackfn);477}478479[Symbol.iterator](): IterableIterator<[string, T]> {480return this._map[Symbol.iterator]();481}482}483484/**485* An error that is clearly from an extension, identified by the `ExtensionIdentifier`486*/487export class ExtensionError extends Error {488489readonly extension: ExtensionIdentifier;490491constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) {492super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${message ?? cause.message}`, { cause });493this.name = 'ExtensionError';494this.extension = extensionIdentifier;495}496}497498export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest {499id?: string;500identifier: ExtensionIdentifier;501uuid?: string;502publisherDisplayName?: string;503targetPlatform: TargetPlatform;504isBuiltin: boolean;505isUserBuiltin: boolean;506isUnderDevelopment: boolean;507extensionLocation: URI;508preRelease: boolean;509}510511export type IExtensionDescription = Readonly<IRelaxedExtensionDescription>;512513export function isApplicationScopedExtension(manifest: IExtensionManifest): boolean {514return isLanguagePackExtension(manifest);515}516517export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {518return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;519}520521export function isAuthenticationProviderExtension(manifest: IExtensionManifest): boolean {522return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;523}524525export function isResolverExtension(manifest: IExtensionManifest, remoteAuthority: string | undefined): boolean {526if (remoteAuthority) {527const activationEvent = `onResolveRemoteAuthority:${getRemoteName(remoteAuthority)}`;528return !!manifest.activationEvents?.includes(activationEvent);529}530return false;531}532533export function parseApiProposals(enabledApiProposals: string[]): { proposalName: string; version?: number }[] {534return enabledApiProposals.map(proposal => {535const [proposalName, version] = proposal.split('@');536return { proposalName, version: version ? parseInt(version) : undefined };537});538}539540export function parseEnabledApiProposalNames(enabledApiProposals: string[]): string[] {541return enabledApiProposals.map(proposal => proposal.split('@')[0]);542}543544export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');545export interface IBuiltinExtensionsScannerService {546readonly _serviceBrand: undefined;547scanBuiltinExtensions(): Promise<IExtension[]>;548}549550551