private async createTerminal()

in src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts [895:1027]


	private async createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder): Promise<[ITerminalInstance | undefined, string | undefined, TaskError | undefined]> {
		let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform;
		let options = this.resolveOptions(resolver, task.command.options);

		let waitOnExit: boolean | string = false;
		const presentationOptions = task.command.presentation;
		if (!presentationOptions) {
			throw new Error('Task presentation options should not be undefined here.');
		}

		if (presentationOptions.reveal !== RevealKind.Never || !task.configurationProperties.isBackground) {
			if (presentationOptions.panel === PanelKind.New) {
				waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.');
			} else if (presentationOptions.showReuseMessage) {
				waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.');
			} else {
				waitOnExit = true;
			}
		}

		let commandExecutable: string | undefined;
		let command: CommandString | undefined;
		let args: CommandString[] | undefined;
		let launchConfigs: IShellLaunchConfig | undefined;

		if (task.command.runtime === RuntimeType.CustomExecution) {
			this.currentTask.shellLaunchConfig = launchConfigs = {
				isRendererOnly: true,
				waitOnExit,
				name: this.createTerminalName(task, workspaceFolder),
				initialText: task.command.presentation && task.command.presentation.echo ? `\x1b[1m> Executing task: ${task._label} <\x1b[0m\n` : undefined
			};
		} else if (task.command.runtime === RuntimeType.CustomExecution2) {
			this.currentTask.shellLaunchConfig = launchConfigs = {
				isVirtualProcess: true,
				waitOnExit,
				name: this.createTerminalName(task, workspaceFolder),
				initialText: task.command.presentation && task.command.presentation.echo ? `\x1b[1m> Executing task: ${task._label} <\x1b[0m\n` : undefined
			};
		} else {
			let resolvedResult: { command: CommandString, args: CommandString[] } = this.resolveCommandAndArgs(resolver, task.command);
			command = resolvedResult.command;
			args = resolvedResult.args;
			commandExecutable = CommandString.value(command);

			this.currentTask.shellLaunchConfig = launchConfigs = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : await this.createShellLaunchConfig(task, workspaceFolder, resolver, platform, options, command, args, waitOnExit);
			if (launchConfigs === undefined) {
				return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)];
			}
		}

		let prefersSameTerminal = presentationOptions.panel === PanelKind.Dedicated;
		let allowsSharedTerminal = presentationOptions.panel === PanelKind.Shared;
		let group = presentationOptions.group;

		let taskKey = task.getMapKey();
		let terminalToReuse: TerminalData | undefined;
		if (prefersSameTerminal) {
			let terminalId = this.sameTaskTerminals[taskKey];
			if (terminalId) {
				terminalToReuse = this.terminals[terminalId];
				delete this.sameTaskTerminals[taskKey];
			}
		} else if (allowsSharedTerminal) {
			// Always allow to reuse the terminal previously used by the same task.
			let terminalId = this.idleTaskTerminals.remove(taskKey);
			if (!terminalId) {
				// There is no idle terminal which was used by the same task.
				// Search for any idle terminal used previously by a task of the same group
				// (or, if the task has no group, a terminal used by a task without group).
				for (const taskId of this.idleTaskTerminals.keys()) {
					const idleTerminalId = this.idleTaskTerminals.get(taskId)!;
					if (idleTerminalId && this.terminals[idleTerminalId] && this.terminals[idleTerminalId].group === group) {
						terminalId = this.idleTaskTerminals.remove(taskId);
						break;
					}
				}
			}
			if (terminalId) {
				terminalToReuse = this.terminals[terminalId];
			}
		}
		if (terminalToReuse) {
			if (!launchConfigs) {
				throw new Error('Task shell launch configuration should not be undefined here.');
			}

			terminalToReuse.terminal.reuseTerminal(launchConfigs);

			if (task.command.presentation && task.command.presentation.clear) {
				terminalToReuse.terminal.clear();
			}
			this.terminals[terminalToReuse.terminal.id.toString()].lastTask = taskKey;
			return [terminalToReuse.terminal, commandExecutable, undefined];
		}

		let result: ITerminalInstance | null = null;
		if (group) {
			// Try to find an existing terminal to split.
			// Even if an existing terminal is found, the split can fail if the terminal width is too small.
			for (const terminal of values(this.terminals)) {
				if (terminal.group === group) {
					const originalInstance = terminal.terminal;
					await originalInstance.waitForTitle();
					result = this.terminalService.splitInstance(originalInstance, launchConfigs);
					if (result) {
						break;
					}
				}
			}
		}
		if (!result) {
			// Either no group is used, no terminal with the group exists or splitting an existing terminal failed.
			result = this.terminalService.createTerminal(launchConfigs);
		}

		const terminalKey = result.id.toString();
		result.onDisposed((terminal) => {
			let terminalData = this.terminals[terminalKey];
			if (terminalData) {
				delete this.terminals[terminalKey];
				delete this.sameTaskTerminals[terminalData.lastTask];
				this.idleTaskTerminals.delete(terminalData.lastTask);
				// Delete the task now as a work around for cases when the onExit isn't fired.
				// This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected.
				// For correct terminal re-use, the task needs to be deleted immediately.
				// Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate.
				delete this.activeTasks[task.getMapKey()];
			}
		});
		this.terminals[terminalKey] = { terminal: result, lastTask: taskKey, group };
		return [result, commandExecutable, undefined];
	}