vscode 插件系统的运行机制!

vscode二次开发有一段时间了,平时都是任务比较重,最近有时间做下总结,详细的讲解下vscode 插件系统的运行机制,vscode做为最受欢迎的编辑器,有着庞大的插件市场。其插件系统确实很复杂,文章很长,但很详细!希望对有这方面需求的同学有帮助,另外有编辑器相关需求的问题欢迎探讨。

流程: 插件列表展示->插件下载->插件激活->插件和主体通讯

插件列表如何生成

在这里插入图片描述
我们来看下请求接口的地方
src/vs/workbench/contrib/extensions/browser/extensionsViews.ts

private async query(query: Query, options: IQueryOptions, token: CancellationToken): Promise<IQueryResult> {...if (ids.length) {const model = await this.queryByIds(ids, options, token);return { model, disposables: new DisposableStore() };}
...
}

跳转到queryByIds 方法里面

const galleryResult = await this.extensionsWorkbenchService.getExtensions(galleryIds.map(id => ({ id })), { source: 'queryById' }, token);result.push(...galleryResult);

继续找 getExtensions方法
src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

const galleryExtensions = await this.galleryService.getExtensions(extensionInfos, arg1, arg2);
this.syncInstalledExtensionsWithGallery(galleryExtensions);

继续跳getExtensions方法
src/vs/platform/extensionManagement/common/extensionGalleryService.ts

const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, includePreRelease: includePreReleases, versions, compatible: !!options.compatible }, token);

继续找 queryGalleryExtensions方法

const { galleryExtensions: rawGalleryExtensions, total } = await this.queryRawGalleryExtensions(query, token);

再跳进来,终于看到接口请求的地方了,兴奋🥰

private async queryRawGalleryExtensions(query: Query, token: CancellationToken): Promise<{ galleryExtensions: IRawGalleryExtension[]; total: number }> {if (!this.isEnabled()) {throw new Error('No extension gallery service configured.');}query = query/* Always exclude non validated extensions */.withFlags(query.flags, Flags.ExcludeNonValidated).withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')/* Always exclude unpublished extensions */.withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished));const commonHeaders = await this.commonHeadersPromise;const data = JSON.stringify(query.raw);const headers = {...commonHeaders,'Content-Type': 'application/json','Accept': 'application/json;api-version=3.0-preview.1','Accept-Encoding': 'gzip','Content-Length': String(data.length)};const startTime = new Date().getTime();let context: IRequestContext | undefined, error: any, total: number = 0;try {context = await this.requestService.request({type: 'POST',url: this.api('/extensionquery'),data,headers}, token);if (context.res.statusCode && context.res.statusCode >= 400 && context.res.statusCode < 500) {return { galleryExtensions: [], total };}const result = await asJson<IRawGalleryQueryResult>(context);if (result) {const r = result.results[0];const galleryExtensions = r.extensions;const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0];total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0;return { galleryExtensions, total };}return { galleryExtensions: [], total };
...}

打印 接口返回的内容

在这里插入图片描述

最后做数据列表展示 渲染页面

src/vs/workbench/contrib/extensions/browser/extensionsViews.ts

private getPagedModel(arg: IPager<IExtension> | IExtension[]): IPagedModel<IExtension> {if (Array.isArray(arg)) {return new PagedModel(arg);}const pager = {total: arg.total,pageSize: arg.pageSize,firstPage: arg.firstPage,getPage: (pageIndex: number, cancellationToken: CancellationToken) => arg.getPage(pageIndex, cancellationToken)};return new PagedModel(pager);}

经过处理后的最终数据格式
在这里插入图片描述

好到此页面已经渲染出来了

接下来是插件下载流程

插件下载按钮定义

src/vs/workbench/contrib/extensions/browser/extensionsActions.ts

