Skip to content

Commit

Permalink
feat!: expose /extensions which allows to register a new extension
Browse files Browse the repository at this point in the history
and remove old api to set default extension files
  • Loading branch information
Loïc Mangeonjean committed Mar 22, 2023
1 parent c8656e2 commit c403db0
Show file tree
Hide file tree
Showing 21 changed files with 976 additions and 999 deletions.
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"types": "./dist/services.d.ts",
"default": "./dist/services.js"
},
"./extensions": {
"types": "./dist/extensions.d.ts",
"default": "./dist/extensions.js"
},
"./service-override/notifications": {
"types": "./dist/notifications.d.ts",
"default": "./dist/notifications.js"
Expand Down Expand Up @@ -86,6 +90,9 @@
"services": [
"./dist/services.d.ts"
],
"extensions": [
"./dist/extensions.d.ts"
],
"service-override/notifications": [
"./dist/notifications.d.ts"
],
Expand Down
1 change: 1 addition & 0 deletions rollup/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function getMemberExpressionPath (node: recast.types.namedTypes.MemberExpression

const input = {
api: './src/api.ts',
extensions: './src/extensions.ts',
services: './src/services.ts',
notifications: './src/service-override/notifications.ts',
dialogs: './src/service-override/dialogs.ts',
Expand Down
1 change: 1 addition & 0 deletions rollup/rollup.types.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interfaceOverride.set('IStandaloneDiffEditorConstructionOptions', 'monaco.editor

export default rollup.defineConfig([
'./dist/types/src/services.d.ts',
'./dist/types/src/extensions.d.ts',
'./dist/types/src/service-override/notifications.d.ts',
'./dist/types/src/service-override/dialogs.d.ts',
'./dist/types/src/service-override/modelEditor.d.ts',
Expand Down
43 changes: 33 additions & 10 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,38 @@ import * as editorOptions from 'vs/editor/common/config/editorOptions'
import * as uri from 'vs/base/common/uri'
import * as log from 'vs/platform/log/common/log'
import * as telemetryUtils from 'vs/platform/telemetry/common/telemetryUtils'
import customLanguages from './vscode-services/languages'
import customCommands from './vscode-services/commands'
import customWorkspace from './vscode-services/workspace'
import customWindow, { TextTabInput } from './vscode-services/window'
import customEnv from './vscode-services/env'
import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions'
import { URI } from 'vs/base/common/uri'
import createLanguagesApi from './vscode-services/languages'
import createCommandsApi from './vscode-services/commands'
import createWorkspaceApi from './vscode-services/workspace'
import createWindowApi, { TextTabInput } from './vscode-services/window'
import createEnvApi from './vscode-services/env'
import { Services } from './services'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const unsupported = <any>undefined

export const DEFAULT_EXTENSION: IExtensionDescription = {
identifier: new ExtensionIdentifier('monaco'),
isBuiltin: true,
isUserBuiltin: true,
isUnderDevelopment: false,
extensionLocation: URI.from({ scheme: 'extension', path: '/' }),
name: 'monaco',
publisher: 'microsoft',
version: '1.0.0',
engines: {
vscode: VSCODE_VERSION
},
targetPlatform: TargetPlatform.WEB
}

function getDefaultExtension () {
return Services.get().extension ?? DEFAULT_EXTENSION
}

const _workspace = createWorkspaceApi(getDefaultExtension)
const api: typeof vscode = {
version: VSCODE_VERSION,

Expand All @@ -34,11 +57,11 @@ const api: typeof vscode = {
authentication: unsupported,
tests: unsupported,

env: customEnv,
commands: customCommands,
window: customWindow,
workspace: customWorkspace,
languages: customLanguages,
env: createEnvApi(getDefaultExtension),
commands: createCommandsApi(getDefaultExtension),
window: createWindowApi(getDefaultExtension, _workspace),
workspace: _workspace,
languages: createLanguagesApi(getDefaultExtension),

Breakpoint: extHostTypes.Breakpoint,
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
Expand Down
93 changes: 93 additions & 0 deletions src/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type * as vscode from 'vscode'
import { ExtensionType, IExtension, IExtensionContributions, IExtensionDescription, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'
import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'
import { IMessage, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'
import { Disposable } from 'vs/workbench/api/common/extHostTypes'
import { generateUuid } from 'vs/base/common/uuid'
import { URI } from 'vs/base/common/uri'
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'
import { StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices'
import { getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'
import * as api from './api'
import { consoleExtensionMessageHandler } from './service-override/tools'
import { registerExtensionFile } from './service-override/files'
import createLanguagesApi from './vscode-services/languages'
import createCommandsApi from './vscode-services/commands'
import createWorkspaceApi from './vscode-services/workspace'
import createWindowApi from './vscode-services/window'
import createEnvApi from './vscode-services/env'

export function createApi (extension: IExtensionDescription): typeof vscode {
const workspace = createWorkspaceApi(() => extension)
return {
...api,
env: createEnvApi(() => extension),
commands: createCommandsApi(() => extension),
window: createWindowApi(() => extension, workspace),
workspace: createWorkspaceApi(() => extension),
languages: createLanguagesApi(() => extension)
}
}

const hasOwnProperty = Object.hasOwnProperty
function handleExtensionPoint<T extends IExtensionContributions[keyof IExtensionContributions]> (extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
const users: IExtensionPointUser<T>[] = []
for (const desc of availableExtensions) {
if ((desc.contributes != null) && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {
users.push({
description: desc,
value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T,
collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)
})
}
}
extensionPoint.acceptUsers(users)
}

interface RegisterExtensionResult {
api: typeof vscode
registerFile: (path: string, getContent: () => Promise<string>) => Disposable
}
export function registerExtension (manifest: IExtensionManifest): RegisterExtensionResult {
const uuid = generateUuid()
const location = URI.from({ scheme: 'extension', path: `/${uuid}` })

const extension: IExtension = {
manifest,
type: ExtensionType.User,
isBuiltin: false,
identifier: {
id: getExtensionId(manifest.publisher, manifest.name),
uuid
},
location,
targetPlatform: TargetPlatform.WEB,
isValid: true,
validations: []
}
const extensionDescription = toExtensionDescription(extension)

void StandaloneServices.get(IExtHostExtensionService).getExtensionRegistry().then(extensionRegistry => {
extensionRegistry.deltaExtensions([extensionDescription], [])

const availableExtensions = extensionRegistry.getAllExtensionDescriptions()

const extensionPoints = ExtensionsRegistry.getExtensionPoints()
for (const extensionPoint of extensionPoints) {
if (Object.hasOwnProperty.call(extensionDescription.contributes, extensionPoint.name)) {
handleExtensionPoint(extensionPoint, availableExtensions, consoleExtensionMessageHandler)
}
}
})

return {
api: createApi(extensionDescription),
registerFile: (path: string, getContent: () => Promise<string>) => {
return registerExtensionFile(location, path, getContent)
}
}
}

export {
IExtensionManifest
}
15 changes: 7 additions & 8 deletions src/service-override/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FileService } from 'vs/platform/files/common/fileService'
import { ILogService } from 'vs/platform/log/common/log'
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'
import { AbstractExtensionResourceLoaderService, IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLoader/common/extensionResourceLoader'
import { Event, URI } from 'vs/workbench/workbench.web.main'
import { URI } from 'vs/base/common/uri'
import { FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode, FileType, IFileSystemProviderWithFileReadWriteCapability, IStat } from 'vs/platform/files/common/files'
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'
import { IStorageService } from 'vs/platform/storage/common/storage'
Expand All @@ -15,9 +15,10 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IProductService } from 'vs/platform/product/common/productService'
import { Disposable } from 'vs/workbench/api/common/extHostTypes'
import { IDisposable } from 'vs/base/common/lifecycle'
import { DEFAULT_EXTENSION } from '../vscode-services/extHost'
import { joinPath } from 'vs/base/common/resources'
import { Event } from 'vs/base/common/event'
import { unsupported } from '../tools'
import { IFileService, Services } from '../services'
import { IFileService } from '../services'

class SimpleExtensionResourceLoaderService extends AbstractExtensionResourceLoaderService {
// required for injection
Expand Down Expand Up @@ -107,14 +108,12 @@ class MemoryFileService extends FileService {
super(logService)

this.registerProvider('user', new InMemoryFileSystemProvider())

const extension = Services.get().extension ?? DEFAULT_EXTENSION
this.registerProvider(extension.extensionLocation.scheme, extensionFileSystemProvider)
this.registerProvider('extension', extensionFileSystemProvider)
}
}

export function registerExtensionFile (resource: URI, getContent: () => Promise<string>): Disposable {
return extensionFileSystemProvider.registerFile(resource, getContent)
export function registerExtensionFile (extensionLocation: URI, path: string, getContent: () => Promise<string>): Disposable {
return extensionFileSystemProvider.registerFile(joinPath(extensionLocation, path), getContent)
}

export default function getServiceOverride (): IEditorOverrideServices {
Expand Down
36 changes: 1 addition & 35 deletions src/service-override/keybindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/browser/keybindingService'
import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'
import { VSBuffer } from 'vs/base/common/buffer'
import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { } from 'vs/workbench/services/actions/common/menusExtensionPoint'
import { ILocalizedString } from 'vs/platform/action/common/action'
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'
import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'
Expand All @@ -23,9 +20,7 @@ import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/b
import { localize } from 'vs/nls'
import getFileServiceOverride from './files'
import getConfigurationServiceOverride, { configurationRegistry } from './configuration'
import { consoleExtensionMessageHandler, getExtensionPoint } from './tools'
import { DEFAULT_EXTENSION } from '../vscode-services/extHost'
import { IFileService, Services } from '../services'
import { IFileService } from '../services'
import { createInjectedClass } from '../tools/injection'

// This class use useful so editor.addAction and editor.addCommand still work
Expand Down Expand Up @@ -114,33 +109,6 @@ interface ContributedKeyBinding {
linux?: string
win?: string
}
interface IUserFriendlyCommand {
command: string
title: string | ILocalizedString
shortTitle?: string | ILocalizedString
enablement?: string
category?: string | ILocalizedString
icon?: string | { light: string, dark: string }
}

const keybindingsExtensionPoint = getExtensionPoint<ContributedKeyBinding | ContributedKeyBinding[]>('keybindings')
const commandsExtensionPoint = getExtensionPoint<IUserFriendlyCommand | IUserFriendlyCommand[]>('commands')

function setKeybindings (grammars: ContributedKeyBinding | ContributedKeyBinding[], extension: IExtensionDescription = Services.get().extension ?? DEFAULT_EXTENSION): void {
keybindingsExtensionPoint.acceptUsers([{
description: extension,
value: grammars,
collector: new ExtensionMessageCollector(consoleExtensionMessageHandler, extension, keybindingsExtensionPoint.name)
}])
}

function setCommands (keybindings: IUserFriendlyCommand | IUserFriendlyCommand[], extension: IExtensionDescription = Services.get().extension ?? DEFAULT_EXTENSION): void {
commandsExtensionPoint.acceptUsers([{
description: extension,
value: keybindings,
collector: new ExtensionMessageCollector(consoleExtensionMessageHandler, extension, keybindingsExtensionPoint.name)
}])
}

async function updateUserKeybindings (keybindingsJson: string): Promise<void> {
const userDataProfilesService: IUserDataProfilesService = StandaloneServices.get(IUserDataProfilesService)
Expand All @@ -158,8 +126,6 @@ export default function getServiceOverride (): IEditorOverrideServices {

export {
updateUserKeybindings,
setKeybindings,
setCommands,
ContributedKeyBinding,
IUserFriendlyKeybinding
}
15 changes: 2 additions & 13 deletions src/service-override/languageConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,9 @@ import '../polyfill'
import '../vscode-services/missing-services'
import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices'
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint'
import { joinPath } from 'vs/base/common/resources'
import getFileServiceOverride, { registerExtensionFile } from './files'
import getFileServiceOverride from './files'
import { onServicesInitialized } from './tools'
import { IInstantiationService, Services } from '../services'
import { DEFAULT_EXTENSION } from '../vscode-services/extHost'

function setLanguageConfiguration (path: string, getConfiguration: () => Promise<string>): void {
const extension = Services.get().extension ?? DEFAULT_EXTENSION
registerExtensionFile(joinPath(extension.extensionLocation, path), getConfiguration)
}
import { IInstantiationService } from '../services'

function initialize (instantiationService: IInstantiationService) {
instantiationService.createInstance(LanguageConfigurationFileHandler)
Expand All @@ -23,7 +16,3 @@ export default function getServiceOverride (): IEditorOverrideServices {
...getFileServiceOverride()
}
}

export {
setLanguageConfiguration
}
17 changes: 1 addition & 16 deletions src/service-override/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,8 @@ import '../polyfill'
import '../vscode-services/missing-services'
import { IEditorOverrideServices } from 'vs/editor/standalone/browser/standaloneServices'
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'
import { IRawLanguageExtensionPoint, WorkbenchLanguageService } from 'vs/workbench/services/language/common/languageService'
import { consoleExtensionMessageHandler, getExtensionPoint } from './tools'
import { DEFAULT_EXTENSION } from '../vscode-services/extHost'
import { ILanguageService, Services } from '../services'

const languageExtensionPoint = getExtensionPoint<Partial<IRawLanguageExtensionPoint>[]>('languages')

function setLanguages (language: Partial<IRawLanguageExtensionPoint>[], extension: IExtensionDescription = Services.get().extension ?? DEFAULT_EXTENSION): void {
languageExtensionPoint.acceptUsers([{
description: extension,
value: language,
collector: new ExtensionMessageCollector(consoleExtensionMessageHandler, extension, languageExtensionPoint.name)
}])
}
import { ILanguageService } from '../services'

export default function getServiceOverride (): IEditorOverrideServices {
return {
Expand All @@ -26,6 +12,5 @@ export default function getServiceOverride (): IEditorOverrideServices {
}

export {
setLanguages,
IRawLanguageExtensionPoint
}
Loading

0 comments on commit c403db0

Please sign in to comment.