Skip to content

Commit

Permalink
wip create problem marker from task output
Browse files Browse the repository at this point in the history
  • Loading branch information
elaihau committed Apr 23, 2019
1 parent 7087a38 commit f44b0d3
Show file tree
Hide file tree
Showing 27 changed files with 1,732 additions and 38 deletions.
36 changes: 35 additions & 1 deletion packages/markers/src/browser/marker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,20 @@ export class MarkerCollection<T> {
if (markerData.length > 0) {
this.owner2Markers.set(owner, markerData.map(data => this.createMarker(owner, data)));
} else {
this.owner2Markers.delete(owner);
this.removeMarkers(owner);
}
return before || [];
}

addMarkers(owner: string, markerData: T[]): Marker<T>[] {
if (markerData.length > 0) {
const existing = this.owner2Markers.get(owner) || [];
const toAdd = markerData.map(data => this.createMarker(owner, data));
this.owner2Markers.set(owner, [...existing, ...toAdd]);
}
return this.owner2Markers.get(owner) || [];
}

protected createMarker(owner: string, data: T): Readonly<Marker<T>> {
return Object.freeze({
uri: this.uri.toString(),
Expand Down Expand Up @@ -95,6 +104,9 @@ export class MarkerCollection<T> {
}
}

removeMarkers(owner: string): void {
this.owner2Markers.delete(owner);
}
}

export interface Uri2MarkerEntry {
Expand Down Expand Up @@ -160,6 +172,22 @@ export abstract class MarkerManager<D extends object> {
return oldMarkers;
}

/*
* adds markers for the given uri and owner with the given data, without touching the exsting markers associated with the same uri and owner.
*/
addMarkers(uri: URI, owner: string, data: D[]): Marker<D>[] {
const uriString = uri.toString();
const collection = this.uri2MarkerCollection.get(uriString) || new MarkerCollection<D>(uri, this.getKind());
const newMarkers = collection.addMarkers(owner, data);
if (collection.empty) {
this.uri2MarkerCollection.delete(uri.toString());
} else {
this.uri2MarkerCollection.set(uriString, collection);
}
this.fireOnDidChangeMarkers(uri);
return newMarkers;
}

/*
* returns all markers that satisfy the given filter.
*/
Expand Down Expand Up @@ -197,4 +225,10 @@ export abstract class MarkerManager<D extends object> {
}
}

cleanMarkersByOwner(owner: string): void {
this.uri2MarkerCollection.forEach((collection, uri) => {
collection.removeMarkers(owner);
this.fireOnDidChangeMarkers(new URI(uri));
});
}
}
7 changes: 7 additions & 0 deletions packages/plugin-ext/src/common/plugin-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ExtPluginApi } from './plugin-ext-api-contribution';
import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema';
import { RecursivePartial } from '@theia/core/lib/common/types';
import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/common/preferences/preference-schema';
import { TaskDefinitionContribution, ProblemMatcherContribution, ProblemPatternContribution } from '@theia/task/lib/common';

export const hostedServicePath = '/services/hostedPlugin';

Expand Down Expand Up @@ -66,6 +67,9 @@ export interface PluginPackageContribution {
keybindings?: PluginPackageKeybinding[];
debuggers?: PluginPackageDebuggersContribution[];
snippets: PluginPackageSnippetsContribution[];
taskDefinitions?: TaskDefinitionContribution[];
problemMatchers?: ProblemMatcherContribution[];
problemPatterns?: ProblemPatternContribution[];
}

