Skip to content

Commit ee9f50c

Browse files
committed
WIP
closes #1109
1 parent a2a547a commit ee9f50c

File tree

8 files changed

+301
-42
lines changed

8 files changed

+301
-42
lines changed

Src/CSharpier.VSCode/src/CSharpierProcessPipeMultipleFiles.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
22
import { Logger } from "./Logger";
3-
import { ICSharpierProcess } from "./CSharpierProcess";
3+
import { ICSharpierProcess } from "./ICSharpierProcess";
44
import { getDotNetRoot } from "./DotNetProvider";
5+
import * as process from "process";
56

67
export class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess {
78
private process: ChildProcessWithoutNullStreams;
89
private callbacks: ((result: string) => void)[] = [];
910
private logger: Logger;
1011
private nextFile: string = "";
11-
public processFailedToStart = false;
12+
private processFailedToStart = false;
13+
private version: string;
1214

13-
constructor(logger: Logger, csharpierPath: string, workingDirectory: string) {
15+
constructor(logger: Logger, csharpierPath: string, workingDirectory: string, version: string) {
1416
this.logger = logger;
1517
this.process = this.spawnProcess(csharpierPath, workingDirectory);
18+
this.version = version;
1619

1720
this.logger.debug("Warm CSharpier with initial format");
1821
// warm by formatting a file twice, the 3rd time is when it gets really fast
@@ -96,4 +99,8 @@ export class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess {
9699
(this.process.stdin as any).pause();
97100
this.process.kill();
98101
}
102+
103+
getVersion(): string {
104+
return this.version;
105+
}
99106
}

Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import { Logger } from "./Logger";
33
import * as path from "path";
44
import * as semver from "semver";
55
import * as convert from "xml-js";
6-
import { ICSharpierProcess, NullCSharpierProcess } from "./CSharpierProcess";
6+
import { ICSharpierProcess } from "./ICSharpierProcess";
77
import { CSharpierProcessSingleFile } from "./CSharpierProcessSingleFile";
88
import { CSharpierProcessPipeMultipleFiles } from "./CSharpierProcessPipeMultipleFiles";
99
import * as fs from "fs";
1010
import { InstallerService } from "./InstallerService";
1111
import { CustomPathInstaller } from "./CustomPathInstaller";
1212
import { execDotNet } from "./DotNetProvider";
13+
import { NullCSharpierProcess } from "./NullCSharpierProcess";
14+
import { CSharpierProcessServer } from "./CSharpierProcessServer";
15+
import { ICSharpierProcess2 } from "./ICSharpierProcess";
1316

1417
export class CSharpierProcessProvider implements Disposable {
1518
warnedForOldVersion = false;
@@ -62,7 +65,7 @@ export class CSharpierProcessProvider implements Disposable {
6265
delete this.warmingByDirectory[directory];
6366
}
6467

65-
public getProcessFor = (filePath: string) => {
68+
public getProcessFor: ICSharpierProcess | ICSharpierProcess2 = (filePath: string) => {
6669
const directory = this.getDirectoryOfFile(filePath);
6770
let version = this.csharpierVersionByDirectory[directory];
6871
if (!version) {
@@ -218,26 +221,37 @@ export class CSharpierProcessProvider implements Disposable {
218221

219222
this.logger.debug(`Adding new version ${version} process for ${directory}`);
220223

221-
if (semver.lt(version, "0.12.0")) {
224+
let csharpierProcess: ICSharpierProcess;
225+
226+
if (semver.gte(version, "0.28.0")) {
227+
csharpierProcess = new CSharpierProcessServer(
228+
this.logger,
229+
customPath,
230+
directory,
231+
version,
232+
);
233+
} else if (semver.gte(version, "0.12.0")) {
234+
csharpierProcess = new CSharpierProcessPipeMultipleFiles(
235+
this.logger,
236+
customPath,
237+
directory,
238+
version,
239+
);
240+
} else {
222241
if (!this.warnedForOldVersion) {
223242
window.showInformationMessage(
224243
"Please upgrade to CSharpier >= 0.12.0 for bug fixes and improved formatting speed.",
225244
);
226245
this.warnedForOldVersion = true;
227246
}
228-
return new CSharpierProcessSingleFile(this.logger, customPath);
229-
} else {
230-
const csharpierProcess = new CSharpierProcessPipeMultipleFiles(
231-
this.logger,
232-
customPath,
233-
directory,
234-
);
235-
if (csharpierProcess.processFailedToStart) {
236-
this.displayFailureMessage();
237-
}
247+
csharpierProcess = new CSharpierProcessSingleFile(this.logger, customPath, version);
248+
}
238249

239-
return csharpierProcess;
250+
if (csharpierProcess.processFailedToStart) {
251+
this.displayFailureMessage();
240252
}
253+
254+
return csharpierProcess;
241255
} catch (ex: any) {
242256
this.logger.error(ex.output.toString());
243257
this.logger.debug(
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
2+
import { Logger } from "./Logger";
3+
import { FormatFileParameter, FormatFileResult, ICSharpierProcess2 } from "./ICSharpierProcess";
4+
5+
export class CSharpierProcessServer implements ICSharpierProcess2 {
6+
private csharpierPath: string;
7+
private logger: Logger;
8+
private port: number = 0;
9+
private process: ChildProcessWithoutNullStreams;
10+
private processFailedToStart = false;
11+
private version: string;
12+
13+
constructor(logger: Logger, csharpierPath: string, workingDirectory: string, version: string) {
14+
this.logger = logger;
15+
this.csharpierPath = csharpierPath;
16+
this.process = this.spawnProcess(csharpierPath, workingDirectory);
17+
this.version = version;
18+
19+
this.logger.debug("Warm CSharpier with initial format");
20+
// warm by formatting a file twice, the 3rd time is when it gets really fast
21+
this.formatFile("public class ClassName { }", "/Temp/Test.cs");
22+
this.formatFile("public class ClassName { }", "/Temp/Test.cs");
23+
}
24+
25+
private spawnProcess(
26+
csharpierPath: string,
27+
workingDirectory: string,
28+
): ChildProcessWithoutNullStreams {
29+
const csharpierProcess = spawn(csharpierPath, ["--server"], {
30+
stdio: "pipe",
31+
cwd: workingDirectory,
32+
env: { ...process.env, DOTNET_NOLOGO: "1" },
33+
});
34+
35+
let output = "";
36+
const regex = /^Started on (\d+)/;
37+
38+
// TODO look at pipe / https://github.com/belav/csharpier/pull/1127/files#diff-844ad2632d6fd1e83ce639f4f1ef23e8dad4f547b7a524b9e1ed715feff04958
39+
csharpierProcess.stdout.on("data", chunk => {
40+
this.logger.debug("Got chunk of size " + chunk.length);
41+
output += chunk;
42+
this.logger.debug("Got " + output);
43+
if (regex.test(output)) {
44+
this.port = parseInt(output.match(regex)![1], 10);
45+
}
46+
});
47+
48+
return csharpierProcess;
49+
}
50+
51+
// private void StartProcess()
52+
// {
53+
// try
54+
// {
55+
// var processStartInfo = new ProcessStartInfo(this.csharpierPath, "--server")
56+
// {
57+
// RedirectStandardOutput = true,
58+
// RedirectStandardError = true,
59+
// UseShellExecute = false,
60+
// CreateNoWindow = true,
61+
// Environment = { ["DOTNET_NOLOGO"] = "1" }
62+
// };
63+
// this.process = Process.Start(processStartInfo);
64+
//
65+
// var output = string.Empty;
66+
//
67+
// var task = Task.Run(() =>
68+
// {
69+
// output = this.process!.StandardOutput.ReadLine();
70+
// });
71+
//
72+
// if (!task.Wait(TimeSpan.FromSeconds(2)))
73+
// {
74+
// this.logger.Warn(
75+
// "Spawning the csharpier server timed out. Formatting cannot occur."
76+
// );
77+
// this.process!.Kill();
78+
// return;
79+
// }
80+
//
81+
// if (this.process!.HasExited)
82+
// {
83+
// this.logger.Warn(
84+
// "Spawning the csharpier server failed because it exited. "
85+
// + this.process!.StandardError.ReadToEnd()
86+
// );
87+
// this.ProcessFailedToStart = true;
88+
// return;
89+
// }
90+
//
91+
// var portString = output.Replace("Started on ", "");
92+
// this.port = int.Parse(portString);
93+
//
94+
// this.logger.Debug("Connecting via port " + portString);
95+
// }
96+
// catch (Exception e)
97+
// {
98+
// this.logger.Warn("Failed to spawn the needed csharpier server." + e);
99+
// this.ProcessFailedToStart = true;
100+
// }
101+
// }
102+
103+
public async formatFile(content: string, filePath: string): Promise<string> {
104+
const parameter = {
105+
fileName: filePath,
106+
fileContents: content,
107+
};
108+
const result = await this.formatFile2(parameter);
109+
return result?.formattedFile ?? "";
110+
}
111+
112+
public async formatFile2(parameter: FormatFileParameter): Promise<FormatFileResult | null> {
113+
if (this.processFailedToStart) {
114+
this.logger.warn("CSharpier process failed to start. Formatting cannot occur.");
115+
return null;
116+
}
117+
118+
const url = "http://localhost:" + this.port + "/format";
119+
120+
try {
121+
// var request = (HttpWebRequest)WebRequest.Create(url);
122+
// request.Method = "POST";
123+
// request.ContentType = "application/json; charset=utf-8";
124+
//
125+
// using (var streamWriter = new StreamWriter(request.GetRequestStream()))
126+
// {
127+
// streamWriter.Write(JsonConvert.SerializeObject(parameter));
128+
// }
129+
//
130+
// var response = (HttpWebResponse)request.GetResponse();
131+
//
132+
// if (response.StatusCode != HttpStatusCode.OK)
133+
// {
134+
// this.logger.Warn(
135+
// "Csharpier server returned non-200 status code of " + response.StatusCode
136+
// );
137+
// response.Close();
138+
// return null;
139+
// }
140+
//
141+
// using (var streamReader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
142+
// {
143+
// var result = JsonConvert.DeserializeObject<FormatFileResult>(
144+
// streamReader.ReadToEnd()
145+
// );
146+
// return result;
147+
// }
148+
} catch (e) {
149+
this.logger.warn("Failed posting to the csharpier server. " + e);
150+
}
151+
152+
return null;
153+
}
154+
155+
dispose() {
156+
(this.process.stdin as any).pause();
157+
this.process.kill();
158+
}
159+
160+
getVersion(): string {
161+
return this.version;
162+
}
163+
}

Src/CSharpier.VSCode/src/CSharpierProcessSingleFile.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { Logger } from "./Logger";
22
import { spawn } from "child_process";
3-
import { ICSharpierProcess } from "./CSharpierProcess";
3+
import { ICSharpierProcess } from "./ICSharpierProcess";
44
import * as path from "path";
55
import { getDotNetRoot } from "./DotNetProvider";
6+
import * as process from "process";
67

78
export class CSharpierProcessSingleFile implements ICSharpierProcess {
89
private readonly csharpierPath: string;
910
private logger: Logger;
11+
private version: string;
1012

11-
constructor(logger: Logger, csharpierPath: string) {
13+
constructor(logger: Logger, csharpierPath: string, version: string) {
1214
this.logger = logger;
1315
this.csharpierPath = csharpierPath;
16+
this.version = version;
1417
}
1518

1619
formatFile(content: string, filePath: string): Promise<string> {
@@ -40,4 +43,8 @@ export class CSharpierProcessSingleFile implements ICSharpierProcess {
4043
}
4144

4245
dispose() {}
46+
47+
getVersion(): string {
48+
return this.version;
49+
}
4350
}

Src/CSharpier.VSCode/src/Extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { ExtensionContext, window, workspace } from "vscode";
22
import { CSharpierProcessProvider } from "./CSharpierProcessProvider";
33
import { FormattingService } from "./FormattingService";
44
import { Logger } from "./Logger";
5-
import { NullCSharpierProcess } from "./CSharpierProcess";
65
import { findDotNet } from "./DotNetProvider";
76
import { options } from "./Options";
7+
import { NullCSharpierProcess } from "./NullCSharpierProcess";
88

99
export async function activate(context: ExtensionContext) {
1010
if (!workspace.isTrusted) {

0 commit comments

Comments
 (0)