export abstract class AbstractInstallAction extends ExtensionAction {static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`;protected _manifest: IExtensionManifest | null = null;set manifest(manifest: IExtensionManifest | null) {this._manifest = manifest;this.updateLabel();}private readonly updateThrottler = new Throttler();constructor(id: string, private readonly installPreReleaseVersion: boolean, cssClass: string,@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,@IInstantiationService private readonly instantiationService: IInstantiationService,@IExtensionService private readonly runtimeExtensionService: IExtensionService,@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,@ILabelService private readonly labelService: ILabelService,) {super(id, localize('install', "Install"), cssClass, false);this.update();this._register(this.labelService.onDidChangeFormatters(() => this.updateLabel(), this));}update(): void {this.updateThrottler.queue(() => this.computeAndUpdateEnablement());}
...// 点击执行事件override async run(): Promise<any> {if (!this.extension) {return;}this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.installPreReleaseVersion });alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName));// 这里下载const extension = await this.install(this.extension);if (extension?.local) {alert(localize('installExtensionComplete', "Installing extension {0} is completed.", this.extension.displayName));const runningExtension = await this.getRunningExtension(extension.local);if (runningExtension && !(runningExtension.activationEvents && runningExtension.activationEvents.some(activationEent => activationEent.startsWith('onLanguage')))) {const action = await this.getThemeAction(extension);if (action) {action.extension = extension;try {return action.run({ showCurrentTheme: true, ignoreFocusLost: true });} finally {action.dispose();}}}}}...private async install(extension: IExtension): Promise<IExtension | undefined> {const installOptions = this.getInstallOptions();try {return await this.extensionsWorkbenchService.install(extension, installOptions);} catch (error) {await this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Install, installOptions, error).run();return undefined;}}...
}

src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

install(extension: URI | IExtension, installOptions?: InstallOptions | InstallVSIXOptions): Promise<IExtension> {if (extension instanceof URI) {return this.installWithProgress(() => this.installFromVSIX(extension, installOptions));}if (extension.isMalicious) {return Promise.reject(new Error(nls.localize('malicious', "This extension is reported to be problematic.")));}const gallery = extension.gallery;if (!gallery) {return Promise.reject(new Error('Missing gallery'));}return this.installWithProgress(() => this.installFromGallery(extension, gallery, installOptions), gallery.displayName);}
...
await this.extensionManagementService.installFromGallery(gallery, installOptions);
...

继续跟进去
src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts

async installFromGallery(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);...

src/vs/platform/extensionManagement/common/extensionGalleryService.ts

这里主要作用是做插件信息展示

private async getAsset(asset: IGalleryExtensionAsset, options: IRequestOptions = {}, token: CancellationToken = CancellationToken.None): Promise<IRequestContext> {const commonHeaders = await this.commonHeadersPromise;const baseOptions = { type: 'GET' };const headers = { ...commonHeaders, ...(options.headers || {}) };options = { ...options, ...baseOptions, headers };const url = asset.uri;const fallbackUrl = asset.fallbackUri;const firstOptions = { ...options, url };try {const context = await this.requestService.request(firstOptions, token);if (context.res.statusCode === 200) {return context;}const message = await asText(context);throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`);} catch (err) {if (isCancellationError(err)) {throw err;}const message = getErrorMessage(err);type GalleryServiceCDNFallbackClassification = {url: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' };message: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' };};type GalleryServiceCDNFallbackEvent = {url: string;message: string;};this.telemetryService.publicLog2<GalleryServiceCDNFallbackEvent, GalleryServiceCDNFallbackClassification>('galleryService:cdnFallback', { url, message });const fallbackOptions = { ...options, url: fallbackUrl };return this.requestService.request(fallbackOptions, token);}}

展示插件信息的url (同步进行),例子仅供参考
https://angular.gallerycdn.azure.cn/extensions/angular/ng-template/16.0.0/1683140320423/Microsoft.VisualStudio.Code.Manifest
https://johnpapa.gallerycdn.azure.cn/extensions/johnpapa/angular2/16.0.1/1686880343716/Microsoft.VisualStudio.Services.Content.Details

下载插件的地方
src/vs/platform/extensionManagement/node/extensionManagementService.ts

