Skip to content

Commit

Permalink
prompt user to choose parser to parse task output
Browse files Browse the repository at this point in the history
- before a task runs, prompt the user to choose which parser to use to
parse the task output, and write user's choice into `tasks.json`
- part of #4212

Signed-off-by: Liang Huang <liang.huang@ericsson.com>
  • Loading branch information
Liang Huang committed Sep 6, 2019
1 parent 4a2b4a5 commit a866f32
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 87 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## v0.11.0

- [task] added `tasks.fetchTasks()` and `tasks.executeTask()` to plugins API [#6058](https://github.com/theia-ide/theia/pull/6058)
- [task] prompt user to choose parser to parse task output [#5877](https://github.com/theia-ide/theia/pull/5877)

Breaking changes:

Expand Down
129 changes: 101 additions & 28 deletions packages/task/src/browser/task-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,27 +324,26 @@ export class TaskConfigurations implements Disposable {
for (const e of errors) {
console.error(`Error parsing ${uri}: error: ${e.error}, length: ${e.length}, offset: ${e.offset}`);
}
}
const rootFolderUri = this.getSourceFolderFromConfigUri(uri);
if (this.rawTaskConfigurations.has(rootFolderUri)) {
this.rawTaskConfigurations.delete(rootFolderUri);
}
if (rawTasks && rawTasks['tasks']) {
const tasks = rawTasks['tasks'].map((t: TaskCustomization | TaskConfiguration) => {
if (this.isDetectedTask(t)) {
const def = this.getTaskDefinition(t);
return Object.assign(t, {
_source: def!.source,
_scope: this.getSourceFolderFromConfigUri(uri)
});
}
return Object.assign(t, { _source: this.getSourceFolderFromConfigUri(uri) });
});
this.rawTaskConfigurations.set(rootFolderUri, tasks);
return tasks;
} else {
const rootFolderUri = this.getSourceFolderFromConfigUri(uri);
if (this.rawTaskConfigurations.has(rootFolderUri)) {
this.rawTaskConfigurations.delete(rootFolderUri);
}
if (rawTasks && rawTasks['tasks']) {
const tasks = rawTasks['tasks'].map((t: TaskCustomization | TaskConfiguration) => {
if (this.isDetectedTask(t)) {
const def = this.getTaskDefinition(t);
return Object.assign(t, {
_source: def!.source,
_scope: this.getSourceFolderFromConfigUri(uri)
});
}
return Object.assign(t, { _source: this.getSourceFolderFromConfigUri(uri) });
});
this.rawTaskConfigurations.set(rootFolderUri, tasks);
return tasks;
} else {
return [];
}
return [];
}
} catch (err) {
console.error(`Error(s) reading config file: ${uri}`);
Expand All @@ -359,13 +358,7 @@ export class TaskConfigurations implements Disposable {
return;
}

const isDetectedTask = this.isDetectedTask(task);
let sourceFolderUri: string | undefined;
if (isDetectedTask) {
sourceFolderUri = task._scope;
} else {
sourceFolderUri = task._source;
}
const sourceFolderUri: string | undefined = this.getSourceFolderUriFromTask(task);
if (!sourceFolderUri) {
console.error('Global task cannot be customized');
return;
Expand Down Expand Up @@ -396,9 +389,25 @@ export class TaskConfigurations implements Disposable {
customization[p] = task[p];
}
});
const problemMatcher: string[] = [];
if (task.problemMatcher) {
if (Array.isArray(task.problemMatcher)) {
problemMatcher.push(...task.problemMatcher.map(t => {
if (typeof t === 'string') {
return t;
} else {
return t.name!;
}
}));
} else if (typeof task.problemMatcher === 'string') {
problemMatcher.push(task.problemMatcher);
} else {
problemMatcher.push(task.problemMatcher.name!);
}
}
return {
...customization,
problemMatcher: []
problemMatcher: problemMatcher.map(name => name.startsWith('$') ? name : `$${name}`)
};
}

