From 00997764952eff168f4210927fdd7638c7b29772 Mon Sep 17 00:00:00 2001 From: Liang Huang Date: Sun, 20 Oct 2019 22:38:05 -0400 Subject: [PATCH] add registered ProblemMatchers to tasks schema - With this pull request, task schemas are updated on problem matchers being added / disposed, and users are benefited from having content assist while specifying the name(s) of problem matcher(s) in tasks.json. - resolves #6134 Signed-off-by: Liang Huang --- .../browser/task-problem-matcher-registry.ts | 14 ++++- .../task/src/browser/task-schema-updater.ts | 55 ++++++++++++++----- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/packages/task/src/browser/task-problem-matcher-registry.ts b/packages/task/src/browser/task-problem-matcher-registry.ts index eb931dc015529..a884afcde36e6 100644 --- a/packages/task/src/browser/task-problem-matcher-registry.ts +++ b/packages/task/src/browser/task-problem-matcher-registry.ts @@ -20,6 +20,7 @@ *--------------------------------------------------------------------------------------------*/ import { inject, injectable, postConstruct } from 'inversify'; +import { Event, Emitter } from '@theia/core/lib/common'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { ApplyToKind, FileLocationKind, NamedProblemMatcher, Severity, @@ -36,11 +37,17 @@ export class ProblemMatcherRegistry { @inject(ProblemPatternRegistry) protected readonly problemPatternRegistry: ProblemPatternRegistry; + protected readonly onDidChangeProblemMatcherEmitter = new Emitter(); + get onDidChangeProblemMatcher(): Event { + return this.onDidChangeProblemMatcherEmitter.event; + } + @postConstruct() protected init(): void { this.problemPatternRegistry.onReady().then(() => { this.fillDefaults(); this.readyPromise = new Promise((res, rej) => res(undefined)); + this.onDidChangeProblemMatcherEmitter.fire(undefined); }); } @@ -58,8 +65,11 @@ export class ProblemMatcherRegistry { console.error('Only named Problem Matchers can be registered.'); return Disposable.NULL; } - const toDispose = new DisposableCollection(Disposable.create(() => {/* mark as not disposed */ })); - this.doRegister(matcher, toDispose); + const toDispose = new DisposableCollection(Disposable.create(() => { + /* mark as not disposed */ + this.onDidChangeProblemMatcherEmitter.fire(undefined); + })); + this.doRegister(matcher, toDispose).then(() => this.onDidChangeProblemMatcherEmitter.fire(undefined)); return toDispose; } protected async doRegister(matcher: ProblemMatcherContribution, toDispose: DisposableCollection): Promise { diff --git a/packages/task/src/browser/task-schema-updater.ts b/packages/task/src/browser/task-schema-updater.ts index 12c24c73c9e12..d6bb90d5c3df4 100644 --- a/packages/task/src/browser/task-schema-updater.ts +++ b/packages/task/src/browser/task-schema-updater.ts @@ -13,13 +13,14 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { injectable, inject } from 'inversify'; +import { injectable, inject, postConstruct } from 'inversify'; import { JsonSchemaStore } from '@theia/core/lib/browser/json-schema-store'; import { InMemoryResources, deepClone } from '@theia/core/lib/common'; import { IJSONSchema } from '@theia/core/lib/common/json-schema'; import { inputsSchema } from '@theia/variable-resolver/lib/browser/variable-input-schema'; import URI from '@theia/core/lib/common/uri'; import { TaskService } from './task-service'; +import { ProblemMatcherRegistry } from './task-problem-matcher-registry'; export const taskSchemaId = 'vscode://schemas/tasks'; @@ -34,8 +35,31 @@ export class TaskSchemaUpdater { @inject(TaskService) protected readonly taskService: TaskService; + @inject(ProblemMatcherRegistry) + protected readonly problemMatcherRegistry: ProblemMatcherRegistry; + + @postConstruct() + protected init(): void { + this.updateProblemMatcherNames(); + // update problem matcher names in the task schema every time a problem matcher is added or disposed + this.problemMatcherRegistry.onDidChangeProblemMatcher(() => this.updateProblemMatcherNames()); + } + async update(): Promise { + const taskSchemaUri = new URI(taskSchemaId); + const schemaContent = await this.getTaskSchema(); + try { + this.inmemoryResources.update(taskSchemaUri, schemaContent); + } catch (e) { + this.inmemoryResources.add(taskSchemaUri, schemaContent); + this.jsonSchemaStore.registerSchema({ + fileMatch: ['tasks.json'], + url: taskSchemaUri.toString() + }); + } + } + private async getTaskSchema(): Promise { const taskSchema = { properties: { tasks: { @@ -49,17 +73,15 @@ export class TaskSchemaUpdater { }; const taskTypes = await this.taskService.getRegisteredTaskTypes(); taskSchema.properties.tasks.items.oneOf![0].allOf![0].properties!.type.enum = taskTypes; - const taskSchemaUri = new URI(taskSchemaId); - const contents = JSON.stringify(taskSchema); - try { - this.inmemoryResources.update(taskSchemaUri, contents); - } catch (e) { - this.inmemoryResources.add(taskSchemaUri, contents); - this.jsonSchemaStore.registerSchema({ - fileMatch: ['tasks.json'], - url: taskSchemaUri.toString() - }); - } + return JSON.stringify(taskSchema); + } + + /** Gets the most up-to-date names of problem matchers from the registry and update the task schema */ + private updateProblemMatcherNames(): void { + const matcherNames = this.problemMatcherRegistry.getAll().map(m => m.name.startsWith('$') ? m.name : `$${m.name}`); + problemMatcherNames.length = 0; + problemMatcherNames.push(...matcherNames); + this.update(); } } @@ -110,6 +132,7 @@ const commandOptionsSchema: IJSONSchema = { } }; +const problemMatcherNames: string[] = []; const taskConfigurationSchema: IJSONSchema = { $id: taskSchemaId, oneOf: [ @@ -161,6 +184,11 @@ const taskConfigurationSchema: IJSONSchema = { }, problemMatcher: { oneOf: [ + { + type: 'string', + description: 'Name of the problem matcher to parse the output of the task', + enum: problemMatcherNames + }, { type: 'object', description: 'User defined problem matcher(s) to parse the output of the task', @@ -169,7 +197,8 @@ const taskConfigurationSchema: IJSONSchema = { type: 'array', description: 'Name(s) of the problem matcher(s) to parse the output of the task', items: { - type: 'string' + type: 'string', + enum: problemMatcherNames } } ]