this.logService.trace('Started downloading extension:', extension.identifier.id);
zipPath = (await this.extensionsDownloader.downloadExtension(extension, operation)).fsPath;
this.logService.info('Downloaded extension:', extension.identifier.id, zipPath);

下载插件的url 例子仅供参考

https://angular.gallery.vsassets.io/_apis/public/gallery/publisher/Angular/extension/ng-template/13.3.4/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage?redirect=true&install=true

src/vs/platform/extensionManagement/common/extensionGalleryService.ts

插件写入本地

const context = await this.getAsset(downloadAsset);
await this.fileService.writeFile(location, context.stream);
log(new Date().getTime() - startTime);

到这里终于把插件市场里的插件下载下来了!

下载下来的是二进制的压缩包 需要解压使用
src/vs/platform/extensionManagement/node/extensionManagementService.ts
提取插件信息

let local = await this.extensionsScanner.extractUserExtension(key, zipPath, metadata, token);
this.logService.info('Extracting completed.', key.id);

解压插件到本地

src/vs/platform/extensionManagement/node/extensionsScanner.ts

async extractUserExtension(extensionKey: ExtensionKey, zipPath: string, metadata: Metadata | undefined, token: CancellationToken): Promise<ILocalExtension> {const folderName = extensionKey.toString();const tempPath = path.join(this.userExtensionsLocation.fsPath, `.${generateUuid()}`);const extensionPath = path.join(this.userExtensionsLocation.fsPath, folderName);try {await pfs.Promises.rm(extensionPath);} catch (error) {throw new ExtensionManagementError(localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, extensionKey.id), ExtensionManagementErrorCode.Delete);}await this.extractAtLocation(extensionKey, zipPath, tempPath, token);let local = await this.scanExtension(URI.file(tempPath), ExtensionType.User);if (!local) {throw new Error(localize('cannot read', "Cannot read the extension from {0}", tempPath));}await this.storeMetadata(local, { ...metadata, installedTimestamp: Date.now() });
...

这时候 插件从下载到解压到本地已完成

接下来讲解插件如何被激活

每个插件里面都有 activate 函数,做为总入口

src/vs/workbench/api/common/extHostExtensionService.ts