Expand Down Expand Up @@ -465,6 +474,59 @@ export class TaskConfigurations implements Disposable {
this.tasksMap = newTaskMap;
}

/**
* saves the names of the problem matchers to be used to parse the output of the given task to `tasks.json`
* @param task task that the problem matcher(s) are applied to
* @param problemMatchers name(s) of the problem matcher(s)
*/
async saveProblemMatcherForTask(task: TaskConfiguration, problemMatchers: string[]): Promise<void> {
const sourceFolderUri: string | undefined = this.getSourceFolderUriFromTask(task);
if (!sourceFolderUri) {
console.error('Global task cannot be customized');
return;
}
const configFileUri = this.getConfigFileUri(sourceFolderUri);
const configuredAndCustomizedTasks = await this.getTasks();
if (configuredAndCustomizedTasks.some(t => this.taskDefinitionRegistry.compareTasks(t, task))) { // task is already in `tasks.json`
try {
const content = (await this.fileSystem.resolveContent(configFileUri)).content;
const errors: ParseError[] = [];
const jsonTasks = jsoncparser.parse(content, errors).tasks;
if (errors.length > 0) {
for (const e of errors) {
console.error(`Error parsing ${configFileUri}: error: ${e.error}, length: ${e.length}, offset: ${e.offset}`);
}
}
if (jsonTasks) {
const ind = jsonTasks.findIndex((t: TaskConfiguration) => {
if (t.type !== (task.taskType || task.type)) {
return false;
}
const def = this.taskDefinitionRegistry.getDefinition(t);
if (def) {
return def.properties.all.every(p => t[p] === task[p]);
}
return t.label === task.label;
});
const newTask = Object.assign(jsonTasks[ind], { problemMatcher: problemMatchers.map(name => name.startsWith('$') ? name : `$${name}`) });
jsonTasks[ind] = newTask;
}
const updatedTasks = JSON.stringify({ tasks: jsonTasks });
const formattingOptions = { tabSize: 4, insertSpaces: true, eol: '' };
const edits = jsoncparser.format(updatedTasks, undefined, formattingOptions);
const updatedContent = jsoncparser.applyEdits(updatedTasks, edits);
const resource = await this.resourceProvider(new URI(configFileUri));
Resource.save(resource, { content: updatedContent });
} catch (e) {
console.error(`Failed to save task configuration for ${task.label} task. ${e.toString()}`);
return;
}
} else { // task is not in `tasks.json`
task.problemMatcher = problemMatchers;
this.saveTask(configFileUri, task);
}
}

private getSourceFolderFromConfigUri(configFileUri: string): string {
return new URI(configFileUri).parent.parent.path.toString();
}
Expand All @@ -482,4 +544,15 @@ export class TaskConfigurations implements Disposable {
type: task.taskType || task.type
});
}

private getSourceFolderUriFromTask(task: TaskConfiguration): string | undefined {
const isDetectedTask = this.isDetectedTask(task);
let sourceFolderUri: string | undefined;
if (isDetectedTask) {
sourceFolderUri = task._scope;
} else {
sourceFolderUri = task._source;
}
return sourceFolderUri;
}
}
12 changes: 12 additions & 0 deletions packages/task/src/browser/task-problem-matcher-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ export class ProblemMatcherRegistry {
return this.matchers[name];
}

/**
* Returns all registered problem matchers in the registry.
*/
getAll(): NamedProblemMatcher[] {
const all: NamedProblemMatcher[] = [];
for (const matcherName of Object.keys(this.matchers)) {
all.push(this.get(matcherName)!);
}
all.sort((one, other) => one.name.localeCompare(other.name));
return all;
}

/**
* Transforms the `ProblemMatcherContribution` to a `ProblemMatcher`
*
Expand Down
Loading

0 comments on commit a866f32

Please sign in to comment.