-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
process-task.ts
141 lines (123 loc) · 5.11 KB
/
process-task.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/********************************************************************************
* Copyright (C) 2017 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { injectable, inject, named } from 'inversify';
import { ILogger } from '@theia/core/lib/common/';
import { Process, IProcessExitEvent } from '@theia/process/lib/node';
import { Task, TaskOptions } from '../task';
import { TaskManager } from '../task-manager';
import { ProcessType, ProcessTaskInfo } from '../../common/process/task-protocol';
import { TaskExitedEvent } from '../../common/task-protocol';
// copied from https://github.com/Microsoft/vscode/blob/1.33.1/src/vs/base/common/strings.ts
// Escape codes
// http://en.wikipedia.org/wiki/ANSI_escape_code
const EL = /\x1B\x5B[12]?K/g; // Erase in line
const COLOR_START = /\x1b\[\d+(;\d+)*m/g; // Color
const COLOR_END = /\x1b\[0?m/g; // Color
export function removeAnsiEscapeCodes(str: string): string {
if (str) {
str = str.replace(EL, '');
str = str.replace(COLOR_START, '');
str = str.replace(COLOR_END, '');
}
return str.trimRight();
}
export const TaskProcessOptions = Symbol('TaskProcessOptions');
export interface TaskProcessOptions extends TaskOptions {
process: Process;
processType: ProcessType;
}
export const TaskFactory = Symbol('TaskFactory');
export type TaskFactory = (options: TaskProcessOptions) => ProcessTask;
/** Represents a Task launched as a process by `ProcessTaskRunner`. */
@injectable()
export class ProcessTask extends Task {
constructor(
@inject(TaskManager) protected readonly taskManager: TaskManager,
@inject(ILogger) @named('task') protected readonly logger: ILogger,
@inject(TaskProcessOptions) protected readonly options: TaskProcessOptions
) {
super(taskManager, logger, options);
const toDispose = this.process.onClose(async event => {
toDispose.dispose();
this.fireTaskExited(await this.getTaskExitedEvent(event));
});
// Buffer to accumulate incoming output.
let dataBuffer: string = '';
this.process.outputStream.on('data', (chunk: string) => {
dataBuffer += chunk;
while (1) {
// Check if we have a complete line.
const eolIdx = dataBuffer.indexOf('\n');
if (eolIdx < 0) {
break;
}
// Get and remove the line from the data buffer.
const lineBuf = dataBuffer.slice(0, eolIdx);
dataBuffer = dataBuffer.slice(eolIdx + 1);
const processedLine = removeAnsiEscapeCodes(lineBuf);
this.fireOutputLine({
taskId: this.taskId,
ctx: this.context,
line: processedLine
});
}
});
this.logger.info(`Created new task, id: ${this.id}, process id: ${this.options.process.id}, OS PID: ${this.process.pid}, context: ${this.context}`);
}
kill(): Promise<void> {
return new Promise<void>(resolve => {
if (this.process.killed) {
resolve();
} else {
const toDispose = this.process.onClose(event => {
toDispose.dispose();
resolve();
});
this.process.kill();
}
});
}
protected async getTaskExitedEvent(evt: IProcessExitEvent): Promise<TaskExitedEvent> {
return {
taskId: this.taskId,
ctx: this.context,
code: evt.code,
signal: evt.signal,
config: this.options.config,
terminalId: this.process.id,
processId: this.process.id
};
}
getRuntimeInfo(): ProcessTaskInfo {
return {
taskId: this.id,
ctx: this.context,
config: this.options.config,
terminalId: this.process.id,
processId: this.process.id,
};
}
get process(): Process {
return this.options.process;
}
get processType(): ProcessType {
return this.options.processType;
}
}