export interface PluginPackageViewContainer {
Expand Down Expand Up @@ -359,6 +363,9 @@ export interface PluginContribution {
keybindings?: Keybinding[];
debuggers?: DebuggerContribution[];
snippets?: SnippetContribution[];
taskDefinitions?: TaskDefinitionContribution[];
problemMatchers?: ProblemMatcherContribution[];
problemPatterns?: ProblemPatternContribution[];
}

export interface SnippetContribution {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export class HostedPluginSupport {
}

if (plugin.model.contributes) {
this.contributionHandler.handleContributions(plugin.model.contributes);
this.contributionHandler.handleContributions(plugin.model.contributes, plugin.model.id);
}
}

Expand Down
13 changes: 13 additions & 0 deletions packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { deepClone } from '@theia/core/lib/common/objects';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/common/preferences/preference-schema';
import { RecursivePartial } from '@theia/core/lib/common/types';
import { TaskDefinitionContribution, ProblemMatcherContribution, ProblemPatternContribution } from '@theia/task/lib/common/task-protocol';

namespace nls {
export function localize(key: string, _default: string) {
Expand Down Expand Up @@ -184,6 +185,18 @@ export class TheiaPluginScanner implements PluginScanner {
contributions.debuggers = debuggers;
}

if (rawPlugin.contributes!.taskDefinitions) {
contributions.taskDefinitions = rawPlugin.contributes!.taskDefinitions as TaskDefinitionContribution[];
}

if (rawPlugin.contributes!.problemMatchers) {
contributions.problemMatchers = rawPlugin.contributes!.problemMatchers as ProblemMatcherContribution[];
}

if (rawPlugin.contributes!.problemPatterns) {
contributions.problemPatterns = rawPlugin.contributes!.problemPatterns as ProblemPatternContribution[];
}

contributions.snippets = this.readSnippets(rawPlugin);
return contributions;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { MonacoSnippetSuggestProvider } from '@theia/monaco/lib/browser/monaco-s
import { PluginSharedStyle } from './plugin-shared-style';
import { CommandRegistry } from '@theia/core';
import { BuiltinThemeProvider } from '@theia/core/lib/browser/theming';
import { TaskDefinitionRegistry, ProblemMatcherRegistry, ProblemPatternRegistry } from '@theia/task/lib/common';

@injectable()
export class PluginContributionHandler {
Expand Down Expand Up @@ -60,7 +61,16 @@ export class PluginContributionHandler {
@inject(PluginSharedStyle)
protected readonly style: PluginSharedStyle;

handleContributions(contributions: PluginContribution): void {
@inject(TaskDefinitionRegistry)
protected readonly taskDefinitionRegistry: TaskDefinitionRegistry;

@inject(ProblemMatcherRegistry)
protected readonly problemMatcherRegistry: ProblemMatcherRegistry;

@inject(ProblemPatternRegistry)
protected readonly problemPatternRegistry: ProblemPatternRegistry;

handleContributions(contributions: PluginContribution, modelId: string): void {
if (contributions.configuration) {
this.updateConfigurationSchema(contributions.configuration);
}
Expand Down Expand Up @@ -150,6 +160,18 @@ export class PluginContributionHandler {
});
}
}

if (contributions.taskDefinitions) {
contributions.taskDefinitions.forEach(def => this.taskDefinitionRegistry.register(def, modelId));
}

if (contributions.problemPatterns) {
contributions.problemPatterns.forEach(pattern => this.problemPatternRegistry.register(pattern.name!, pattern));
}

if (contributions.problemMatchers) {
contributions.problemMatchers.forEach(matcher => this.problemMatcherRegistry.register(matcher));
}
}

protected pluginCommandIconId = 0;
Expand Down
6 changes: 4 additions & 2 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1728,12 +1728,14 @@ export class Task {
if (this.taskExecution instanceof ProcessExecution) {
Object.assign(this.taskDefinition, {
type: 'process',
id: this.taskExecution.computeId()
id: this.taskExecution.computeId(),
taskType: this.taskDefinition!.type
});
} else if (this.taskExecution instanceof ShellExecution) {
Object.assign(this.taskDefinition, {
type: 'shell',
id: this.taskExecution.computeId()
id: this.taskExecution.computeId(),
taskType: this.taskDefinition!.type
});
}
}
Expand Down
3 changes: 3 additions & 0 deletions packages/process/src/node/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export abstract class Process {
return this.errorEmitter.event;
}

abstract onData(listener: (buffer: string) => void): void;
abstract onDataClosed(listener: (exitCode: number, signal?: number) => void): void;

protected emitOnStarted() {
this.startEmitter.fire({});
}
Expand Down
8 changes: 8 additions & 0 deletions packages/process/src/node/raw-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ export class RawProcess extends Process {
}
}

onData(listener: (buffer: string) => void): void {
this.output.on('data', listener);
}

onDataClosed(listener: (exitCode: number, signal?: number) => void): void {
this.output.on('close', listener);
}

get pid() {
return this.process.pid;
}
Expand Down
8 changes: 8 additions & 0 deletions packages/process/src/node/terminal-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,12 @@ export class TerminalProcess extends Process {
this.terminal.write(data);
}

onData(listener: (buffer: string) => void): void {
this.terminal.on('data', listener);
}

onDataClosed(listener: (exitCode: number, signal?: number) => void): void {
this.terminal.on('exit', listener);
// this.ringBuffer.getStream().on('close', listener); // TODO check if we should listen to the `close` from the buffer
}
}
3 changes: 2 additions & 1 deletion packages/task/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"@theia/terminal": "^0.5.0",
"@theia/variable-resolver": "^0.5.0",
"@theia/workspace": "^0.5.0",
"jsonc-parser": "^2.0.2"
"jsonc-parser": "^2.0.2",
"vscode-uri": "^1.0.1"
},
"publishConfig": {
"access": "public"
Expand Down
48 changes: 43 additions & 5 deletions packages/task/src/browser/task-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import { inject, injectable } from 'inversify';
import { TaskConfiguration } from '../common/task-protocol';
import { TaskConfiguration, TaskCutomization } from '../common/task-protocol';
import { Disposable, DisposableCollection, ResourceProvider } from '@theia/core/lib/common';
import URI from '@theia/core/lib/common/uri';
import { FileSystemWatcher, FileChangeEvent } from '@theia/filesystem/lib/browser/filesystem-watcher';
Expand Down Expand Up @@ -47,6 +47,8 @@ export class TaskConfigurations implements Disposable {
* For the inner map (i.e., task config map), the key is task label and value TaskConfiguration
*/
protected tasksMap = new Map<string, Map<string, TaskConfiguration>>();
protected taskCustomizations: TaskCutomization[] = [];

protected watchedConfigFileUris: string[] = [];
protected watchersMap = new Map<string, Disposable>(); // map of watchers for task config files, where the key is folder uri

Expand Down Expand Up @@ -173,6 +175,10 @@ export class TaskConfigurations implements Disposable {
this.tasksMap.delete(source);
}

getTaskCustomizations(type: string): TaskCutomization[] {
return this.taskCustomizations.filter(c => c.type === type);
}

/** returns the string uri of where the config file would be, if it existed under a given root directory */
protected getConfigFileUri(rootDir: string): string {
return new URI(rootDir).resolve(this.TASKFILEPATH).resolve(this.TASKFILE).toString();
Expand All @@ -198,23 +204,29 @@ export class TaskConfigurations implements Disposable {
* If reading a config file wasn't successful then does nothing.
*/
protected async refreshTasks(configFileUri: string) {
const tasksConfigsArray = await this.readTasks(configFileUri);
if (tasksConfigsArray) {
const configuredTasksArray = await this.readTasks(configFileUri);
if (configuredTasksArray) {
// only clear tasks map when successful at parsing the config file
// this way we avoid clearing and re-filling it multiple times if the
// user is editing the file in the auto-save mode, having momentarily
// non-parsing JSON.
this.removeTasks(configFileUri);

if (tasksConfigsArray.length > 0) {
if (configuredTasksArray.length > 0) {
const newTaskMap = new Map<string, TaskConfiguration>();
for (const task of tasksConfigsArray) {
for (const task of configuredTasksArray) {
newTaskMap.set(task.label, task);
}
const source = this.getSourceFolderFromConfigUri(configFileUri);
this.tasksMap.set(source, newTaskMap);
}
}

const cutomizations = await this.readTaskCustomizations(configFileUri);
if (cutomizations) {
this.taskCustomizations.length = 0;
this.taskCustomizations = cutomizations;
}
}

/** parses a config file and extracts the tasks launch configurations */
Expand Down Expand Up @@ -301,4 +313,30 @@ export class TaskConfigurations implements Disposable {
private getSourceFolderFromConfigUri(configFileUri: string): string {
return new URI(configFileUri).parent.parent.path.toString();
}

// TODO put the file read logic into a separate function and reuse
protected async readTaskCustomizations(uri: string): Promise<TaskCutomization[] | undefined> {
if (!await this.fileSystem.exists(uri)) {
return undefined;
} else {
try {
const response = await this.fileSystem.resolveContent(uri);

const strippedContent = jsoncparser.stripComments(response.content);
const errors: ParseError[] = [];
const tasks = jsoncparser.parse(strippedContent, errors);

if (errors.length) {
for (const e of errors) {
console.error(`Error parsing ${uri}: error: ${e.error}, length: ${e.length}, offset: ${e.offset}`);
}
} else {
// tslint:disable-next-line:no-any
return (tasks['tasks'] as Array<any>).filter(t => !TaskConfiguration.is(t));
}
} catch (err) {
console.error(`Error(s) reading config file: ${uri}`);
}
}
}
}
16 changes: 15 additions & 1 deletion packages/task/src/browser/task-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import { TaskConfigurations } from './task-configurations';
import { ProvidedTaskConfigurations } from './provided-task-configurations';
import { TaskFrontendContribution } from './task-frontend-contribution';
import { createCommonBindings } from '../common/task-common-module';
import { TaskServer, taskPath } from '../common/task-protocol';
import {
TaskServer, taskPath,
problemMatcherPath, ProblemMatcherRegistry,
problemPatternPath, ProblemPatternRegistry,
taskDefinitionPath, TaskDefinitionRegistry
} from '../common/task-protocol';
import { TaskWatcher } from '../common/task-watcher';
import { bindProcessTaskModule } from './process/process-task-frontend-module';
import { TaskSchemaUpdater } from './task-schema-updater';
Expand All @@ -51,6 +56,15 @@ export default new ContainerModule(bind => {
const taskWatcher = ctx.container.get(TaskWatcher);
return connection.createProxy<TaskServer>(taskPath, taskWatcher.getTaskClient());
}).inSingletonScope();
bind(TaskDefinitionRegistry).toDynamicValue(({ container }) =>
WebSocketConnectionProvider.createProxy(container, taskDefinitionPath)
).inSingletonScope();
bind(ProblemMatcherRegistry).toDynamicValue(({ container }) =>
WebSocketConnectionProvider.createProxy(container, problemMatcherPath)
).inSingletonScope();
bind(ProblemPatternRegistry).toDynamicValue(({ container }) =>
WebSocketConnectionProvider.createProxy(container, problemPatternPath)
).inSingletonScope();

createCommonBindings(bind);

Expand Down
Loading

0 comments on commit f44b0d3

Please sign in to comment.