// ***************************************************************************** // Copyright (C) 2019 Red Hat, Inc. and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { DecorationData, DecorationRequest, DecorationsExt, DecorationsMain, MAIN_RPC_CONTEXT } from '../../../common/plugin-api-rpc'; import { interfaces } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { Disposable } from '@theia/core/lib/common/disposable'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { UriComponents } from '../../../common/uri-components'; import { URI as VSCodeURI } from '@theia/core/shared/vscode-uri'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; import URI from '@theia/core/lib/common/uri'; import { Decoration, DecorationsService } from '@theia/core/lib/browser/decorations-service'; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // some code copied and modified from https://github.com/microsoft/vscode/blob/1.52.1/src/vs/workbench/api/browser/mainThreadDecorations.ts#L85 class DecorationRequestsQueue { private idPool = 0; private requests = new Map(); private resolver = new Map void>(); // eslint-disable-next-line @typescript-eslint/no-explicit-any private timer: any; constructor( private readonly proxy: DecorationsExt, private readonly handle: number ) { } enqueue(uri: URI, token: CancellationToken): Promise { const id = ++this.idPool; const result = new Promise(resolve => { this.requests.set(id, { id, uri: VSCodeURI.parse(uri.toString()) }); this.resolver.set(id, resolve); this.processQueue(); }); token.onCancellationRequested(() => { this.requests.delete(id); this.resolver.delete(id); }); return result; } private processQueue(): void { if (typeof this.timer === 'number') { // already queued return; } this.timer = setTimeout(() => { // make request const requests = this.requests; const resolver = this.resolver; this.proxy.$provideDecorations(this.handle, [...requests.values()], CancellationToken.None).then(data => { for (const [id, resolve] of resolver) { resolve(data[id]); } }); // reset this.requests = new Map(); this.resolver = new Map(); this.timer = undefined; }, 0); } } export class DecorationsMainImpl implements DecorationsMain, Disposable { private readonly proxy: DecorationsExt; private readonly providers = new Map, Disposable]>(); private readonly decorationsService: DecorationsService; constructor(rpc: RPCProtocol, container: interfaces.Container) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.DECORATIONS_EXT); this.decorationsService = container.get(DecorationsService); } dispose(): void { this.providers.forEach(value => value.forEach(v => v.dispose())); this.providers.clear(); } async $registerDecorationProvider(handle: number): Promise { const emitter = new Emitter(); const queue = new DecorationRequestsQueue(this.proxy, handle); const registration = this.decorationsService.registerDecorationsProvider({ onDidChange: emitter.event, provideDecorations: async (uri, token) => { const data = await queue.enqueue(uri, token); if (!data) { return undefined; } const [bubble, tooltip, letter, themeColor] = data; return { weight: 10, bubble: bubble ?? false, colorId: themeColor?.id, tooltip, letter }; } }); this.providers.set(handle, [emitter, registration]); } $onDidChange(handle: number, resources: UriComponents[]): void { const providerSet = this.providers.get(handle); if (providerSet) { const [emitter] = providerSet; emitter.fire(resources && resources.map(r => new URI(VSCodeURI.revive(r).toString()))); } } $unregisterDecorationProvider(handle: number): void { const provider = this.providers.get(handle); if (provider) { provider.forEach(p => p.dispose()); this.providers.delete(handle); } } }