Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2

This commit is contained in:
ADS Merger
2020-03-29 01:29:32 +00:00
parent 586ec50916
commit a64304602e
316 changed files with 6524 additions and 11687 deletions

View File

@@ -79,13 +79,11 @@ import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { find } from 'vs/base/common/arrays';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IViewsService } from 'vs/workbench/common/views';
import { ProviderProgressMananger } from 'vs/workbench/contrib/tasks/browser/providerProgressManager';
import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
import { isWorkspaceFolder, TaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG } from 'vs/workbench/contrib/tasks/browser/taskQuickPick';
const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history';
const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail';
const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt';
const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip';
export namespace ConfigureTaskAction {
export const ID = 'workbench.action.tasks.configureTaskRunner';
@@ -137,10 +135,6 @@ interface TaskCustomizationTelemetryEvent {
properties: string[];
}
function isWorkspaceFolder(folder: IWorkspace | IWorkspaceFolder): folder is IWorkspaceFolder {
return 'uri' in folder;
}
class TaskMap {
private _store: Map<string, Task[]> = new Map();
@@ -186,10 +180,6 @@ class TaskMap {
}
}
interface TaskQuickPickEntry extends IQuickPickItem {
task: Task | undefined | null;
}
interface ProblemMatcherDisableMetrics {
type: string;
}
@@ -220,7 +210,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
private _providers: Map<number, ITaskProvider>;
private _providerTypes: Map<number, string>;
protected _taskSystemInfos: Map<string, TaskSystemInfo>;
private _providerProgressManager: ProviderProgressMananger | undefined;
protected _workspaceTasksPromise?: Promise<Map<string, WorkspaceFolderTaskResult>>;
protected _areJsonTasksSupportedPromise: Promise<boolean> = Promise.resolve(false);
@@ -264,7 +253,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
@ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService,
@IRemotePathService private readonly remotePathService: IRemotePathService,
@ITextModelService private readonly textModelResolverService: ITextModelService,
@IPreferencesService private readonly preferencesService: IPreferencesService
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService
) {
super();
@@ -562,39 +552,42 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
});
}
protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean;
private tasksAndGroupedTasks(filter?: TaskFilter): { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } {
if (!this.versionAndEngineCompatible(filter)) {
return { tasks: Promise.resolve<Task[]>([]), grouped: Promise.resolve(new TaskMap()) };
}
const grouped = this.getGroupedTasks(filter ? filter.type : undefined);
const tasks = grouped.then((map) => {
if (!filter || !filter.type) {
return map.all();
public async tryResolveTask(configuringTask: ConfiguringTask): Promise<Task | undefined> {
let matchingProvider: ITaskProvider | undefined;
for (const [handle, provider] of this._providers) {
if (configuringTask.type === this._providerTypes.get(handle)) {
matchingProvider = provider;
break;
}
let result: Task[] = [];
map.forEach((tasks) => {
for (let task of tasks) {
if (ContributedTask.is(task) && task.defines.type === filter.type) {
result.push(task);
} else if (CustomTask.is(task)) {
if (task.type === filter.type) {
result.push(task);
} else {
let customizes = task.customizes();
if (customizes && customizes.type === filter.type) {
result.push(task);
}
}
}
}
});
return result;
});
return { tasks, grouped };
}
if (!matchingProvider) {
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
// Try to resolve the task first
try {
const resolvedTask = await matchingProvider.resolveTask(configuringTask);
if (resolvedTask && (resolvedTask._id === configuringTask._id)) {
return TaskConfig.createCustomTask(resolvedTask, configuringTask);
}
} catch (error) {
// Ignore errors. The task could not be provided by any of the providers.
}
// The task couldn't be resolved. Instead, use the less efficient provideTask.
const tasks = await this.tasks({ type: configuringTask.type });
for (const task of tasks) {
if (task._id === configuringTask._id) {
return TaskConfig.createCustomTask(<ContributedTask>task, configuringTask);
}
}
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean;
public tasks(filter?: TaskFilter): Promise<Task[]> {
if (!this.versionAndEngineCompatible(filter)) {
return Promise.resolve<Task[]>([]);
@@ -624,6 +617,19 @@ 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) {
types.push(type);
}
}
}
return types;
}
public createSorter(): TaskSorter {
return new TaskSorter(this.contextService.getWorkspace() ? this.contextService.getWorkspace().folders : []);
}
@@ -695,6 +701,59 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return this._recentlyUsedTasks;
}
private getFolderFromTaskKey(key: string): string | undefined {
const keyValue: { folder: string | undefined } = JSON.parse(key);
return keyValue.folder;
}
public async readRecentTasks(): Promise<(Task | ConfiguringTask)[]> {
const folderMap: IStringDictionary<IWorkspaceFolder> = Object.create(null);
this.workspaceFolders.forEach(folder => {
folderMap[folder.uri.toString()] = folder;
});
const folderToTasksMap: Map<string, any> = new Map();
const recentlyUsedTasks = this.getRecentlyUsedTasks();
const tasks: (Task | ConfiguringTask)[] = [];
for (const key of recentlyUsedTasks.keys()) {
const folder = this.getFolderFromTaskKey(key);
const task = JSON.parse(recentlyUsedTasks.get(key)!);
if (folder && !folderToTasksMap.has(folder)) {
folderToTasksMap.set(folder, []);
}
if (folder && (folderMap[folder] || (folder === USER_TASKS_GROUP_KEY)) && task) {
folderToTasksMap.get(folder).push(task);
}
}
const readTasksMap: Map<string, (Task | ConfiguringTask)> = new Map();
for (const key of folderToTasksMap.keys()) {
let custom: CustomTask[] = [];
let customized: IStringDictionary<ConfiguringTask> = Object.create(null);
await this.computeTasksForSingleConfig(folderMap[key] ?? this.workspaceFolders[0], {
version: '2.0.0',
tasks: folderToTasksMap.get(key)
}, TaskRunSource.System, custom, customized, folderMap[key] ? TaskConfig.TaskConfigSource.TasksJson : TaskConfig.TaskConfigSource.User, true);
custom.forEach(task => {
const taskKey = task.getRecentlyUsedKey();
if (taskKey) {
readTasksMap.set(taskKey, task);
}
});
for (const configuration in customized) {
const taskKey = customized[configuration].getRecentlyUsedKey();
if (taskKey) {
readTasksMap.set(taskKey, customized[configuration]);
}
}
}
for (const key of recentlyUsedTasks.keys()) {
if (readTasksMap.has(key)) {
tasks.push(readTasksMap.get(key)!);
}
}
return tasks;
}
private setTaskLRUCacheLimit() {
const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
if (this._recentlyUsedTasks) {
@@ -719,11 +778,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (quickOpenHistoryLimit === 0) {
return;
}
let values = this._recentlyUsedTasks.toJSON();
if (values.length > quickOpenHistoryLimit) {
values = values.slice(0, quickOpenHistoryLimit);
let keys = this._recentlyUsedTasks.keys();
if (keys.length > quickOpenHistoryLimit) {
keys = keys.slice(0, quickOpenHistoryLimit);
}
this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(values), StorageScope.WORKSPACE);
const keyValues: [string, string][] = [];
for (const key of keys) {
keyValues.push([key, this._recentlyUsedTasks.get(key)!]);
}
this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE);
}
private openDocumentation(): void {
@@ -764,22 +827,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
});
}
public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System, grouped?: Promise<TaskMap>): Promise<ITaskSummary | undefined> {
public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise<ITaskSummary | undefined> {
if (!task) {
throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound);
}
return (grouped ?? this.getGroupedTasks()).then((grouped) => {
let resolver = this.createResolver(grouped);
return new Promise<ITaskSummary | undefined>(async (resolve) => {
let resolver = this.createResolver();
if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) {
return this.attachProblemMatcher(task).then((toExecute) => {
if (toExecute) {
return this.executeTask(toExecute, resolver);
} else {
return Promise.resolve(undefined);
}
});
const toExecute = await this.attachProblemMatcher(task);
if (toExecute) {
resolve(this.executeTask(toExecute, resolver));
} else {
resolve(undefined);
}
}
return this.executeTask(task, resolver);
resolve(this.executeTask(task, resolver));
}).then((value) => {
if (runSource === TaskRunSource.User) {
this.getWorkspaceTasks().then(workspaceTasks => {
@@ -1051,6 +1114,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) {
toCustomize.problemMatcher = [];
}
if (task._source.label !== 'Workspace') {
toCustomize.label = task.configurationProperties.identifier;
} else {
toCustomize.label = task._label;
}
toCustomize.detail = task.configurationProperties.detail;
return toCustomize;
}
@@ -1234,7 +1303,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
});
let resolver: ITaskResolver = {
resolve: (uri: URI | string, alias: string) => {
resolve: async (uri: URI | string, alias: string) => {
let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString());
if (!data) {
return undefined;
@@ -1274,34 +1343,37 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
}
private createResolver(grouped: TaskMap): ITaskResolver {
private createResolver(grouped?: TaskMap): ITaskResolver {
interface ResolverData {
label: Map<string, Task>;
identifier: Map<string, Task>;
taskIdentifier: Map<string, Task>;
}
let resolverData: Map<string, ResolverData> = new Map();
grouped.forEach((tasks, folder) => {
let data = resolverData.get(folder);
if (!data) {
data = { label: new Map<string, Task>(), identifier: new Map<string, Task>(), taskIdentifier: new Map<string, Task>() };
resolverData.set(folder, data);
}
for (let task of tasks) {
data.label.set(task._label, task);
if (task.configurationProperties.identifier) {
data.identifier.set(task.configurationProperties.identifier, task);
}
let keyedIdentifier = task.getDefinition(true);
if (keyedIdentifier !== undefined) {
data.taskIdentifier.set(keyedIdentifier._key, task);
}
}
});
let resolverData: Map<string, ResolverData> | undefined;
return {
resolve: (uri: URI | string, identifier: string | TaskIdentifier | undefined) => {
resolve: async (uri: URI | string, identifier: string | TaskIdentifier | undefined) => {
if (resolverData === undefined) {
resolverData = new Map();
(grouped || await this.getGroupedTasks()).forEach((tasks, folder) => {
let data = resolverData!.get(folder);
if (!data) {
data = { label: new Map<string, Task>(), identifier: new Map<string, Task>(), taskIdentifier: new Map<string, Task>() };
resolverData!.set(folder, data);
}
for (let task of tasks) {
data.label.set(task._label, task);
if (task.configurationProperties.identifier) {
data.identifier.set(task.configurationProperties.identifier, task);
}
let keyedIdentifier = task.getDefinition(true);
if (keyedIdentifier !== undefined) {
data.taskIdentifier.set(keyedIdentifier._key, task);
}
}
});
}
let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString());
if (!data || !identifier) {
return undefined;
@@ -1397,7 +1469,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
this.modelService, this.configurationResolverService, this.telemetryService,
this.contextService, this.environmentService,
AbstractTaskService.OutputChannelId, this.fileService, this.terminalInstanceService,
this.remotePathService,
this.remotePathService, this.viewDescriptorService,
(workspaceFolder: IWorkspaceFolder) => {
if (!workspaceFolder) {
return undefined;
@@ -1409,29 +1481,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
protected abstract getTaskSystem(): ITaskSystem;
private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary<boolean>): Promise<TaskSet | undefined> {
return new Promise<TaskSet>(async (resolve, reject) => {
let isDone = false;
let disposable: IDisposable | undefined;
const providePromise = provider.provideTasks(validTypes);
this._providerProgressManager?.addProvider(type, providePromise);
disposable = this._providerProgressManager?.canceled.token.onCancellationRequested(() => {
if (!isDone) {
resolve();
}
});
providePromise.then((value) => {
isDone = true;
disposable?.dispose();
resolve(value);
}, (e) => {
isDone = true;
disposable?.dispose();
reject(e);
});
});
}
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(() => {
@@ -1439,7 +1488,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
validTypes['shell'] = true;
validTypes['process'] = true;
this._providerProgressManager = new ProviderProgressMananger();
return new Promise<TaskSet[]>(resolve => {
let result: TaskSet[] = [];
let counter: number = 0;
@@ -1472,7 +1520,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
for (const [handle, provider] of this._providers) {
if ((type === undefined) || (type === this._providerTypes.get(handle))) {
counter++;
this.provideTasksWithWarning(provider, this._providerTypes.get(handle)!, validTypes).then(done, error);
provider.provideTasks(validTypes).then(done, error);
}
}
} else {
@@ -1557,6 +1605,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
const unUsedConfigurationPromises = unUsedConfigurationsAsArray.map(async (value) => {
let configuringTask = configurations!.byIdentifier[value];
if (type && (type !== configuringTask.configures.type)) {
return;
}
for (const [handle, provider] of this._providers) {
if (configuringTask.type === this._providerTypes.get(handle)) {
@@ -1781,13 +1832,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false };
}
private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary<ConfiguringTask>, source: TaskConfig.TaskConfigSource): Promise<boolean> {
private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.ExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary<ConfiguringTask>, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise<boolean> {
if (!config) {
return false;
}
let taskSystemInfo: TaskSystemInfo | undefined = workspaceFolder ? this._taskSystemInfos.get(workspaceFolder.uri.scheme) : undefined;
let problemReporter = new ProblemReporter(this._outputChannel);
let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source);
let parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, isRecentTask);
let hasErrors = false;
if (!parseResult.validationStatus.isOK()) {
this.showOutput(runSource);
@@ -2031,12 +2082,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => {
let entryLabel = task._label;
let commonKey = task._id.split('|')[0];
if (count[commonKey]) {
entryLabel = entryLabel + ' (' + count[commonKey].toString() + ')';
count[commonKey]++;
if (count[task._id]) {
entryLabel = entryLabel + ' (' + count[task._id].toString() + ')';
count[task._id]++;
} else {
count[commonKey] = 1;
count[task._id] = 1;
}
return { label: entryLabel, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined };
@@ -2108,6 +2158,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
return entries;
}
private async showTwoLevelQuickPick(placeHolder: string, defaultEntry?: TaskQuickPickEntry) {
return TaskQuickPick.show(this, this.configurationService, this.quickInputService, placeHolder, defaultEntry);
}
private async showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, additionalEntries?: TaskQuickPickEntry[]): Promise<TaskQuickPickEntry | undefined | null> {
const tokenSource = new CancellationTokenSource();
const cancellationToken: CancellationToken = tokenSource.token;
@@ -2160,32 +2214,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
});
picker.busy = true;
const progressManager = this._providerProgressManager;
const progressTimeout = setTimeout(() => {
if (progressManager) {
progressManager.showProgress = (stillProviding, total) => {
let message = undefined;
if (stillProviding.length > 0) {
message = nls.localize('pickProgressManager.description', 'Detecting tasks ({0} of {1}): {2} in progress', total - stillProviding.length, total, stillProviding.join(', '));
}
picker.description = message;
};
progressManager.addOnDoneListener(() => {
picker.focusOnInput();
picker.customButton = false;
});
if (!progressManager.isDone) {
picker.customLabel = nls.localize('taskQuickPick.cancel', "Stop detecting");
picker.onDidCustom(() => {
this._providerProgressManager?.cancel();
});
picker.customButton = true;
}
}
}, 1000);
pickEntries.then(entries => {
clearTimeout(progressTimeout);
progressManager?.dispose();
picker.busy = false;
picker.items = entries;
});
@@ -2262,12 +2291,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
let identifier = this.getTaskIdentifier(arg);
if (identifier !== undefined) {
this.getGroupedTasks().then((grouped) => {
this.getGroupedTasks().then(async (grouped) => {
let resolver = this.createResolver(grouped);
let folders: (IWorkspaceFolder | string)[] = this.contextService.getWorkspace().folders;
folders = folders.concat([USER_TASKS_GROUP_KEY]);
for (let folder of folders) {
let task = resolver.resolve(typeof folder === 'string' ? folder : folder.uri, identifier);
let task = await resolver.resolve(typeof folder === 'string' ? folder : folder.uri, identifier);
if (task) {
this.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
@@ -2286,26 +2315,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
private doRunTaskCommand(tasks?: Task[]): void {
this.showIgnoredFoldersMessage().then(() => {
let taskResult: { tasks: Promise<Task[]>, grouped: Promise<TaskMap> } | undefined = undefined;
if (!tasks) {
taskResult = this.tasksAndGroupedTasks();
}
this.showQuickPick(tasks ? tasks : taskResult!.tasks,
this.showTwoLevelQuickPick(
nls.localize('TaskService.pickRunTask', 'Select the task to run'),
{
label: nls.localize('TaskService.noEntryToRun', 'No task to run found. Configure Tasks...'),
label: nls.localize('TaskService.noEntryToRun', 'No configured tasks. Configure Tasks...'),
task: null
},
true).
then((entry) => {
let task: Task | undefined | null = entry ? entry.task : undefined;
}).
then((task) => {
if (task === undefined) {
return;
}
if (task === null) {
this.runConfigureTasks();
} else {
this.run(task, { attachProblemMatcher: true }, TaskRunSource.User, taskResult?.grouped).then(undefined, reason => {
this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
}
@@ -2661,7 +2684,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
}
public getTaskDescription(task: Task): string | undefined {
public getTaskDescription(task: Task | ConfiguringTask): string | undefined {
let description: string | undefined;
if (task._source.kind === TaskSourceKind.User) {
description = nls.localize('taskQuickPick.userSettings', 'User Settings');