Split up the editor's worker and the editor worker helper scripts, remove AMD support

This commit is contained in:
Alex Dima 2025-03-20 18:37:55 +01:00
parent 91f0cb7b90
commit 46035d0296
No known key found for this signature in database
GPG Key ID: 4FA498B1FFF19E4D
23 changed files with 153 additions and 340 deletions

View File

@ -44,7 +44,7 @@ export = new class implements eslint.Rule.RuleModule {
readonly meta: eslint.Rule.RuleMetaData = {
messages: {
badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization',
badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/.eslintrc.json',
badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/eslint.config.js',
badAbsolute: 'Imports have to be relative to support ESM',
badExtension: 'Imports have to end with `.js` or `.css` to support ESM',
},

View File

@ -24,7 +24,7 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.worker.start/.test(fileName)
) {
return createImportRuleListener((node, path) => {
// resolve relative paths

View File

@ -38,7 +38,7 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker.start/.test(path)
) {
context.report({
loc: node.loc,

View File

@ -42,7 +42,8 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => {
sourcesRoot: path.join(root, 'src'),
entryPoints: [
'vs/editor/editor.main',
'vs/editor/editor.worker',
'vs/editor/editor.worker.start',
'vs/editor/common/services/editorSimpleWorkerMain',
],
inlineEntryPoints: [
apiusages,

View File

@ -92,7 +92,7 @@ declare namespace monaco.editor {
#includeAll(vs/editor/standalone/browser/standaloneEditor;languages.Token=>Token):
#include(vs/editor/standalone/common/standaloneTheme): BuiltinTheme, IStandaloneThemeData, IColors
#include(vs/editor/common/languages/supports/tokenization): ITokenThemeRule
#include(vs/editor/standalone/browser/standaloneWebWorker): MonacoWebWorker, IWebWorkerOptions
#include(vs/editor/standalone/browser/standaloneWebWorker): MonacoWebWorker, IInternalWebWorkerOptions
#include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IStandaloneDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor
export interface ICommandHandler {
(...args: any[]): void;

View File

@ -4,8 +4,7 @@
import { IObservable } from './vs/base/common/observable';
import { ServiceIdentifier } from './vs/platform/instantiation/common/instantiation';
import { create as create1 } from './vs/base/common/worker/simpleWorker';
import { create as create2 } from './vs/editor/common/services/editorSimpleWorker';
import { start } from './vs/editor/editor.worker.start';
import { SyncDescriptor0 } from './vs/platform/instantiation/common/descriptors';
import * as editorAPI from './vs/editor/editor.api';
@ -13,8 +12,7 @@ import * as editorAPI from './vs/editor/editor.api';
var a: any;
var b: any;
a = (<ServiceIdentifier<any>>b).type;
a = create1;
a = create2;
a = start;
// injection madness
a = (<SyncDescriptor0<any>>b).ctor;

View File

@ -959,7 +959,7 @@ export default tseslint.config(
]
},
{
'target': 'src/vs/editor/editor.worker.ts',
'target': 'src/vs/editor/editor.worker.start.ts',
'layer': 'worker',
'restrictions': [
'vs/base/~',

View File

@ -116,20 +116,22 @@ function isPromiseLike<T>(obj: any): obj is PromiseLike<T> {
class WebWorker extends Disposable implements IWorker {
private readonly id: number;
private readonly label: string;
private worker: Promise<Worker> | null;
constructor(esmWorkerLocation: URI | undefined, moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) {
constructor(descriptorOrWorker: IWorkerDescriptor | Worker, id: number, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) {
super();
this.id = id;
this.label = label;
const workerOrPromise = getWorker(esmWorkerLocation, label);
const workerOrPromise = (
descriptorOrWorker instanceof Worker
? descriptorOrWorker
: getWorker(descriptorOrWorker.esmModuleLocation, descriptorOrWorker.label || 'anonymous' + id)
);
if (isPromiseLike(workerOrPromise)) {
this.worker = workerOrPromise;
} else {
this.worker = Promise.resolve(workerOrPromise);
}
this.postMessage(moduleId, []);
this.postMessage(descriptorOrWorker instanceof Worker ? '-please-ignore-' : descriptorOrWorker.moduleId, []);
this.worker.then((w) => {
w.onmessage = function (ev) {
onMessageCallback(ev.data);
@ -160,7 +162,7 @@ class WebWorker extends Disposable implements IWorker {
w.postMessage(message, transfer);
} catch (err) {
onUnexpectedError(err);
onUnexpectedError(new Error(`FAILED to post message to '${this.label}'-worker`, { cause: err }));
onUnexpectedError(new Error(`FAILED to post message to worker`, { cause: err }));
}
});
}
@ -187,14 +189,14 @@ class DefaultWorkerFactory implements IWorkerFactory {
this._webWorkerFailedBeforeError = false;
}
public create(desc: IWorkerDescriptor, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker {
public create(descOrWorker: IWorkerDescriptor | Worker, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker {
const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
if (this._webWorkerFailedBeforeError) {
throw this._webWorkerFailedBeforeError;
}
return new WebWorker(desc.esmModuleLocation, desc.moduleId, workerId, desc.label || 'anonymous' + workerId, onMessageCallback, (err) => {
return new WebWorker(descOrWorker, workerId, onMessageCallback, (err) => {
logOnceWebWorkerWarning(err);
this._webWorkerFailedBeforeError = err;
onErrorCallback(err);
@ -203,8 +205,8 @@ class DefaultWorkerFactory implements IWorkerFactory {
}
export function createWebWorker<T extends object>(moduleId: string, label: string | undefined): IWorkerClient<T>;
export function createWebWorker<T extends object>(workerDescriptor: IWorkerDescriptor): IWorkerClient<T>;
export function createWebWorker<T extends object>(arg0: string | IWorkerDescriptor, arg1?: string | undefined): IWorkerClient<T> {
const workerDescriptor = (typeof arg0 === 'string' ? new WorkerDescriptor(arg0, arg1) : arg0);
return new SimpleWorkerClient<T>(new DefaultWorkerFactory(), workerDescriptor);
export function createWebWorker<T extends object>(workerDescriptor: IWorkerDescriptor | Worker): IWorkerClient<T>;
export function createWebWorker<T extends object>(arg0: string | IWorkerDescriptor | Worker, arg1?: string | undefined): IWorkerClient<T> {
const workerDescriptorOrWorker = (typeof arg0 === 'string' ? new WorkerDescriptor(arg0, arg1) : arg0);
return new SimpleWorkerClient<T>(new DefaultWorkerFactory(), workerDescriptorOrWorker);
}

View File

@ -231,41 +231,6 @@ export function filter(obj: obj, predicate: (key: string, value: any) => boolean
return result;
}
export function getAllPropertyNames(obj: object): string[] {
let res: string[] = [];
while (Object.prototype !== obj) {
res = res.concat(Object.getOwnPropertyNames(obj));
obj = Object.getPrototypeOf(obj);
}
return res;
}
export function getAllMethodNames(obj: object): string[] {
const methods: string[] = [];
for (const prop of getAllPropertyNames(obj)) {
if (typeof (obj as any)[prop] === 'function') {
methods.push(prop);
}
}
return methods;
}
export function createProxyObject<T extends object>(methodNames: string[], invoke: (method: string, args: unknown[]) => unknown): T {
const createProxyMethod = (method: string): () => unknown => {
return function () {
const args = Array.prototype.slice.call(arguments, 0);
return invoke(method, args);
};
};
// eslint-disable-next-line local/code-no-dangerous-type-assertions
const result = {} as T;
for (const methodName of methodNames) {
(<any>result)[methodName] = createProxyMethod(methodName);
}
return result;
}
export function mapValues<T extends {}, R>(obj: T, fn: (value: T[keyof T], key: string) => R): { [K in keyof T]: R } {
const result: { [key: string]: R } = {};
for (const [key, value] of Object.entries(obj)) {

View File

@ -7,7 +7,6 @@ import { CharCode } from '../charCode.js';
import { onUnexpectedError, transformErrorForSerialization } from '../errors.js';
import { Emitter, Event } from '../event.js';
import { Disposable, IDisposable } from '../lifecycle.js';
import { AppResourcePath, FileAccess } from '../network.js';
import { isWeb } from '../platform.js';
import * as strings from '../strings.js';
import { URI } from '../uri.js';
@ -25,7 +24,7 @@ export interface IWorkerCallback {
}
export interface IWorkerFactory {
create(modules: IWorkerDescriptor, callback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker;
create(modules: IWorkerDescriptor | Worker, callback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker;
}
export interface IWorkerDescriptor {
@ -326,15 +325,15 @@ export class SimpleWorkerClient<W extends object> extends Disposable implements
constructor(
workerFactory: IWorkerFactory,
workerDescriptor: IWorkerDescriptor,
workerDescriptorOrWorker: IWorkerDescriptor | Worker,
) {
super();
this._worker = this._register(workerFactory.create(
{
workerDescriptorOrWorker instanceof Worker ? workerDescriptorOrWorker : {
moduleId: 'vs/base/common/worker/simpleWorker',
esmModuleLocation: workerDescriptor.esmModuleLocation,
label: workerDescriptor.label
esmModuleLocation: workerDescriptorOrWorker.esmModuleLocation,
label: workerDescriptorOrWorker.label
},
(msg: Message) => {
this._protocol.handleMessage(msg);
@ -359,28 +358,14 @@ export class SimpleWorkerClient<W extends object> extends Disposable implements
});
this._protocol.setWorkerId(this._worker.getId());
// Gather loader configuration
let loaderConfiguration: any = null;
const globalRequire: { getConfig?(): object } | undefined = (globalThis as any).require;
if (typeof globalRequire !== 'undefined' && typeof globalRequire.getConfig === 'function') {
// Get the configuration from the Monaco AMD Loader
loaderConfiguration = globalRequire.getConfig();
} else if (typeof (globalThis as any).requirejs !== 'undefined') {
// Get the configuration from requirejs
loaderConfiguration = (globalThis as any).requirejs.s.contexts._.config;
}
// Send initialize message
this._onModuleLoaded = this._protocol.sendMessage(DEFAULT_CHANNEL, INITIALIZE, [
this._worker.getId(),
JSON.parse(JSON.stringify(loaderConfiguration)),
workerDescriptor.moduleId,
]);
this.proxy = this._protocol.createProxyToRemoteChannel(DEFAULT_CHANNEL, async () => { await this._onModuleLoaded; });
this._onModuleLoaded.catch((e) => {
this._onError('Worker failed to load ' + workerDescriptor.moduleId, e);
this._onError('Worker failed to load ', e);
});
}
@ -455,24 +440,21 @@ export interface IRequestHandler {
[prop: string]: any;
}
export interface IRequestHandlerFactory {
(workerServer: IWorkerServer): IRequestHandler;
export interface IRequestHandlerFactory<T extends IRequestHandler> {
(workerServer: IWorkerServer): T;
}
/**
* Worker side
*/
export class SimpleWorkerServer implements IWorkerServer {
export class SimpleWorkerServer<T extends IRequestHandler> implements IWorkerServer {
private _requestHandlerFactory: IRequestHandlerFactory | null;
private _requestHandler: IRequestHandler | null;
public readonly requestHandler: T;
private _protocol: SimpleWorkerProtocol;
private readonly _localChannels: Map<string, object> = new Map();
private readonly _remoteChannels: Map<string, object> = new Map();
constructor(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void, requestHandlerFactory: IRequestHandlerFactory | null) {
this._requestHandlerFactory = requestHandlerFactory;
this._requestHandler = null;
constructor(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void, requestHandlerFactory: IRequestHandlerFactory<T>) {
this._protocol = new SimpleWorkerProtocol({
sendMessage: (msg: any, transfer: ArrayBuffer[]): void => {
postMessage(msg, transfer);
@ -480,6 +462,7 @@ export class SimpleWorkerServer implements IWorkerServer {
handleMessage: (channel: string, method: string, args: any[]): Promise<any> => this._handleMessage(channel, method, args),
handleEvent: (channel: string, eventName: string, arg: any): Event<any> => this._handleEvent(channel, eventName, arg)
});
this.requestHandler = requestHandlerFactory(this);
}
public onmessage(msg: any): void {
@ -488,10 +471,10 @@ export class SimpleWorkerServer implements IWorkerServer {
private _handleMessage(channel: string, method: string, args: any[]): Promise<any> {
if (channel === DEFAULT_CHANNEL && method === INITIALIZE) {
return this.initialize(<number>args[0], <any>args[1], <string>args[2]);
return this.initialize(<number>args[0]);
}
const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this._requestHandler : this._localChannels.get(channel));
const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this.requestHandler : this._localChannels.get(channel));
if (!requestHandler) {
return Promise.reject(new Error(`Missing channel ${channel} on worker thread`));
}
@ -507,7 +490,7 @@ export class SimpleWorkerServer implements IWorkerServer {
}
private _handleEvent(channel: string, eventName: string, arg: any): Event<any> {
const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this._requestHandler : this._localChannels.get(channel));
const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this.requestHandler : this._localChannels.get(channel));
if (!requestHandler) {
throw new Error(`Missing channel ${channel} on worker thread`);
}
@ -540,50 +523,7 @@ export class SimpleWorkerServer implements IWorkerServer {
return this._remoteChannels.get(channel) as Proxied<T>;
}
private async initialize(workerId: number, loaderConfig: any, moduleId: string): Promise<void> {
private async initialize(workerId: number): Promise<void> {
this._protocol.setWorkerId(workerId);
if (this._requestHandlerFactory) {
// static request handler
this._requestHandler = this._requestHandlerFactory(this);
return;
}
if (loaderConfig) {
// Remove 'baseUrl', handling it is beyond scope for now
if (typeof loaderConfig.baseUrl !== 'undefined') {
delete loaderConfig['baseUrl'];
}
if (typeof loaderConfig.paths !== 'undefined') {
if (typeof loaderConfig.paths.vs !== 'undefined') {
delete loaderConfig.paths['vs'];
}
}
if (typeof loaderConfig.trustedTypesPolicy !== 'undefined') {
// don't use, it has been destroyed during serialize
delete loaderConfig['trustedTypesPolicy'];
}
// Since this is in a web worker, enable catching errors
loaderConfig.catchError = true;
(globalThis as any).require.config(loaderConfig);
}
const url = FileAccess.asBrowserUri(`${moduleId}.js` as AppResourcePath).toString(true);
return import(`${url}`).then((module: { create: IRequestHandlerFactory }) => {
this._requestHandler = module.create(this);
if (!this._requestHandler) {
throw new Error(`No RequestHandler!`);
}
});
}
}
/**
* Defines the worker entry point. Must be exported and named `create`.
* @skipMangle
*/
export function create(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void): SimpleWorkerServer {
return new SimpleWorkerServer(postMessage, null);
}

View File

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRequestHandlerFactory, SimpleWorkerServer } from './simpleWorker.js';
import { IRequestHandler, IRequestHandlerFactory, SimpleWorkerServer } from './simpleWorker.js';
type MessageEvent = {
data: any;
@ -16,13 +16,13 @@ declare const globalThis: {
let initialized = false;
function initialize(factory: IRequestHandlerFactory) {
function initialize<T extends IRequestHandler>(factory: IRequestHandlerFactory<T>) {
if (initialized) {
return;
}
initialized = true;
const simpleWorker = new SimpleWorkerServer(
const simpleWorker = new SimpleWorkerServer<T>(
msg => globalThis.postMessage(msg),
(workerServer) => factory(workerServer)
);
@ -32,7 +32,7 @@ function initialize(factory: IRequestHandlerFactory) {
};
}
export function bootstrapSimpleWorker(factory: IRequestHandlerFactory) {
export function bootstrapSimpleWorker(factory: IRequestHandlerFactory<any>) {
globalThis.onmessage = (_e: MessageEvent) => {
// Ignore first message in this case and initialize if not yet initialized
if (!initialized) {

View File

@ -410,7 +410,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
private _disposed = false;
constructor(
private readonly _workerDescriptor: IWorkerDescriptor,
private readonly _workerDescriptorOrWorker: IWorkerDescriptor | Worker,
keepIdleModels: boolean,
@IModelService modelService: IModelService,
) {
@ -429,7 +429,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
private _getOrCreateWorker(): IWorkerClient<EditorSimpleWorker> {
if (!this._worker) {
try {
this._worker = this._register(createWebWorker<EditorSimpleWorker>(this._workerDescriptor));
this._worker = this._register(createWebWorker<EditorSimpleWorker>(this._workerDescriptorOrWorker));
EditorWorkerHost.setChannel(this._worker, this._createEditorWorkerHost());
} catch (err) {
logOnceWebWorkerWarning(err);
@ -452,7 +452,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
}
private _createFallbackLocalWorker(): SynchronousWorkerClient<EditorSimpleWorker> {
return new SynchronousWorkerClient(new EditorSimpleWorker(this._createEditorWorkerHost(), null));
return new SynchronousWorkerClient(new EditorSimpleWorker(null));
}
private _createEditorWorkerHost(): EditorWorkerHost {

View File

@ -6,7 +6,7 @@
import { stringDiff } from '../../../base/common/diff/diff.js';
import { IDisposable } from '../../../base/common/lifecycle.js';
import { URI } from '../../../base/common/uri.js';
import { IRequestHandler, IWorkerServer } from '../../../base/common/worker/simpleWorker.js';
import { IRequestHandler } from '../../../base/common/worker/simpleWorker.js';
import { Position } from '../core/position.js';
import { IRange, Range } from '../core/range.js';
import { EndOfLineSequence, ITextModel } from '../model.js';
@ -16,16 +16,13 @@ import { computeLinks } from '../languages/linkComputer.js';
import { BasicInplaceReplace } from '../languages/supports/inplaceReplaceSupport.js';
import { DiffAlgorithmName, IDiffComputationResult, ILineChange, IUnicodeHighlightsResult } from './editorWorker.js';
import { createMonacoBaseAPI } from './editorBaseApi.js';
import { EditorWorkerHost } from './editorWorkerHost.js';
import { StopWatch } from '../../../base/common/stopwatch.js';
import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from './unicodeTextModelHighlighter.js';
import { DiffComputer, IChange } from '../diff/legacyLinesDiffComputer.js';
import { ILinesDiffComputer, ILinesDiffComputerOptions } from '../diff/linesDiffComputer.js';
import { DetailedLineRangeMapping } from '../diff/rangeMapping.js';
import { linesDiffComputers } from '../diff/linesDiffComputers.js';
import { createProxyObject, getAllMethodNames } from '../../../base/common/objects.js';
import { IDocumentDiffProviderOptions } from '../diff/documentDiffProvider.js';
import { AppResourcePath, FileAccess } from '../../../base/common/network.js';
import { BugIndicatingError } from '../../../base/common/errors.js';
import { computeDefaultDocumentColors } from '../languages/defaultDocumentColorsComputer.js';
import { FindSectionHeaderOptions, SectionHeader, findSectionHeaders } from './findSectionHeaders.js';
@ -64,15 +61,6 @@ export interface IWordRange {
readonly end: number;
}
/**
* @internal
*/
export interface IForeignModuleFactory {
(ctx: IWorkerContext, createData: any): any;
}
declare const require: any;
/**
* @internal
*/
@ -91,7 +79,7 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync
return this._workerTextModelSyncServer.getModel(uri);
}
protected _getModels(): ICommonModel[] {
public getModels(): ICommonModel[] {
return this._workerTextModelSyncServer.getModels();
}
@ -516,11 +504,8 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync
*/
export class EditorSimpleWorker extends BaseEditorSimpleWorker {
private _foreignModule: any = null;
constructor(
private readonly _host: EditorWorkerHost,
private readonly _foreignModuleFactory: IForeignModuleFactory | null
private readonly _foreignModule: any | null
) {
super();
}
@ -529,40 +514,6 @@ export class EditorSimpleWorker extends BaseEditorSimpleWorker {
return 'pong';
}
// ---- BEGIN foreign module support --------------------------------------------------------------------------
public $loadForeignModule(moduleId: string, createData: any, foreignHostMethods: string[]): Promise<string[]> {
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
return this._host.$fhr(method, args);
};
const foreignHost = createProxyObject(foreignHostMethods, proxyMethodRequest);
const ctx: IWorkerContext<any> = {
host: foreignHost,
getMirrorModels: (): IMirrorModel[] => {
return this._getModels();
}
};
if (this._foreignModuleFactory) {
this._foreignModule = this._foreignModuleFactory(ctx, createData);
// static foreing module
return Promise.resolve(getAllMethodNames(this._foreignModule));
}
return new Promise<any>((resolve, reject) => {
const onModuleCallback = (foreignModule: { create: IForeignModuleFactory }) => {
this._foreignModule = foreignModule.create(ctx, createData);
resolve(getAllMethodNames(this._foreignModule));
};
const url = FileAccess.asBrowserUri(`${moduleId}.js` as AppResourcePath).toString(true);
import(`${url}`).then(onModuleCallback).catch(reject);
});
}
// foreign method request
public $fmr(method: string, args: any[]): Promise<any> {
if (!this._foreignModule || typeof this._foreignModule[method] !== 'function') {
@ -579,15 +530,6 @@ export class EditorSimpleWorker extends BaseEditorSimpleWorker {
// ---- END foreign module support --------------------------------------------------------------------------
}
/**
* Defines the worker entry point. Must be exported and named `create`.
* @skipMangle
* @internal
*/
export function create(workerServer: IWorkerServer): IRequestHandler {
return new EditorSimpleWorker(EditorWorkerHost.getChannel(workerServer), null);
}
// This is only available in a Web Worker
declare function importScripts(...urls: string[]): void;

View File

@ -3,7 +3,38 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { create } from './editorSimpleWorker.js';
import { bootstrapSimpleEditorWorker } from './editorWorkerBootstrap.js';
import { SimpleWorkerServer } from '../../../base/common/worker/simpleWorker.js';
import { EditorSimpleWorker } from './editorSimpleWorker.js';
bootstrapSimpleEditorWorker(create);
type MessageEvent = {
data: any;
};
declare const globalThis: {
postMessage: (message: any) => void;
onmessage: (event: MessageEvent) => void;
};
let initialized = false;
function initialize() {
if (initialized) {
return;
}
initialized = true;
const simpleWorker = new SimpleWorkerServer((msg) => {
globalThis.postMessage(msg);
}, () => new EditorSimpleWorker(null));
globalThis.onmessage = (e: MessageEvent) => {
simpleWorker.onmessage(e.data);
};
}
globalThis.onmessage = (e: MessageEvent) => {
// Ignore first message in this case and initialize if not yet initialized
if (!initialized) {
initialize();
}
};

View File

@ -1,51 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkerServer, SimpleWorkerServer } from '../../../base/common/worker/simpleWorker.js';
import { EditorSimpleWorker } from './editorSimpleWorker.js';
import { EditorWorkerHost } from './editorWorkerHost.js';
type MessageEvent = {
data: any;
};
declare const globalThis: {
postMessage: (message: any) => void;
onmessage: (event: MessageEvent) => void;
};
let initialized = false;
export function initialize(factory: any) {
if (initialized) {
return;
}
initialized = true;
const simpleWorker = new SimpleWorkerServer((msg) => {
globalThis.postMessage(msg);
}, (workerServer: IWorkerServer) => new EditorSimpleWorker(EditorWorkerHost.getChannel(workerServer), null));
globalThis.onmessage = (e: MessageEvent) => {
simpleWorker.onmessage(e.data);
};
}
globalThis.onmessage = (e: MessageEvent) => {
// Ignore first message in this case and initialize if not yet initialized
if (!initialized) {
initialize(null);
}
};
type CreateFunction<C, D, R = any> = (ctx: C, data: D) => R;
export function bootstrapSimpleEditorWorker<C, D, R>(createFn: CreateFunction<C, D, R>) {
globalThis.onmessage = () => {
initialize((ctx: C, createData: D) => {
return createFn.call(self, ctx, createData);
});
};
}

View File

@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SimpleWorkerServer } from '../base/common/worker/simpleWorker.js';
import { EditorSimpleWorker, IWorkerContext } from './common/services/editorSimpleWorker.js';
import { EditorWorkerHost } from './common/services/editorWorkerHost.js';
/**
* Used by `monaco-editor` to hook up web worker rpc.
* @skipMangle
* @internal
*/
export function start<THost extends object, TClient extends object>(client: TClient): IWorkerContext<THost> {
const simpleWorker = new SimpleWorkerServer((msg) => {
globalThis.postMessage(msg);
}, () => new EditorSimpleWorker(client));
globalThis.onmessage = (e: MessageEvent) => {
simpleWorker.onmessage(e.data);
};
const editorWorkerHost = EditorWorkerHost.getChannel(simpleWorker);
const host = new Proxy({}, {
get(target, prop, receiver) {
if (typeof prop !== 'string') {
throw new Error(`Not supported`);
}
return (...args: any[]) => {
return editorWorkerHost.$fhr(prop, args);
};
}
});
return {
host: host as THost,
getMirrorModels: () => {
return simpleWorker.requestHandler.getModels();
}
};
}

View File

@ -1,6 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export * from './common/services/editorWorkerBootstrap.js';

View File

@ -12,7 +12,7 @@ import { FontMeasurements } from '../../browser/config/fontMeasurements.js';
import { ICodeEditor } from '../../browser/editorBrowser.js';
import { EditorCommand, ServicesAccessor } from '../../browser/editorExtensions.js';
import { ICodeEditorService } from '../../browser/services/codeEditorService.js';
import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from './standaloneWebWorker.js';
import { IInternalWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from './standaloneWebWorker.js';
import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from '../../common/config/editorOptions.js';
import { EditorZoom } from '../../common/config/editorZoom.js';
import { BareFontInfo, FontInfo } from '../../common/config/fontInfo.js';
@ -331,7 +331,7 @@ export function onDidChangeModelLanguage(listener: (e: { readonly model: ITextMo
* Create a new web worker that has model syncing capabilities built in.
* Specify an AMD module to load that will `create` an object that will be proxied.
*/
export function createWebWorker<T extends object>(opts: IWebWorkerOptions): MonacoWebWorker<T> {
export function createWebWorker<T extends object>(opts: IInternalWebWorkerOptions): MonacoWebWorker<T> {
return actualCreateWebWorker<T>(StandaloneServices.get(IModelService), opts);
}

View File

@ -1077,7 +1077,7 @@ class StandaloneContextMenuService extends ContextMenuService {
}
}
export const standaloneEditorWorkerDescriptor: IWorkerDescriptor = {
const standaloneEditorWorkerDescriptor: IWorkerDescriptor = {
moduleId: 'vs/editor/common/services/editorSimpleWorker',
esmModuleLocation: undefined,
label: 'editorWorkerService'

View File

@ -3,18 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getAllMethodNames } from '../../../base/common/objects.js';
import { URI } from '../../../base/common/uri.js';
import { IWorkerDescriptor } from '../../../base/common/worker/simpleWorker.js';
import { EditorWorkerClient } from '../../browser/services/editorWorkerService.js';
import { IModelService } from '../../common/services/model.js';
import { standaloneEditorWorkerDescriptor } from './standaloneServices.js';
/**
* Create a new web worker that has model syncing capabilities built in.
* Specify an AMD module to load that will `create` an object that will be proxied.
*/
export function createWebWorker<T extends object>(modelService: IModelService, opts: IWebWorkerOptions): MonacoWebWorker<T> {
export function createWebWorker<T extends object>(modelService: IModelService, opts: IInternalWebWorkerOptions): MonacoWebWorker<T> {
return new MonacoWebWorkerImpl<T>(modelService, opts);
}
@ -37,20 +34,11 @@ export interface MonacoWebWorker<T> {
withSyncedResources(resources: URI[]): Promise<T>;
}
export interface IWebWorkerOptions {
export interface IInternalWebWorkerOptions {
/**
* The AMD moduleId to load.
* It should export a function `create` that should return the exported proxy.
* The worker.
*/
moduleId: string;
/**
* The data to send over when calling create on the module.
*/
createData?: any;
/**
* A label to be used to identify the web worker for debugging purposes.
*/
label?: string;
worker: Worker;
/**
* An object that can be used by the web worker to make calls back to the main thread.
*/
@ -64,22 +52,24 @@ export interface IWebWorkerOptions {
class MonacoWebWorkerImpl<T extends object> extends EditorWorkerClient implements MonacoWebWorker<T> {
private readonly _foreignModuleId: string;
private readonly _foreignModuleHost: { [method: string]: Function } | null;
private _foreignModuleCreateData: any | null;
private _foreignProxy: Promise<T> | null;
private _foreignProxy: Promise<T>;
constructor(modelService: IModelService, opts: IWebWorkerOptions) {
const workerDescriptor: IWorkerDescriptor = {
moduleId: standaloneEditorWorkerDescriptor.moduleId,
esmModuleLocation: standaloneEditorWorkerDescriptor.esmModuleLocation,
label: opts.label,
};
super(workerDescriptor, opts.keepIdleModels || false, modelService);
this._foreignModuleId = opts.moduleId;
this._foreignModuleCreateData = opts.createData || null;
constructor(modelService: IModelService, opts: IInternalWebWorkerOptions) {
super(opts.worker, opts.keepIdleModels || false, modelService);
this._foreignModuleHost = opts.host || null;
this._foreignProxy = null;
this._foreignProxy = this._getProxy().then(proxy => {
return new Proxy({}, {
get(target, prop, receiver) {
if (typeof prop !== 'string') {
throw new Error(`Not supported`);
}
return (...args: any[]) => {
return proxy.$fmr(prop, args);
};
}
}) as T;
});
}
// foreign host request
@ -95,38 +85,8 @@ class MonacoWebWorkerImpl<T extends object> extends EditorWorkerClient implement
}
}
private _getForeignProxy(): Promise<T> {
if (!this._foreignProxy) {
this._foreignProxy = this._getProxy().then((proxy) => {
const foreignHostMethods = this._foreignModuleHost ? getAllMethodNames(this._foreignModuleHost) : [];
return proxy.$loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData, foreignHostMethods).then((foreignMethods) => {
this._foreignModuleCreateData = null;
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
return proxy.$fmr(method, args);
};
const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise<any>): () => Promise<any> => {
return function () {
const args = Array.prototype.slice.call(arguments, 0);
return proxyMethodRequest(method, args);
};
};
const foreignProxy = {} as any as T;
for (const foreignMethod of foreignMethods) {
(<any>foreignProxy)[foreignMethod] = createProxyMethod(foreignMethod, proxyMethodRequest);
}
return foreignProxy;
});
});
}
return this._foreignProxy;
}
public getProxy(): Promise<T> {
return this._getForeignProxy();
return this._foreignProxy;
}
public withSyncedResources(resources: URI[]): Promise<T> {

17
src/vs/monaco.d.ts vendored
View File

@ -1099,7 +1099,7 @@ declare namespace monaco.editor {
* Create a new web worker that has model syncing capabilities built in.
* Specify an AMD module to load that will `create` an object that will be proxied.
*/
export function createWebWorker<T extends object>(opts: IWebWorkerOptions): MonacoWebWorker<T>;
export function createWebWorker<T extends object>(opts: IInternalWebWorkerOptions): MonacoWebWorker<T>;
/**
* Colorize the contents of `domNode` using attribute `data-lang`.
@ -1218,20 +1218,11 @@ declare namespace monaco.editor {
withSyncedResources(resources: Uri[]): Promise<T>;
}
export interface IWebWorkerOptions {
export interface IInternalWebWorkerOptions {
/**
* The AMD moduleId to load.
* It should export a function `create` that should return the exported proxy.
* The worker.
*/
moduleId: string;
/**
* The data to send over when calling create on the module.
*/
createData?: any;
/**
* A label to be used to identify the web worker for debugging purposes.
*/
label?: string;
worker: Worker;
/**
* An object that can be used by the web worker to make calls back to the main thread.
*/

View File

@ -7,7 +7,7 @@ import * as monaco from 'monaco-editor-core';
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
return './editor.worker.bundle.js';
return './editorSimpleWorkerMain.bundle.js';
}
};

View File

@ -10,7 +10,7 @@ module.exports = {
mode: 'production',
entry: {
'core': './core.js',
'editor.worker': '../../out-monaco-editor-core/esm/vs/editor/editor.worker.js',
'editorSimpleWorkerMain': '../../out-monaco-editor-core/esm/vs/editor/common/services/editorSimpleWorkerMain.js',
},
output: {
globalObject: 'self',
@ -39,7 +39,6 @@ module.exports = {
},
resolve: {
alias: {
'monaco-editor-core/esm/vs/editor/editor.worker': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.worker.js'),
'monaco-editor-core': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.main.js'),
}
},