private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {if (typeof extensionModule.activate === 'function') {try {activationTimesBuilder.activateCallStart();logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workers// 这里激活const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(scope, [context]);activationTimesBuilder.activateCallStop();activationTimesBuilder.activateResolveStart();return Promise.resolve(activateResult).then((value) => {activationTimesBuilder.activateResolveStop();return value;});} catch (err) {return Promise.reject(err);}} else {// No activate found => the module is the extension's exportsreturn Promise.resolve<IExtensionAPI>(extensionModule);}}

读取所有插件信息

src/vs/platform/extensionManagement/node/extensionsScanner.ts

private async readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: Metadata | null }> {const promises = [pfs.Promises.readFile(path.join(extensionPath, 'package.json'), 'utf8').then(raw => this.parseManifest(raw)),pfs.Promises.readFile(path.join(extensionPath, 'package.nls.json'), 'utf8').then(undefined, err => err.code !== 'ENOENT' ? Promise.reject<string>(err) : '{}').then(raw => JSON.parse(raw))];const [{ manifest, metadata }, translations] = await Promise.all(promises);return {manifest: localizeManifest(manifest, translations),metadata};
}

vscode的插件分三类

  • defaultSystemExtensions vscode自带插件在源码extensions目录里面
  • devSystemExtensions product.json里的builtInExtensions字段里的插件
  • userExtensions 系统用户目录.vscode里面的插件
try {const [defaultSystemExtensions, devSystemExtensions, userExtensions] = await Promise.all(promises);const result = this.dedupExtensions([...defaultSystemExtensions, ...devSystemExtensions, ...userExtensions], await this.targetPlatform);return type !== null ? result.filter(r => r.type === type) : result;
} catch (error) {throw this.joinErrors(error);
}

打印result
在这里插入图片描述
每个插件都有一个active函数总入口,那么每个插件是如何被load加载的 以及 active如何被触发的呢?

src/vs/workbench/api/common/extHostExtensionService.ts

拿到所有插件数据之后 加载触发active

_loadCommonJSModule是重头戏

private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
// 读取package.json main字段const entryPoint = this._getEntryPoint(extensionDescription);...return Promise.all([// 重头戏在这里this._loadCommonJSModule<IExtensionModule>(extensionDescription.identifier, joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),this._loadExtensionContext(extensionDescription)]).then(values => {performance.mark(`code/extHost/willActivateExtension/${extensionDescription.identifier.value}`);// 激活插件return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);}).then((activatedExtension) => {performance.mark(`code/extHost/didActivateExtension/${extensionDescription.identifier.value}`);return activatedExtension;});
}

this._getEntryPoint(extensionDescription); 读取package.json main字段

protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {return extensionDescription.main;
}

可以看出找的就是package.json里的mian字段,也就是每个插件的总入口
src/vs/workbench/api/node/extHostExtensionService.ts

protected _loadCommonJSModule<T>(extensionId: ExtensionIdentifier | null, module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {if (module.scheme !== Schemas.file) {throw new Error(`Cannot load URI: '${module}', must be of file-scheme`);}let r: T | null = null;activationTimesBuilder.codeLoadingStart();this._logService.trace(`ExtensionService#loadCommonJSModule ${module.toString(true)}`);this._logService.flush();try {if (extensionId) {performance.mark(`code/extHost/willLoadExtensionCode/${extensionId.value}`);}r = require.__$__nodeRequire<T>(module.fsPath);} catch (e) {return Promise.reject(e);} finally {if (extensionId) {performance.mark(`code/extHost/didLoadExtensionCode/${extensionId.value}`);}activationTimesBuilder.codeLoadingStop();}return Promise.resolve(r);
}

require.__$__nodeRequire(module.fsPath); 加载main引入的模块

插件总入口加载完毕后 触发active函数

return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);

src/vs/workbench/api/common/extHostExtensionService.ts

可以看到这个active函数被触发了

private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {if (typeof extensionModule.activate === 'function') {try {activationTimesBuilder.activateCallStart();logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workersconst activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(scope, [context]);activationTimesBuilder.activateCallStop();activationTimesBuilder.activateResolveStart();return Promise.resolve(activateResult).then((value) => {activationTimesBuilder.activateResolveStop();return value;});} catch (err) {return Promise.reject(err);}} else {// No activate found => the module is the extension's exportsreturn Promise.resolve<IExtensionAPI>(extensionModule);}}

插件进程是如何创建的

src/vs/platform/extensions/node/extensionHostStarterWorker.ts

start(opts: IExtensionHostProcessOptions): { pid: number } {if (platform.isCI) {this._host.logInfo(`Calling fork to start extension host...`);}const sw = StopWatch.create(false);this._process = fork(FileAccess.asFileUri('bootstrap-fork', require).fsPath,['--type=extensionHost', '--skipWorkspaceStorageLock'],mixin({ cwd: cwd() }, opts),);const forkTime = sw.elapsed();const pid = this._process.pid!;this._host.logInfo(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`);const stdoutDecoder = new StringDecoder('utf-8');this._process.stdout?.on('data', (chunk) => {const strChunk = typeof chunk === 'string' ? chunk : stdoutDecoder.write(chunk);this._onStdout.fire(strChunk);});const stderrDecoder = new StringDecoder('utf-8');this._process.stderr?.on('data', (chunk) => {const strChunk = typeof chunk === 'string' ? chunk : stderrDecoder.write(chunk);this._onStderr.fire(strChunk);});this._process.on('message', msg => {this._onMessage.fire(msg);});this._process.on('error', (err) => {this._onError.fire({ error: transformErrorForSerialization(err) });});this._process.on('exit', (code: number, signal: string) => {this._hasExited = true;this._onExit.fire({ pid, code, signal });});return { pid };
}

一个插件一个进程,所有插件的数据都是被隔离开的。不会被互相影响

vscode一共对外扩展了300多个属性和方法。

想知道如何扩展vscode属性、方法 看我这篇博客
https://blog.csdn.net/woyebuzhidao321/article/details/131071724
如何创建一个插件看我这篇
https://blog.csdn.net/woyebuzhidao321/article/details/121603141

vscode打开速度如此快,它的懒加载机制占很大原因,没有用到的代码是不会一开始就加载进来。
在插件package.json里面 有activationEvents字段。

"activationEvents": ["onCustomEditor:Vs.Audio"],

做为插件被加载的触发因素。满足以上事件时被触发
src/vs/workbench/api/common/extHostExtensionService.ts

public $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {if (activationKind === ActivationKind.Immediate) {return this._activateByEvent(activationEvent, false);}return (this._readyToRunExtensions.wait().then(_ => this._activateByEvent(activationEvent, false)));
}

最后的激活插件的执行方法都在这
src/vs/workbench/api/common/extHostExtensionService.ts

private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: vscode.ExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {if (typeof extensionModule.activate === 'function') {try {activationTimesBuilder.activateCallStart();logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workersconst activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(scope, [context]);activationTimesBuilder.activateCallStop();activationTimesBuilder.activateResolveStart();return Promise.resolve(activateResult).then((value) => {activationTimesBuilder.activateResolveStop();return value;});} catch (err) {return Promise.reject(err);}} else {// No activate found => the module is the extension's exportsreturn Promise.resolve<IExtensionAPI>(extensionModule);}}

插件中需要引入一个叫 vscode 的模块

import * as vscode from 'vscode';

熟悉 TypeScript 的朋友都知道这实际上只是引入了一个 vscode.d.ts 类型声明文件而已,这个文件包含了所有插件可用的 API 及类型定义。
所有的API方法 都在这里
src/vs/workbench/api/common/extHost.api.impl.ts

export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): IExtensionApiFactory {...return <typeof vscode>{version: initData.version,trash,// namespacesuserAuthentication,authentication,commands,comments,env,....
}    

API注册到运行环境
src/vs/workbench/api/common/extHostRequireInterceptor.ts

public load(_request: string, parent: URI): any {// get extension id from filename and api for extension// 这里会为每一个插件生成一份独立的 APIconst ext = this._extensionPaths.findSubstr(parent);if (ext) {let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));if (!apiImpl) {apiImpl = this._apiFactory(ext, this._extensionRegistry, this._configProvider);this._extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);}return apiImpl;}// fall back to a default implementationif (!this._defaultApiImpl) {let extensionPathsPretty = '';this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);this._logService.warn(`Could not identify extension for 'vscode' require call from ${parent}. These are the extension path mappings: \n${extensionPathsPretty}`);this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);}return this._defaultApiImpl;}

在这里插入图片描述
src/vs/workbench/api/node/extHostExtensionService.ts

protected _installInterceptor(): void {const that = this;const node_module = <any>require.__$__nodeRequire('module');const originalLoad = node_module._load;node_module._load = function load(request: string, parent: { filename: string }, isMain: boolean) {request = applyAlternatives(request);if (!that._factories.has(request)) {return originalLoad.apply(this, arguments);}return that._factories.get(request)!.load(request,URI.file(realpathSync(parent.filename)),request => originalLoad.apply(this, [request, parent, isMain]));};const originalLookup = node_module._resolveLookupPaths;node_module._resolveLookupPaths = (request: string, parent: unknown) => {return originalLookup.call(this, applyAlternatives(request), parent);};const applyAlternatives = (request: string) => {for (let alternativeModuleName of that._alternatives) {let alternative = alternativeModuleName(request);if (alternative) {request = alternative;break;}}return request;};}

这里在讲解在插件和vscode主体之间的消息通讯

首先我们在插件 activate 函数里面定义一个方法

export async function activate(context: vscode.ExtensionContext) {vscode.window.showInformationMessage('你好 世界!');
}    

调用方法
src/vs/workbench/api/common/extHost.api.impl.ts

showInformationMessage(message: string, ...rest: Array<vscode.MessageOptions | string | vscode.MessageItem>) {return <Thenable<any>>extHostMessageService.showMessage(extension, Severity.Info, message, rest[0], <Array<string | vscode.MessageItem>>rest.slice(1));
},

找到对应service
src/vs/workbench/api/common/extHostMessageService.ts

return this._proxy.$showMessage(severity, message, options, commands).then(handle => {if (typeof handle === 'number') {return items[handle];}return undefined;
});

我们看到这里有一个 this._proxy 代理,跟进去
src/vs/workbench/services/extensions/common/rpcProtocol.ts

private _createProxy<T>(rpcId: number, debugName: string): T {let handler = {get: (target: any, name: PropertyKey) => {if (typeof name === 'string' && !target[name] && name.charCodeAt(0) === CharCode.DollarSign) {target[name] = (...myArgs: any[]) => {// 这里重点return this._remoteCall(rpcId, name, myArgs);};}if (name === _RPCProxySymbol) {return debugName;}return target[name];}};return new Proxy(Object.create(null), handler);
}

this._remoteCall 方法里面有 ipc 通讯方法
src/vs/workbench/services/extensions/common/rpcProtocol.ts

 private _remoteCall(rpcId: number, methodName: string, args: any[]): Promise<any> {if (this._isDisposed) {return Promise.reject<any>(errors.canceled());}let cancellationToken: CancellationToken | null = null;if (args.length > 0 && CancellationToken.isCancellationToken(args[args.length - 1])) {cancellationToken = args.pop();}if (cancellationToken && cancellationToken.isCancellationRequested) {// No need to do anything...return Promise.reject<any>(errors.canceled());}const serializedRequestArguments = MessageIO.serializeRequestArguments(args, this._uriReplacer);const req = ++this._lastMessageId;const callId = String(req);const result = new LazyPromise();if (cancellationToken) {cancellationToken.onCancellationRequested(() => {const msg = MessageIO.serializeCancel(req);if (this._logger) {this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`);}this._protocol.send(MessageIO.serializeCancel(req));});}this._pendingRPCReplies[callId] = result;this._onWillSendRequest(req);const msg = MessageIO.serializeRequest(req, rpcId, methodName, serializedRequestArguments, !!cancellationToken);if (this._logger) {this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args);}// 重头戏this._protocol.send(msg);return result;}

