UNPKG

@codingame/monaco-vscode-extensions-service-override

Version:

VSCode public API plugged on the monaco editor - extensions service-override

305 lines (301 loc) 13.9 kB
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6'; import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls'; import { ExtensionsRegistry } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionsRegistry'; import { isProposedApiEnabled } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions'; import { joinPath, isEqualOrParent } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources'; import { FileChangeType } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files'; import { IFileService } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files.service'; import { IBrowserWorkbenchEnvironmentService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/environment/browser/environmentService.service'; import { DisposableStore, toDisposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle'; import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri'; import { FileAccess } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network'; import { createLinkElement } from '@codingame/monaco-vscode-api/vscode/vs/base/browser/dom'; import { IWorkbenchThemeService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/themes/common/workbenchThemeService.service'; import { StorageScope, StorageTarget } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage'; import { IStorageService } from '@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service'; import { ExtensionIdentifier } from '@codingame/monaco-vscode-api/vscode/vs/platform/extensions/common/extensions'; import { registerWorkbenchContribution2, WorkbenchPhase } from '@codingame/monaco-vscode-api/vscode/vs/workbench/common/contributions'; import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation'; const CSS_CACHE_STORAGE_KEY = "workbench.contrib.css.cache"; const cssExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: "css", jsonSchema: { description: ( localize(16957, "Contributes CSS files to be loaded in the workbench.")), type: "array", items: { type: "object", properties: { path: { description: ( localize( 16958, "Path to the CSS file. The path is relative to the extension folder." )), type: "string" } }, required: ["path"] }, defaultSnippets: [{ body: [{ path: "${1:styles.css}" }] }] } }); class CSSFileWatcher { constructor(fileService, environmentService, onUpdate) { this.fileService = fileService; this.environmentService = environmentService; this.onUpdate = onUpdate; this.watchedLocations = ( new Map()); } watch(uri) { const key = ( uri.toString()); if (( this.watchedLocations.has(key))) { return; } if (!this.environmentService.isExtensionDevelopment) { return; } const disposables = ( new DisposableStore()); disposables.add(this.fileService.watch(uri)); disposables.add(this.fileService.onDidFilesChange(e => { if (e.contains(uri, FileChangeType.UPDATED)) { this.onUpdate(uri); } })); this.watchedLocations.set(key, { uri, disposables }); } unwatch(uri) { const key = ( uri.toString()); const entry = this.watchedLocations.get(key); if (entry) { entry.disposables.dispose(); this.watchedLocations.delete(key); } } dispose() { for (const entry of ( this.watchedLocations.values())) { entry.disposables.dispose(); } this.watchedLocations.clear(); } } let CSSExtensionPoint = class CSSExtensionPoint { constructor(fileService, environmentService, themeService, storageService) { this.themeService = themeService; this.storageService = storageService; this.disposables = ( new DisposableStore()); this.stylesheetsByExtension = ( new Map()); this.pendingExtensions = ( new Map()); this.watcher = this.disposables.add(( new CSSFileWatcher(fileService, environmentService, uri => this.reloadStylesheet(uri)))); this.disposables.add(toDisposable(() => { for (const entries of ( this.stylesheetsByExtension.values())) { for (const entry of entries) { entry.disposables.dispose(); } } this.stylesheetsByExtension.clear(); })); this.applyCachedCSS(); this.disposables.add(this.themeService.onDidColorThemeChange(() => this.onThemeChange())); this.disposables.add(this.themeService.onDidFileIconThemeChange(() => this.onThemeChange())); this.disposables.add(this.themeService.onDidProductIconThemeChange(() => this.onThemeChange())); cssExtensionPoint.setHandler((extensions, delta) => { for (const extension of delta.removed) { const extensionId = extension.description.identifier.value; this.pendingExtensions.delete(extensionId); this.removeStylesheets(extensionId); this.clearCacheForExtension(extensionId); } for (const extension of delta.added) { if (!isProposedApiEnabled(extension.description)) { extension.collector.error(`The '${cssExtensionPoint.name}' contribution point is proposed API.`); continue; } const extensionValue = extension.value; const collector = extension.collector; if (!extensionValue || !Array.isArray(extensionValue)) { collector.error(( localize(16959, "'contributes.css' must be an array."))); continue; } const extensionId = extension.description.identifier.value; this.pendingExtensions.set(extensionId, extension); if (this.isExtensionThemeActive(extensionId)) { this.activateExtensionCSS(extension); } else if (( this.stylesheetsByExtension.has(extensionId))) { this.removeStylesheets(extensionId); this.clearCacheForExtension(extensionId); } } }); } isExtensionThemeActive(extensionId) { const colorTheme = this.themeService.getColorTheme(); const fileIconTheme = this.themeService.getFileIconTheme(); const productIconTheme = this.themeService.getProductIconTheme(); return !!(colorTheme.extensionData && ExtensionIdentifier.equals(colorTheme.extensionData.extensionId, extensionId)) || !!(fileIconTheme.extensionData && ExtensionIdentifier.equals(fileIconTheme.extensionData.extensionId, extensionId)) || !!(productIconTheme.extensionData && ExtensionIdentifier.equals(productIconTheme.extensionData.extensionId, extensionId)); } onThemeChange() { for (const [extensionId, extension] of this.pendingExtensions) { if (!( this.stylesheetsByExtension.has(extensionId)) && this.isExtensionThemeActive(extensionId)) { this.activateExtensionCSS(extension); } } for (const extensionId of ( this.stylesheetsByExtension.keys())) { if (!this.isExtensionThemeActive(extensionId)) { this.removeStylesheets(extensionId); this.clearCacheForExtension(extensionId); } } } activateExtensionCSS(extension) { const extensionId = extension.description.identifier.value; if (( this.stylesheetsByExtension.has(extensionId))) { return; } const extensionLocation = extension.description.extensionLocation; const extensionValue = extension.value; const collector = extension.collector; const entries = []; const cssLocations = []; for (const cssContribution of extensionValue) { if (!cssContribution.path || typeof cssContribution.path !== "string") { collector.error(( localize(16960, "'contributes.css.path' must be a string."))); continue; } const cssLocation = joinPath(extensionLocation, cssContribution.path); if (!isEqualOrParent(cssLocation, extensionLocation)) { collector.warn(( localize( 16961, "Expected 'contributes.css.path' ({0}) to be included inside extension's folder ({1}).", cssLocation.path, extensionLocation.path ))); continue; } const entryDisposables = ( new DisposableStore()); const element = this.createCSSLinkElement(cssLocation, extensionId, entryDisposables); entries.push({ uri: cssLocation, element, disposables: entryDisposables }); cssLocations.push(( cssLocation.toString())); this.watcher.watch(cssLocation); } if (entries.length > 0) { this.stylesheetsByExtension.set(extensionId, entries); this.cacheExtensionCSS(extensionId, cssLocations); } } removeStylesheets(extensionId) { const entries = this.stylesheetsByExtension.get(extensionId); if (entries) { for (const entry of entries) { this.watcher.unwatch(entry.uri); entry.disposables.dispose(); } this.stylesheetsByExtension.delete(extensionId); } } applyCachedCSS() { const cached = this.getCachedCSS(); if (!cached) { return; } if (!this.isExtensionThemeActive(cached.extensionId)) { this.clearCacheForExtension(cached.extensionId); return; } const entries = []; for (const cssLocationString of cached.cssLocations) { const cssLocation = ( URI.parse(cssLocationString)); const entryDisposables = ( new DisposableStore()); const element = this.createCSSLinkElement(cssLocation, cached.extensionId, entryDisposables); entries.push({ uri: cssLocation, element, disposables: entryDisposables }); this.watcher.watch(cssLocation); } if (entries.length > 0) { this.stylesheetsByExtension.set(cached.extensionId, entries); } } getCachedCSS() { const raw = this.storageService.get(CSS_CACHE_STORAGE_KEY, StorageScope.PROFILE); if (!raw) { return undefined; } try { return JSON.parse(raw); } catch { return undefined; } } cacheExtensionCSS(extensionId, cssLocations) { const entry = { extensionId, cssLocations }; this.storageService.store( CSS_CACHE_STORAGE_KEY, JSON.stringify(entry), StorageScope.PROFILE, StorageTarget.MACHINE ); } clearCacheForExtension(extensionId) { const cached = this.getCachedCSS(); if (cached && ExtensionIdentifier.equals(cached.extensionId, extensionId)) { this.storageService.remove(CSS_CACHE_STORAGE_KEY, StorageScope.PROFILE); } } createCSSLinkElement(uri, extensionId, disposables) { const element = createLinkElement(); element.rel = "stylesheet"; element.type = "text/css"; element.className = `extension-contributed-css ${extensionId}`; element.href = ( FileAccess.uriToBrowserUri(uri).toString(true)); disposables.add(toDisposable(() => element.remove())); return element; } reloadStylesheet(uri) { const uriString = ( uri.toString()); for (const entries of ( this.stylesheetsByExtension.values())) { for (const entry of entries) { if (( entry.uri.toString()) === uriString) { const browserUri = FileAccess.uriToBrowserUri(uri); entry.element.href = ( browserUri.with({ query: `v=${Date.now()}` }).toString(true)); } } } } dispose() { this.disposables.dispose(); } }; CSSExtensionPoint = ( __decorate([( __param(0, IFileService)), ( __param(1, IBrowserWorkbenchEnvironmentService)), ( __param(2, IWorkbenchThemeService)), ( __param(3, IStorageService))], CSSExtensionPoint)); let TokenClassificationExtensionPointWorkbenchContribution = class TokenClassificationExtensionPointWorkbenchContribution { static { this.ID = "workbench.contrib.cssExtensionPoint"; } constructor(instantiationService) { this.instantiationService = instantiationService; this.instantiationService.createInstance(CSSExtensionPoint); } }; TokenClassificationExtensionPointWorkbenchContribution = ( __decorate([( __param(0, IInstantiationService))], TokenClassificationExtensionPointWorkbenchContribution)); registerWorkbenchContribution2( TokenClassificationExtensionPointWorkbenchContribution.ID, TokenClassificationExtensionPointWorkbenchContribution, WorkbenchPhase.BlockStartup ); export { CSSExtensionPoint };