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

Improve documentation for TaskAPI #8695

Merged
merged 1 commit into from
Nov 6, 2020
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
106 changes: 100 additions & 6 deletions packages/task/src/browser/task-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,79 @@ import { WaitUntilEvent, Emitter } from '@theia/core/lib/common/event';

export const TaskContribution = Symbol('TaskContribution');

/** Allows to contribute custom Task Resolvers, Task Providers. */
/**
* A {@link TaskContribution} allows to contribute custom {@link TaskResolver}s and/or {@link TaskProvider}s.
*
* ### Example usage
* ```typescript
* @injectable()
* export class ProcessTaskContribution implements TaskContribution {
*
* @inject(ProcessTaskResolver)
* protected readonly processTaskResolver: ProcessTaskResolver;
*
* registerResolvers(resolvers: TaskResolverRegistry): void {
* resolvers.register('process', this.processTaskResolver);
* resolvers.register('shell', this.processTaskResolver);
* }
* }
* ```
*/
export interface TaskContribution {
/**
* Register task resolvers using the given `TaskResolverRegistry`.
* @param resolvers the task resolver registry.
*/
registerResolvers?(resolvers: TaskResolverRegistry): void;
/**
* Register task providers using the given `TaskProviderRegistry`.
* @param resolvers the task provider registry.
*/
registerProviders?(providers: TaskProviderRegistry): void;
}

/**
* A {@link TaskResolver} is used to preprocess/resolve a task before sending
* it to the Task Server. For instance, the resolver can be used to add missing information to the configuration
* (e.g default values for optional parameters).
*/
export interface TaskResolver {
/** Resolves a Task Configuration before sending it for execution to the Task Server. */
/**
* Resolves a `TaskConfiguration` before sending it for execution to the `TaskServer` (Backend).
* @param taskConfig the configuration that should be resolved.
*
* @returns a promise of the resolved `TaskConfiguration`.
*/

resolveTask(taskConfig: TaskConfiguration): Promise<TaskConfiguration>;
}

/**
* A {@link TaskProvider} can be used to define the set of tasks that should
* be provided to the system. i.e. that are available for the user to run.
*/
export interface TaskProvider {
/** Returns the Task Configurations which are provides programmatically to the system. */
/**
* Retrieves the task configurations which are provided programmatically to the system.
*
* @returns a promise of the provided tasks configurations.
*/
provideTasks(): Promise<TaskConfiguration[]>;
}

/**
* The {@link TaskResolverRegistry} is the common component for registration and provision of
* {@link TaskResolver}s. Theia will collect all {@link TaskContribution}s and invoke {@link TaskContribution#registerResolvers}
* for each contribution.
*/
@injectable()
export class TaskResolverRegistry {

protected readonly onWillProvideTaskResolverEmitter = new Emitter<WaitUntilEvent>();
/**
* Emit when the registry provides a registered resolver. i.e. when the {@link TaskResolverRegistry#getResolver}
* function is called.
*/
readonly onWillProvideTaskResolver = this.onWillProvideTaskResolverEmitter.event;

protected resolvers: Map<string, TaskResolver>;
Expand All @@ -50,24 +103,47 @@ export class TaskResolverRegistry {
this.resolvers = new Map();
}

/** Registers the given Task Resolver to resolve the Task Configurations of the specified type. */
/**
* Registers the given {@link TaskResolver} to resolve the `TaskConfiguration` of the specified type.
* If there is already a `TaskResolver` registered for the specified type the registration will
* be overwritten with the new value.
* @param type the task configuration type for which the given resolver should be registered.
* @param resolver the task resolver that should be registered.
*
* @returns a `Disposable` that can be invoked to unregister the given resolver
*/
register(type: string, resolver: TaskResolver): Disposable {
this.resolvers.set(type, resolver);
return {
dispose: () => this.resolvers.delete(type)
};
}

/**
* Retrieves the {@link TaskResolver} registered for the given type task configuration type.
* @param type the task configuration type
*
* @returns a promise of the registered `TaskResolver` or `undefined` if no resolver is registered for the given type.
*/
async getResolver(type: string): Promise<TaskResolver | undefined> {
await WaitUntilEvent.fire(this.onWillProvideTaskResolverEmitter, {});
return this.resolvers.get(type);
}
}