his._protocol.send(msg); 重头戏
src/vs/base/parts/ipc/node/ipc.net.ts

这里面使用nodejs net模块 实现的ipc通讯

import { createConnection, createServer, Server as NetServer, Socket } from 'net';

对 nodejs net模块理解 看我这篇
https://blog.csdn.net/woyebuzhidao321/article/details/131494461
好了 再往下走 消息传到了主进程

src/vs/workbench/api/browser/mainThreadMessageService.ts

private async _showModalMessage(severity: Severity, message: string, detail: string | undefined, commands: { title: string; isCloseAffordance: boolean; handle: number }[], useCustom?: boolean): Promise<number | undefined> {let cancelId: number | undefined = undefined;const buttons = commands.map((command, index) => {if (command.isCloseAffordance === true) {cancelId = index;}return command.title;});if (cancelId === undefined) {if (buttons.length > 0) {buttons.push(nls.localize('cancel', "Cancel"));} else {buttons.push(nls.localize('ok', "OK"));}cancelId = buttons.length - 1;}const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, custom: useCustom, detail });return choice === commands.length ? undefined : commands[choice].handle;}

dialogService是vscode封装的调用主进程吐司提示的方法,看到这里流程就跑通了!

在这里插入图片描述

兄弟萌给个关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/9647.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Maven基础概念

