From f1c48e568cd7179a0c8bbd1fddec9a94755c5e17 Mon Sep 17 00:00:00 2001 From: I060847 Date: Mon, 13 Apr 2020 19:25:23 +0300 Subject: [PATCH] fix: 3983 support contributes.jsonValidation VSCode API. Signed-off-by: I060847 --- CHANGELOG.md | 3 ++ .../src/browser/json-client-contribution.ts | 19 +++++---- .../json/src/browser/json-frontend-module.ts | 2 + .../src/browser/json-validation-registry.ts | 39 +++++++++++++++++++ packages/plugin-ext/compile.tsconfig.json | 3 ++ packages/plugin-ext/package.json | 1 + .../plugin-ext/src/common/plugin-protocol.ts | 12 ++++++ .../src/hosted/node/scanners/scanner-theia.ts | 36 ++++++++++++++++- .../browser/plugin-contribution-handler.ts | 15 +++++++ 9 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 packages/json/src/browser/json-validation-registry.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b245ea8501a47..20cf1dd7e7f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - [task] fixed presentation.reveal & focus for detected tasks [#7548](https://github.com/eclipse-theia/theia/pull/7548) +- [json] support `contributes.jsonValidation` VSCode API. [#7545](https://github.com/eclipse-theia/theia/pull/7560) + Breaking changes: - [core] `CommandRegistry.registerHandler` registers a new handler with a higher priority than previous [#7539](https://github.com/eclipse-theia/theia/pull/7539) @@ -14,6 +16,7 @@ Use `PluginManager.configStorage` property instead. [#7265](https://github.com/e ## v1.0.0 +- [core] `CommandRegistry#getAllHandlers` returns with the reversed order of handlers, so if a command has multiple handlers, any handler is considered to be more specific than the other subsequent handlers in the array. In other words, if `@theia/core` contributes a handler for a particular command and your extension also contributes a handler for the same command, the handler from your extension will have `0` index, and the handler from `@theia/core` will have `1` index when calling `getAllHandlers`. - [core] added functionality to ensure that nodes are refreshed properly on tree expansion [#7400](https://github.com/eclipse-theia/theia/pull/7400) - [core] added loading state for trees [#7249](https://github.com/eclipse-theia/theia/pull/7249) - [core] added the ability to customize the layout of view-containers [#6655](https://github.com/eclipse-theia/theia/pull/6655) diff --git a/packages/json/src/browser/json-client-contribution.ts b/packages/json/src/browser/json-client-contribution.ts index f3ff43d151240..42b8df6dee299 100644 --- a/packages/json/src/browser/json-client-contribution.ts +++ b/packages/json/src/browser/json-client-contribution.ts @@ -29,6 +29,7 @@ import URI from '@theia/core/lib/common/uri'; import { JsonPreferences } from './json-preferences'; import { JsonSchemaStore } from '@theia/core/lib/browser/json-schema-store'; import { Endpoint } from '@theia/core/lib/browser'; +import { JsonValidationContributionsRegistry } from './json-validation-registry'; @injectable() export class JsonClientContribution extends BaseLanguageClientContribution { @@ -42,18 +43,22 @@ export class JsonClientContribution extends BaseLanguageClientContribution { @inject(Languages) protected readonly languages: Languages, @inject(LanguageClientFactory) protected readonly languageClientFactory: LanguageClientFactory, @inject(JsonPreferences) protected readonly preferences: JsonPreferences, - @inject(JsonSchemaStore) protected readonly jsonSchemaStore: JsonSchemaStore - ) { + @inject(JsonSchemaStore) protected readonly jsonSchemaStore: JsonSchemaStore, + @inject(JsonValidationContributionsRegistry) protected readonly jsonValidationContributionsRegistry: JsonValidationContributionsRegistry +) { super(workspace, languages, languageClientFactory); this.initializeJsonSchemaAssociations(); } protected updateSchemas(client: ILanguageClient): void { - const allConfigs = [...this.jsonSchemaStore.getJsonSchemaConfigurations()]; - const config = this.preferences['json.schemas']; - if (config instanceof Array) { - allConfigs.push(...config); - } + const schemaStoreConfigs = this.jsonSchemaStore.getJsonSchemaConfigurations(); + const pluginContributionConfigs = this.jsonValidationContributionsRegistry.getJsonValidations(); + const preferencesConfigs = this.preferences['json.schemas'] instanceof Array ? this.preferences['json.schemas'] : []; + + // The order of combining the schema configs is very important as it implies a precedence. + // Given multiple *identical** fileMatch properties, The **last** schema will take precedence. + // This means: schemaStore < pluginContribution < preferences + const allConfigs = [...schemaStoreConfigs, ...pluginContributionConfigs, ...preferencesConfigs]; const registry: { [pattern: string]: string[] } = {}; for (const s of allConfigs) { if (s.fileMatch) { diff --git a/packages/json/src/browser/json-frontend-module.ts b/packages/json/src/browser/json-frontend-module.ts index 74419b567a8d4..ab3563677643d 100644 --- a/packages/json/src/browser/json-frontend-module.ts +++ b/packages/json/src/browser/json-frontend-module.ts @@ -18,9 +18,11 @@ import { ContainerModule } from 'inversify'; import { LanguageClientContribution } from '@theia/languages/lib/browser'; import { JsonClientContribution } from './json-client-contribution'; import { bindJsonPreferences } from './json-preferences'; +import { JsonValidationContributionsRegistry } from './json-validation-registry'; export default new ContainerModule(bind => { bindJsonPreferences(bind); bind(LanguageClientContribution).to(JsonClientContribution).inSingletonScope(); + bind(JsonValidationContributionsRegistry).toSelf().inSingletonScope(); }); diff --git a/packages/json/src/browser/json-validation-registry.ts b/packages/json/src/browser/json-validation-registry.ts new file mode 100644 index 0000000000000..4de12c6da5822 --- /dev/null +++ b/packages/json/src/browser/json-validation-registry.ts @@ -0,0 +1,39 @@ +/******************************************************************************** + * Copyright (c) 2020 SAP SE or an SAP affiliate company 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 WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { injectable } from 'inversify'; +import { Disposable } from '@theia/core/lib/common/disposable'; +import { deepClone } from '@theia/core'; +import { JsonSchemaConfiguration } from '@theia/core/lib/browser/json-schema-store'; + +@injectable() +/** + * Holds JSON Validation contributions from plugins / VSCode extensions. + * - https://code.visualstudio.com/api/references/contribution-points#contributes.jsonValidation + */ +export class JsonValidationContributionsRegistry { + + protected readonly registry: JsonSchemaConfiguration[] = []; + + registerJsonValidation(jsonValidationEntry: JsonSchemaConfiguration): Disposable { + this.registry.push(jsonValidationEntry); + return Disposable.NULL; + } + + getJsonValidations(): JsonSchemaConfiguration[] { + return deepClone(this.registry); + } +} diff --git a/packages/plugin-ext/compile.tsconfig.json b/packages/plugin-ext/compile.tsconfig.json index 977aecf50ed70..3fec2016b6fd7 100644 --- a/packages/plugin-ext/compile.tsconfig.json +++ b/packages/plugin-ext/compile.tsconfig.json @@ -70,6 +70,9 @@ }, { "path": "../callhierarchy/compile.tsconfig.json" + }, + { + "path": "../json/compile.tsconfig.json" } ] } diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 030bf5393bad3..ed7ca265ca103 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -11,6 +11,7 @@ "@theia/editor": "^1.0.0", "@theia/file-search": "^1.0.0", "@theia/filesystem": "^1.0.0", + "@theia/json": "^1.0.0", "@theia/languages": "^1.0.0", "@theia/markers": "^1.0.0", "@theia/messages": "^1.0.0", diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts index a43af8403a4e4..3f52c72eddac2 100644 --- a/packages/plugin-ext/src/common/plugin-protocol.ts +++ b/packages/plugin-ext/src/common/plugin-protocol.ts @@ -78,6 +78,7 @@ export interface PluginPackageContribution { keybindings?: PluginPackageKeybinding | PluginPackageKeybinding[]; debuggers?: PluginPackageDebuggersContribution[]; snippets: PluginPackageSnippetsContribution[]; + jsonValidation?: PluginJsonValidationContribution[]; themes?: PluginThemeContribution[]; iconThemes?: PluginIconThemeContribution[]; colors?: PluginColorContribution[]; @@ -139,6 +140,11 @@ export interface PluginPackageSnippetsContribution { path?: string; } +export interface PluginJsonValidationContribution { + fileMatch: string | string[], + url: string +} + export interface PluginColorContribution { id?: string; description?: string; @@ -469,6 +475,7 @@ export interface PluginContribution { keybindings?: Keybinding[]; debuggers?: DebuggerContribution[]; snippets?: SnippetContribution[]; + jsonValidations?: JsonValidationContribution[]; themes?: ThemeContribution[]; iconThemes?: IconThemeContribution[]; colors?: ColorDefinition[]; @@ -483,6 +490,11 @@ export interface SnippetContribution { language?: string } +export interface JsonValidationContribution { + fileMatch: string, + url: string +} + export type UiTheme = 'vs' | 'vs-dark' | 'hc-black'; export interface ThemeContribution { diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 66df74e63b3e7..10db405ffe92f 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -45,7 +45,8 @@ import { PluginCommand, IconUrl, ThemeContribution, - IconThemeContribution + IconThemeContribution, + JsonValidationContribution } from '../../../common/plugin-protocol'; import * as fs from 'fs'; import * as path from 'path'; @@ -277,6 +278,12 @@ export class TheiaPluginScanner implements PluginScanner { console.error(`Could not read '${rawPlugin.name}' contribution 'snippets'.`, rawPlugin.contributes!.snippets, err); } + try { + contributions.jsonValidations = this.readJsonValidations(rawPlugin); + } catch (err) { + console.error(`Could not read '${rawPlugin.name}' contribution 'jsonValidation'.`, rawPlugin.contributes!.jsonValidation, err); + } + try { contributions.themes = this.readThemes(rawPlugin); } catch (err) { @@ -413,6 +420,33 @@ export class TheiaPluginScanner implements PluginScanner { return result; } + protected readJsonValidations(pck: PluginPackage): JsonValidationContribution[] | undefined { + if (!pck.contributes || !pck.contributes.jsonValidation) { + return undefined; + } + const result: JsonValidationContribution[] = []; + const addSingleValidation = (fileMatch: string, url: string) => { + if (typeof fileMatch === 'string') { + result.push({ + fileMatch: fileMatch, + url: url + }); + } + }; + for (const contribution of pck.contributes.jsonValidation) { + if (typeof contribution.url === 'string') { + if (typeof contribution.fileMatch === 'string') { + addSingleValidation(contribution.fileMatch, contribution.url); + } else if (Array.isArray(contribution.fileMatch)) { + contribution.fileMatch.forEach(fileMatchItem => { + addSingleValidation(fileMatchItem, contribution.url); + }); + } + } + } + return result; + } + protected readJson(filePath: string): T | undefined { const content = this.readFileSync(filePath); return content ? jsoncparser.parse(content, undefined, { disallowComments: false }) : undefined; diff --git a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts index d3d6abd6ed649..4fadbad09a1f4 100644 --- a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts @@ -34,6 +34,7 @@ import { DebugSchemaUpdater } from '@theia/debug/lib/browser/debug-schema-update import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { PluginIconThemeService } from './plugin-icon-theme-service'; +import { JsonValidationContributionsRegistry } from '@theia/json/lib/browser/json-validation-registry'; @injectable() export class PluginContributionHandler { @@ -61,6 +62,9 @@ export class PluginContributionHandler { @inject(MonacoSnippetSuggestProvider) protected readonly snippetSuggestProvider: MonacoSnippetSuggestProvider; + @inject(JsonValidationContributionsRegistry) + protected readonly jsonValidationContributionsRegistry: JsonValidationContributionsRegistry; + @inject(CommandRegistry) protected readonly commands: CommandRegistry; @@ -254,6 +258,17 @@ export class PluginContributionHandler { } } + if (contributions.jsonValidations) { + for (const jsonValidation of contributions.jsonValidations) { + pushContribution( + `jsonValidation.${jsonValidation.url}`, + () => this.jsonValidationContributionsRegistry.registerJsonValidation( { + url: jsonValidation.url, + fileMatch: [jsonValidation.fileMatch] + })); + } + } + if (contributions.themes && contributions.themes.length) { const pending = {}; for (const theme of contributions.themes) {