/**
* The {@link TaskProviderRegistry} is the common component for registration and provision of
* {@link TaskProvider}s. Theia will collect all {@link TaskContribution}s and invoke {@link TaskContribution#registerProviders}
* for each contribution.
*/
@injectable()
export class TaskProviderRegistry {

protected readonly onWillProvideTaskProviderEmitter = new Emitter<WaitUntilEvent>();
/**
* Emit when the registry provides a registered task provider. i.e. when the {@link TaskProviderRegistry#getProvider}
* function is called.
*/
readonly onWillProvideTaskProvider = this.onWillProvideTaskProviderEmitter.event;

protected providers: Map<string, TaskProvider>;
Expand All @@ -77,7 +153,13 @@ export class TaskProviderRegistry {
this.providers = new Map();
}

/** Registers the given Task Provider to return Task Configurations of the specified type. */
/**
* Registers the given {@link TaskProvider} for task configurations of the specified type
* @param type the task configuration type for which the given provider should be registered.
* @param provider the `TaskProvider` that should be registered.
*
* @returns a `Disposable` that can be invoked to unregister the given resolver.
*/
register(type: string, provider: TaskProvider, handle?: number): Disposable {
const key = handle === undefined ? type : `${type}::${handle}`;
this.providers.set(key, provider);
Expand All @@ -86,12 +168,24 @@ export class TaskProviderRegistry {
};
}

/**
* Retrieves the {@link TaskProvider} registered for the given type task configuration type.
* If there is already a `TaskProvider` registered for the specified type the registration will
* be overwritten with the new value.
* @param type the task configuration type.
*
* @returns a promise of the registered `TaskProvider`` or `undefined` if no provider is registered for the given type.
*/
async getProvider(type: string): Promise<TaskProvider | undefined> {
await WaitUntilEvent.fire(this.onWillProvideTaskProviderEmitter, {});
return this.providers.get(type);
}

/** Returns all registered Task Providers. */
/**
* Retrieve all registered {@link TaskProvider}s.
*
* @returns a promise of all registered {@link TaskProvider}s.
*/
async getProviders(): Promise<TaskProvider[]> {
await WaitUntilEvent.fire(this.onWillProvideTaskProviderEmitter, {});
return [...this.providers.values()];
Expand Down
34 changes: 31 additions & 3 deletions packages/task/src/node/task-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { Task } from './task';

// inspired by process-manager.ts

/**
* The {@link TaskManager} is the common component responsible for managing running tasks.
*/
@injectable()
export class TaskManager implements BackendApplicationContribution {

Expand All @@ -29,12 +32,23 @@ export class TaskManager implements BackendApplicationContribution {
protected readonly tasksPerCtx: Map<string, Task[]> = new Map();
/** each task has this unique task id, for this back-end */
protected id: number = -1;
/**
* Emit when a registered task is deleted.
*/
protected readonly deleteEmitter = new Emitter<number>();

constructor(
@inject(ILogger) @named('task') protected readonly logger: ILogger
) { }

/**
* Registers a new task (in the given context if present). Each registered
* task is considered to be currently running.
* @param task the new task.
* @param ctx the provided context.
*
* @returns the registration id for the given task.
*/
register(task: Task, ctx?: string): number {
const id = ++this.id;
this.tasks.set(id, task);
Expand All @@ -51,13 +65,22 @@ export class TaskManager implements BackendApplicationContribution {
return id;
}

/**
* Try to retrieve the registered task for the given id.
* @param id the task registration id.
*
* @returns the task or `undefined` if no task was registered for the given id.
*/
get(id: number): Task | undefined {
return this.tasks.get(id);
}

/**
* Returns all running tasks. If a context is provided, filter-down to
* only tasks started from that context
* only tasks started from that context.
* @param ctx the task execution context.
*
* @returns all running tasks for the given context or `undefined` if no tasks are registered for the given context.
*/
getTasks(ctx?: string): Task[] | undefined {
if (!ctx) {
Expand All @@ -69,7 +92,10 @@ export class TaskManager implements BackendApplicationContribution {
}
}

/** Deletes a task from the task manager */
/**
* Delete the given task from the task manager.
* @param task the task to delete.
*/
delete(task: Task): void {
this.tasks.delete(task.id);

Expand All @@ -88,7 +114,9 @@ export class TaskManager implements BackendApplicationContribution {
return this.deleteEmitter.event;
}

/** When the application stops, clean-up all ongoing tasks */
/**
* Is triggered on application stop to cleanup all ongoing tasks.
*/
onStop(): void {
this.tasks.forEach((task: Task, id: number) => {
this.logger.debug(`Task Backend application: onStop(): cleaning task id: ${id}`);
Expand Down
49 changes: 42 additions & 7 deletions packages/task/src/node/task-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,36 @@ import { TaskConfiguration } from '../common/task-protocol';

export const TaskRunnerContribution = Symbol('TaskRunnerContribution');

/** Allows to contribute custom Task Runners. */
/** The {@link TaskRunnerContribution} can be used to contribute custom {@link TaskRunner}s. */
export interface TaskRunnerContribution {
/**
* Register custom runners using the given {@link TaskRunnerRegistry}.
* @param runners the common task runner registry.
*/
registerRunner(runners: TaskRunnerRegistry): void;
}

export const TaskRunner = Symbol('TaskRunner');
/** A Task Runner knows how to run and kill a Task of a particular type. */
/**
* A {@link TaskRunner} knows how to run a task configuration of a particular type.
*/
export interface TaskRunner {
/** Runs a task based on the given task configuration. */
run(taskConfig: TaskConfiguration, ctx?: string): Promise<Task>;
/**
* Runs a task based on the given `TaskConfiguration`.
* @param taskConfig the task configuration that should be executed.
* @param ctx the execution context.
*
* @returns a promise of the (currently running) {@link Task}.
*/
run(tskConfig: TaskConfiguration, ctx?: string): Promise<Task>;
}

/**
* The {@link TaskRunnerRegistry} is the common component for the registration and provisioning of
* {@link TaskRunner}s. Theia will collect all {@link TaskRunner}s and invoke {@link TaskRunnerContribution#registerRunner}
* for each contribution. The `TaskServer` will use the runners provided by this registry to execute `TaskConfiguration`s that
* have been triggered by the user.
*/
@injectable()
export class TaskRunnerRegistry {

Expand All @@ -49,21 +67,38 @@ export class TaskRunnerRegistry {
this.runners = new Map();
this.defaultRunner = this.processTaskRunner;
}

/** Registers the given Task Runner to execute the Tasks of the specified type. */
/**
* Registers the given {@link TaskRunner} to execute Tasks of the specified type.
* If there is already a {@link TaskRunner} registered for the specified type the registration will
* be overwritten with the new value.
* @param type the task type for which the given runner should be registered.
* @param runner the task runner that should be registered.
*
* @returns a `Disposable` that can be invoked to unregister the given runner.
*/
registerRunner(type: string, runner: TaskRunner): Disposable {
this.runners.set(type, runner);
return {
dispose: () => this.runners.delete(type)
};
}

/** Returns a Task Runner registered for the specified Task type or a default Task Runner if none. */
/**
* Retrieves the {@link TaskRunner} registered for the specified Task type.
* @param type the task type.
*
* @returns the registered {@link TaskRunner} or a default runner if none is registered for the specified type.
*/
getRunner(type: string): TaskRunner {
const runner = this.runners.get(type);
return runner ? runner : this.defaultRunner;
}

/**
* Derives all task types for which a {@link TaskRunner} is registered.
*
* @returns all derived task types.
*/
getRunnerTypes(): string[] {
return [...this.runners.keys()];
}
Expand Down
26 changes: 22 additions & 4 deletions packages/task/src/node/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,23 @@ import { injectable } from 'inversify';
import { ILogger, Disposable, DisposableCollection, Emitter, Event, MaybePromise } from '@theia/core/lib/common/';
import { TaskManager } from './task-manager';
import { TaskInfo, TaskExitedEvent, TaskConfiguration, TaskOutputEvent } from '../common/task-protocol';

/**
* Represents the options used for running a task.
*/
export interface TaskOptions {
/** The task label */
label: string;
/** The task configuration which should be executed */
config: TaskConfiguration;
/** The optional execution context */
context?: string;
}

/**
* A {@link Task} represents the execution state of a `TaskConfiguration`.
* Implementing classes have to call the {@link Task#fireOutputLine} function
* whenever a new output occurs during the execution.
*/
@injectable()
export abstract class Task implements Disposable {

Expand All @@ -45,7 +55,11 @@ export abstract class Task implements Disposable {
this.toDispose.push(this.outputEmitter);
}

/** Terminates the task. */
/**
* Terminate this task.
*
* @returns a promise that resolves once the task has been properly terminated.
*/
abstract kill(): Promise<void>;

get onExit(): Event<TaskExitedEvent> {
Expand All @@ -64,8 +78,12 @@ export abstract class Task implements Disposable {
protected fireOutputLine(event: TaskOutputEvent): void {
this.outputEmitter.fire(event);
}

/** Returns runtime information about task. */
/**
* Retrieves the runtime information about this task.
* The runtime information computation may happen asynchronous.
*
* @returns (a promise of) the runtime information as `TaskInfo`.
*/
abstract getRuntimeInfo(): MaybePromise<TaskInfo>;

get id(): number {
Expand Down