仓库 作用&#xff1a;用于存储资源&#xff0c;包含各种jar包 仓库分类 本地仓库远程仓库 2.1中央仓库 2.2私服&#xff08;用于解决下载速度慢&#xff0c;版权问题等&#xff09; 1.maven坐标 <groupId></groupId>当前MAVEN项目隶属组织名称 <artifactId…

nginx纳入skywalking调用链监控

nginx纳入skywalking调用链监控 一、说明二、nginx部署2.1 OpenResty介绍2.2 准备SkyWalking Nginx Agent2.3 docker方式部署OpenResty2.3.1 修改配置文件2.3.2 启动OpenResty容器 2.4 验证 一、说明 服务器中已部署好skywalking&#xff0c;并将tomcat纳入skywalking监控(tom…

Unity包体积优化实践

目录 简述优化前优化中assets目录资源ab包动态下发资源大小优化dll大小优化场景模型动态下载和加载优化assets目录后大小 lib目录优化目标架构裁剪代码优化代码和引用 其他优化项Shader优化Release模式编译选项 优化后 简述 在移动端App混合Unity开发的项目中&#xff0c;Unit…

MATLAB图像处理实现高光抑制

下面是的几个用MATLAB进行高光抑制的处理例子。 1. 基于最大值滤波的亮光抑制方法 原理是用某像素周围一定大小的邻域中的最大值减去该像素值&#xff0c;可达到亮光抑制的效果。在MATLAB中&#xff0c;可以使用mat2gray函数将图像归一化后&#xff0c;再使用imextendedmax函…

