-
-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathCSharpierProcessServer.ts
133 lines (111 loc) · 4.63 KB
/
CSharpierProcessServer.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
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { Logger } from "./Logger";
import { FormatFileParameter, FormatFileResult, ICSharpierProcess2 } from "./ICSharpierProcess";
import fetch from "node-fetch";
import { getDotNetRoot } from "./DotNetProvider";
import * as semver from "semver";
export class CSharpierProcessServer implements ICSharpierProcess2 {
private logger: Logger;
private port: number = 0;
private process: ChildProcessWithoutNullStreams | undefined;
private processFailedToStart = false;
private version: string;
constructor(logger: Logger, csharpierPath: string, workingDirectory: string, version: string) {
this.logger = logger;
this.version = version;
this.spawnProcess(csharpierPath, workingDirectory);
this.logger.debug("Warm CSharpier with initial format");
// warm by formatting a file twice, the 3rd time is when it gets really fast
// make sure we give a path that should not exist to avoid any errors when trying to find config files etc.
this.formatFile("public class ClassName { }", `/${Date.now()}/Test.cs`).then(() => {
this.formatFile("public class ClassName { }", `/${Date.now()}/Test.cs`);
});
}
getProcessFailedToStart(): boolean {
return this.processFailedToStart;
}
private spawnProcess(csharpierPath: string, workingDirectory: string) {
let newCommandsVersion = "1.0.0-alpha1";
let argument = semver.gte(this.version, newCommandsVersion) ? "server" : "--server";
const csharpierProcess = spawn(csharpierPath, [argument], {
stdio: "pipe",
cwd: workingDirectory,
env: { ...process.env, DOTNET_NOLOGO: "1", DOTNET_ROOT: getDotNetRoot() },
});
csharpierProcess.on("error", data => {
this.logger.warn(
"Failed to spawn the needed csharpier process. Formatting cannot occur.",
data,
);
this.processFailedToStart = true;
});
csharpierProcess.on("exit", () => {
if (csharpierProcess.exitCode !== null && csharpierProcess.exitCode > 0) {
this.processFailedToStart = true;
}
});
csharpierProcess.stderr.on("data", data => {
this.logger.warn("CSharpier process return the following error: ", data.toString());
});
let output = "";
const regex = /^Started on (\d+)/;
csharpierProcess.stdout.on("data", chunk => {
output += chunk;
if (regex.test(output) && this.port === 0) {
this.port = parseInt(output.match(regex)![1], 10);
this.logger.debug("Connecting via port " + this.port);
this.process = csharpierProcess;
}
});
}
public async formatFile(content: string, filePath: string): Promise<string> {
const parameter = {
fileName: filePath,
fileContents: content,
};
const result = await this.formatFile2(parameter);
return result?.formattedFile ?? "";
}
public async formatFile2(parameter: FormatFileParameter): Promise<FormatFileResult | null> {
if (this.processFailedToStart) {
this.logger.warn("CSharpier process failed to start. Formatting cannot occur.");
return null;
}
if (typeof this.process === "undefined") {
await new Promise(r => setTimeout(r, 1000));
}
if (this.processFailedToStart || typeof this.process === "undefined") {
this.logger.warn("CSharpier process failed to start. Formatting cannot occur.");
return null;
}
try {
const url = "http://127.0.0.1:" + this.port + "/format";
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(parameter),
});
if (response.status !== 200) {
this.logger.warn(
"Csharpier server returned non-200 status code of " + response.status,
);
return null;
}
return await response.json();
} catch (e) {
this.logger.warn("Failed posting to the csharpier server. " + e);
}
return null;
}
dispose() {
if (typeof this.process !== "undefined") {
(this.process.stdin as any).pause();
this.process.kill();
}
}
getVersion(): string {
return this.version;
}
}