Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add registered ProblemMatchers to tasks schema #6422

Merged
merged 1 commit into from
Oct 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <liang.huang@ericsson.com>
  • Loading branch information
Liang Huang committed Oct 22, 2019
commit 00997764952eff168f4210927fdd7638c7b29772
14 changes: 12 additions & 2 deletions packages/task/src/browser/task-problem-matcher-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -36,11 +37,17 @@ export class ProblemMatcherRegistry {
@inject(ProblemPatternRegistry)
protected readonly problemPatternRegistry: ProblemPatternRegistry;

protected readonly onDidChangeProblemMatcherEmitter = new Emitter<void>();
get onDidChangeProblemMatcher(): Event<void> {
return this.onDidChangeProblemMatcherEmitter.event;
}

@postConstruct()
protected init(): void {
this.problemPatternRegistry.onReady().then(() => {
this.fillDefaults();
this.readyPromise = new Promise<void>((res, rej) => res(undefined));
this.onDidChangeProblemMatcherEmitter.fire(undefined);
});
}

Expand All @@ -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 */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akosyakov this is the part i am not sure about.
could you please give me more information on what /* mark as not disposed */ means here? Thank you !

Copy link
Member

@akosyakov akosyakov Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDisposed returns true if a collection is empty. If we don't add anything on the initialization when checking it returns false, so we push an empty disposable object to indicate that it was not disposed yet.

this.onDidChangeProblemMatcherEmitter.fire(undefined);
}));
this.doRegister(matcher, toDispose).then(() => this.onDidChangeProblemMatcherEmitter.fire(undefined));
return toDispose;
}
protected async doRegister(matcher: ProblemMatcherContribution, toDispose: DisposableCollection): Promise<void> {
Expand Down
55 changes: 42 additions & 13 deletions packages/task/src/browser/task-schema-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<void> {
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<string> {
const taskSchema = {
properties: {
tasks: {
Expand All @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elaihau do you mind documenting this method?
I see that we getAll named problem matchers, reset the current list, and finally push the new list.
I just wanted to understand why it's required and in what scenario.

Copy link
Contributor Author

@elaihau elaihau Oct 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep i will document this function.

This function is called in the callback of this.problemMatcherRegistry.onDidChangeProblemMatcher( ). In other words, every time a problem matcher is registered or disposed, we want to update the task schema to ensure the users get the most up-to-date when they are manually entering the problem matcher(s).

I cannot simply do problemMatcherNames = matcherNames; instead of resetting the length of array, as the reference of problemMatcherNames is assigned to the task schema when theia starts.

const matcherNames = this.problemMatcherRegistry.getAll().map(m => m.name.startsWith('$') ? m.name : `$${m.name}`);
problemMatcherNames.length = 0;
problemMatcherNames.push(...matcherNames);
this.update();
}
}

Expand Down Expand Up @@ -110,6 +132,7 @@ const commandOptionsSchema: IJSONSchema = {
}
};

const problemMatcherNames: string[] = [];
const taskConfigurationSchema: IJSONSchema = {
$id: taskSchemaId,
oneOf: [
Expand Down Expand Up @@ -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',
Expand All @@ -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
}
}
]
Expand Down