【花雕】全国青少年机器人技术一级考试备考实操搭建手册10

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

目标检测的评估指标

Precision(精确率/查准率)&#xff1a;是指在所有被预测为正的样本中&#xff0c;确实是正样本的占比。当Precision越大时&#xff0c;FP越小&#xff0c;此时将其他类别预测为本类别的个数也就越少&#xff0c;可以理解为预测出的正例纯度越高。Precision越高&#xff0c;误检…

ORA-01122 ORA-01200故障处理---惜分飞

由于某种原因客户的数据库启动报ORA-01122 ORA-01200错误 让客户把system01.dbf文件发给我进行分析,发现system01.dbf文件大于32G(在8k的blocksize库中,默认情况system01.dbf文件不会超过32G),这个明显异常 检测坏块情况发现4096000之后的block全部为全0块 通过bbed分析文…

uniapp-设置全屏

需求&#xff1a;就是想要小程序不受限制&#xff0c;可以把图片或者文字全屏的展示&#xff0c;如下图 vue代码如下&#xff1a; <template><view class"content"><image class"image-bg" src"/static/logo.png" /><imag…

学生成绩管理系统的设计与实现(论文+源码)_kaic

摘要 该系统在开发过程中&#xff0c;要注意使其与业务流程的运作相一致&#xff0c;力争使该系统全面&#xff0c;通用&#xff0c;以便该系统不仅适用于教育机构。在开发方法的选择上&#xff0c;选择生命周期方法和原型方法&#xff0c;并按照四个主要阶段的系统研究&#x…

Jvm jmx_exporter Prometheus dubbo Grafana 重点看端口要对应上 单独进程和程序进程内jmx_exporter

目录 JMX Exporter 的两种用法 启动独立进程 jmx_prometheus_httpserver-0.18.0.jar 方式 下载 jmx_exporter 找地方随便一放 创建配置文件 config_jmx_exporter.yaml 增加 启动 jvm 配置 一定要是jvm参数 可别意外写成程序参数 启动jmx_exporter Prometheus yml 配置 …

php://input文件包含

实验目的 通过本实验&#xff0c;了解php封装伪协议&#xff0c;掌握php://input文件包含的用法 实验环境 操作机&#xff1a;kali 靶机&#xff1a;Windows 实验地址&#xff1a;http://靶机ip/exp/include2/input/input2/ 工具&#xff1a;burpsuite 用户名&#xff1a…

UE4/5用贴图和GeneratedDynamicMeshActor曲面细分与贴图位移制作模型

目录 制作逻辑&#xff1a; ​编辑 曲面细分函数&#xff1a; 添加贴图逻辑&#xff1a; 代码&#xff1a; 制作逻辑&#xff1a; 在之前的文章中&#xff0c;我们使用了网格细分&#xff0c;而这一次我们将使用曲面细分函数&#xff0c;使用方法和之前是一样的&#xff1a…