Compare commits

...

2 commits

Author SHA1 Message Date
Alex Ross 319d2458d0
Add task provider enabled check back in 2021-11-23 11:02:42 +01:00
Alex Dima f3d35c5926
Selective task provider activation using a new onTaskType: activation event 2021-11-22 18:26:41 +01:00
8 changed files with 118 additions and 48 deletions

View file

@ -24,7 +24,7 @@
},
"main": "./out/main",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask"
"onTaskType:grunt"
],
"capabilities": {
"virtualWorkspaces": false,

View file

@ -24,7 +24,7 @@
},
"main": "./out/main",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask"
"onTaskType:gulp"
],
"capabilities": {
"virtualWorkspaces": false,

View file

@ -24,7 +24,7 @@
},
"main": "./out/main",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask"
"onTaskType:jake"
],
"capabilities": {
"virtualWorkspaces": false,

View file

@ -37,7 +37,7 @@
"main": "./out/npmMain",
"browser": "./dist/browser/npmBrowserMain",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask",
"onTaskType:npm",
"onCommand:npm.runScriptFromFolder",
"onLanguage:json",
"workspaceContains:package.json",

View file

@ -63,7 +63,7 @@
"onCommand:javascript.goToProjectConfig",
"onCommand:typescript.goToProjectConfig",
"onCommand:typescript.openTsServerLog",
"onCommand:workbench.action.tasks.runTask",
"onTaskType:typescript",
"onCommand:_typescript.configurePlugin",
"onCommand:_typescript.learnMoreAboutRefactorings",
"onCommand:typescript.fileReferences",

View file

@ -150,7 +150,7 @@ class TaskMap {
this._store.forEach(callback);
}
private getKey(workspaceFolder: IWorkspace | IWorkspaceFolder | string): string {
public static getKey(workspaceFolder: IWorkspace | IWorkspaceFolder | string): string {
let key: string | undefined;
if (Types.isString(workspaceFolder)) {
key = workspaceFolder;
@ -162,7 +162,7 @@ class TaskMap {
}
public get(workspaceFolder: IWorkspace | IWorkspaceFolder | string): Task[] {
const key = this.getKey(workspaceFolder);
const key = TaskMap.getKey(workspaceFolder);
let result: Task[] | undefined = this._store.get(key);
if (!result) {
result = [];
@ -172,7 +172,7 @@ class TaskMap {
}
public add(workspaceFolder: IWorkspace | IWorkspaceFolder | string, ...task: Task[]): void {
const key = this.getKey(workspaceFolder);
const key = TaskMap.getKey(workspaceFolder);
let values = this._store.get(key);
if (!values) {
values = [];
@ -493,6 +493,31 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return this._showIgnoreMessage;
}
private _getActivationEvents(type: string | undefined): string[] {
const result: string[] = [];
result.push('onCommand:workbench.action.tasks.runTask');
if (type) {
// send a specific activation event for this task type
result.push(`onTaskType:${type}`);
} else {
// send activation events for all task types
for (const definition of TaskDefinitionRegistry.all()) {
result.push(`onTaskType:${definition.taskType}`);
}
}
return result;
}
private async _activateTaskProviders(type: string | undefined): Promise<void> {
// We need to first wait for extensions to be registered because we might read
// the `TaskDefinitionRegistry` in case `type` is `undefined`
await this.extensionService.whenInstalledExtensionsRegistered();
await Promise.all(
this._getActivationEvents(type).map(activationEvent => this.extensionService.activateByEvent(activationEvent))
);
}
private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void {
if (!setup) {
setup = this.computeWorkspaceFolderSetup();
@ -575,6 +600,44 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return this._taskSystem.customExecutionComplete(task, result);
}
/**
* Get a subset of workspace tasks that match a certain predicate.
*/
private async _findWorkspaceTasks(predicate: (task: ConfiguringTask | Task, workspaceFolder: IWorkspaceFolder) => boolean): Promise<(ConfiguringTask | Task)[]> {
const result: (ConfiguringTask | Task)[] = [];
const tasks = await this.getWorkspaceTasks();
for (const [, workspaceTasks] of tasks) {
if (workspaceTasks.configurations) {
for (const taskName in workspaceTasks.configurations.byIdentifier) {
const task = workspaceTasks.configurations.byIdentifier[taskName];
if (predicate(task, workspaceTasks.workspaceFolder)) {
result.push(task);
}
}
}
if (workspaceTasks.set) {
for (const task of workspaceTasks.set.tasks) {
if (predicate(task, workspaceTasks.workspaceFolder)) {
result.push(task);
}
}
}
}
return result;
}
private async _findWorkspaceTasksInGroup(group: TaskGroup): Promise<(ConfiguringTask | Task)[]> {
return this._findWorkspaceTasks((task) => {
const taskGroup = task.configurationProperties.group;
if (taskGroup && typeof taskGroup !== 'string') {
return (taskGroup._id === group._id && !!taskGroup.isDefault);
}
return false;
});
}
public async getTask(folder: IWorkspace | IWorkspaceFolder | string, identifier: string | TaskIdentifier, compareId: boolean = false): Promise<Task | undefined> {
if (!(await this.trust())) {
return;
@ -590,6 +653,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (key === undefined) {
return Promise.resolve(undefined);
}
// Try to find the task in the workspace
const requestedFolder = TaskMap.getKey(folder);
const matchedTasks = await this._findWorkspaceTasks((task, workspaceFolder) => {
const taskFolder = TaskMap.getKey(workspaceFolder);
if (taskFolder !== requestedFolder || taskFolder !== USER_TASKS_GROUP_KEY) {
return false;
}
return task.matches(key, compareId);
});
matchedTasks.sort(task => task._source.kind === TaskSourceKind.Extension ? 1 : -1);
if (matchedTasks.length > 0) {
// Nice, we found a configured task!
const task = matchedTasks[0];
if (ConfiguringTask.is(task)) {
return this.tryResolveTask(task);
} else {
return task;
}
}
// We didn't find the task, so we need to ask all resolvers about it
return this.getGroupedTasks().then((map) => {
let values = map.get(folder);
values = values.concat(map.get(USER_TASKS_GROUP_KEY));
@ -606,7 +691,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (!(await this.trust())) {
return;
}
await Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]);
await this._activateTaskProviders(configuringTask.type);
let matchingProvider: ITaskProvider | undefined;
let matchingProviderUnavailable: boolean = false;
for (const [handle, provider] of this._providers) {
@ -690,10 +775,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
public taskTypes(): string[] {
const types: string[] = [];
if (this.isProvideTasksEnabled()) {
for (const [handle] of this._providers) {
const type = this._providerTypes.get(handle);
if (type && this.isTaskProviderEnabled(type)) {
types.push(type);
for (const definition of TaskDefinitionRegistry.all()) {
if (this.isTaskProviderEnabled(definition.taskType)) {
types.push(definition.taskType);
}
}
}
@ -900,7 +984,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher'));
}
private build(): Promise<ITaskSummary> {
private async build(): Promise<ITaskSummary> {
const buildTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Build);
if (buildTasks.length > 0) {
// TODO: If a task was found, execute it and return early
}
return this.getGroupedTasks().then((tasks) => {
let runnable = this.createRunnableTask(tasks, TaskGroup.Build);
if (!runnable || !runnable.task) {
@ -917,7 +1006,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
});
}
private runTest(): Promise<ITaskSummary> {
private async runTest(): Promise<ITaskSummary> {
const cleanTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Test);
if (cleanTasks.length > 0) {
// TODO: If a task was found, execute it and return early
}
return this.getGroupedTasks().then((tasks) => {
let runnable = this.createRunnableTask(tasks, TaskGroup.Test);
if (!runnable || !runnable.task) {
@ -1683,7 +1777,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
private getGroupedTasks(type?: string): Promise<TaskMap> {
const needsRecentTasksMigration = this.needsRecentTasksMigration();
return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => {
return this._activateTaskProviders(type).then(() => {
let validTypes: IStringDictionary<boolean> = Object.create(null);
TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
validTypes['shell'] = true;
@ -2647,30 +2741,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
location: ProgressLocation.Window,
title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...')
};
let promise = this.getWorkspaceTasks().then(tasks => {
const buildTasks: (ConfiguringTask | Task)[] = [];
for (const taskSource of tasks) {
for (const task in taskSource[1].configurations?.byIdentifier) {
if (taskSource[1].configurations) {
const taskGroup: TaskGroup = taskSource[1].configurations.byIdentifier[task].configurationProperties.group as TaskGroup;
if (taskGroup && taskGroup._id === TaskGroup.Build._id && taskGroup.isDefault) {
buildTasks.push(taskSource[1].configurations.byIdentifier[task]);
}
}
}
if (taskSource[1].set) {
for (const task of taskSource[1].set?.tasks) {
const taskGroup: TaskGroup = task.configurationProperties.group as TaskGroup;
if (taskGroup && taskGroup._id === TaskGroup.Build._id && taskGroup.isDefault) {
buildTasks.push(task);
}
}
}
if (buildTasks.length > 0) {
break;
}
}
let promise = (async () => {
const buildTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Build);
async function runSingleBuildTask(task: Task | undefined, problemMatcherOptions: ProblemMatcherRunOptions | undefined, that: AbstractTaskService) {
that.run(task, problemMatcherOptions, TaskRunSource.User).then(undefined, reason => {
@ -2720,7 +2792,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
});
});
});
});
})();
this.progressService.withProgress(options, () => promise);
}

View file

@ -22,8 +22,6 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
static PREFIX = 'task ';
private activationPromise: Promise<void>;
constructor(
@IExtensionService extensionService: IExtensionService,
@ITaskService private taskService: ITaskService,
@ -37,14 +35,9 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
label: localize('noTaskResults', "No matching tasks")
}
});
this.activationPromise = extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask');
}
protected async _getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Promise<Array<IPickerQuickAccessItem | IQuickPickSeparator>> {
// always await extensions
await this.activationPromise;
if (token.isCancellationRequested) {
return [];
}

View file

@ -288,6 +288,11 @@ export const schema: IJSONSchema = {
description: nls.localize('vscode.extension.activationEvents.onStartupFinished', 'An activation event emitted after the start-up finished (after all `*` activated extensions have finished activating).'),
body: 'onStartupFinished'
},
{
label: 'onTaskType',
description: nls.localize('vscode.extension.activationEvents.onTaskType', 'An activation event emitted whenever tasks of a certain type need to be listed or resolved.'),
body: 'onTaskType:${1:taskType}'
},
{
label: 'onFileSystem',
description: nls.localize('vscode.extension.activationEvents.onFileSystem', 'An activation event emitted whenever a file or folder is accessed with the given scheme.'),