Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Src/CSharpier.VSCode/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [1.7.0]
- Use CSharpier Http Server for 0.28.0+
- Log version of CSharpier used to format a given file

## [1.6.0]
- Better support for dotnet commands. The extension will now try to locate dotnet by doing the following.
- If `dotnet.dotnetPath` is set, will try using that to find `dotnet`
Expand Down
2,630 changes: 1,168 additions & 1,462 deletions Src/CSharpier.VSCode/package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions Src/CSharpier.VSCode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "csharpier-vscode",
"displayName": "CSharpier - Code formatter",
"description": "Code formatter using csharpier",
"version": "1.6.0",
"version": "1.7.0",
"publisher": "csharpier",
"author": "CSharpier",
"homepage": "https://marketplace.visualstudio.com/items?itemName=csharpier.csharpier-vscode",
Expand Down Expand Up @@ -69,16 +69,20 @@
"@types/glob": "7.1.4",
"@types/mocha": "9.0.0",
"@types/node": "14.x",
"@types/node-fetch": "^2.6.11",
"@types/semver": "7.3.9",
"@types/vscode": "1.60.0",
"prettier": "2.4.1",
"rimraf": "3.0.2",
"semver": "7.6.0",
"ts-loader": "9.2.5",
"typescript": "4.4.3",
"vsce": "2.15.0",
"vsce": "^1.97.0",
"webpack": "5.90.1",
"webpack-cli": "4.9.1",
"xml-js": "1.6.11"
},
"dependencies": {
"node-fetch": "^2.7.0"
}
}
17 changes: 14 additions & 3 deletions Src/CSharpier.VSCode/src/CSharpierProcessPipeMultipleFiles.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { Logger } from "./Logger";
import { ICSharpierProcess } from "./CSharpierProcess";
import { ICSharpierProcess } from "./ICSharpierProcess";
import { getDotNetRoot } from "./DotNetProvider";
import * as process from "process";

export class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess {
private process: ChildProcessWithoutNullStreams;
private callbacks: ((result: string) => void)[] = [];
private logger: Logger;
private nextFile: string = "";
public processFailedToStart = false;
private processFailedToStart = false;
private version: string;

constructor(logger: Logger, csharpierPath: string, workingDirectory: string) {
constructor(logger: Logger, csharpierPath: string, workingDirectory: string, version: string) {
this.logger = logger;
this.process = this.spawnProcess(csharpierPath, workingDirectory);
this.version = version;

this.logger.debug("Warm CSharpier with initial format");
// warm by formatting a file twice, the 3rd time is when it gets really fast
Expand All @@ -21,6 +24,10 @@ export class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess {
});
}

getProcessFailedToStart(): boolean {
return this.processFailedToStart;
}

private spawnProcess = (csharpierPath: string, workingDirectory: string) => {
const csharpierProcess = spawn(csharpierPath, ["--pipe-multiple-files"], {
stdio: "pipe",
Expand Down Expand Up @@ -96,4 +103,8 @@ export class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess {
(this.process.stdin as any).pause();
this.process.kill();
}

getVersion(): string {
return this.version;
}
}
47 changes: 32 additions & 15 deletions Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { Logger } from "./Logger";
import * as path from "path";
import * as semver from "semver";
import * as convert from "xml-js";
import { ICSharpierProcess, NullCSharpierProcess } from "./CSharpierProcess";
import { ICSharpierProcess } from "./ICSharpierProcess";
import { CSharpierProcessSingleFile } from "./CSharpierProcessSingleFile";
import { CSharpierProcessPipeMultipleFiles } from "./CSharpierProcessPipeMultipleFiles";
import * as fs from "fs";
import { InstallerService } from "./InstallerService";
import { CustomPathInstaller } from "./CustomPathInstaller";
import { execDotNet } from "./DotNetProvider";
import { NullCSharpierProcess } from "./NullCSharpierProcess";
import { CSharpierProcessServer } from "./CSharpierProcessServer";
import { ICSharpierProcess2 } from "./ICSharpierProcess";

export class CSharpierProcessProvider implements Disposable {
warnedForOldVersion = false;
Expand All @@ -18,7 +21,10 @@ export class CSharpierProcessProvider implements Disposable {
installerService: InstallerService;
warmingByDirectory: Record<string, boolean | undefined> = {};
csharpierVersionByDirectory: Record<string, string | undefined> = {};
csharpierProcessesByVersion: Record<string, ICSharpierProcess | undefined> = {};
csharpierProcessesByVersion: Record<
string,
ICSharpierProcess | ICSharpierProcess2 | undefined
> = {};

constructor(logger: Logger, extension: Extension<unknown>) {
this.logger = logger;
Expand Down Expand Up @@ -62,7 +68,7 @@ export class CSharpierProcessProvider implements Disposable {
delete this.warmingByDirectory[directory];
}

public getProcessFor = (filePath: string) => {
public getProcessFor = (filePath: string): ICSharpierProcess | ICSharpierProcess2 => {
const directory = this.getDirectoryOfFile(filePath);
let version = this.csharpierVersionByDirectory[directory];
if (!version) {
Expand Down Expand Up @@ -218,26 +224,37 @@ export class CSharpierProcessProvider implements Disposable {

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

if (semver.lt(version, "0.12.0")) {
let csharpierProcess: ICSharpierProcess;

if (semver.gte(version, "0.28.0")) {
csharpierProcess = new CSharpierProcessServer(
this.logger,
customPath,
directory,
version,
);
} else if (semver.gte(version, "0.12.0")) {
csharpierProcess = new CSharpierProcessPipeMultipleFiles(
this.logger,
customPath,
directory,
version,
);
} else {
if (!this.warnedForOldVersion) {
window.showInformationMessage(
"Please upgrade to CSharpier >= 0.12.0 for bug fixes and improved formatting speed.",
);
this.warnedForOldVersion = true;
}
return new CSharpierProcessSingleFile(this.logger, customPath);
} else {
const csharpierProcess = new CSharpierProcessPipeMultipleFiles(
this.logger,
customPath,
directory,
);
if (csharpierProcess.processFailedToStart) {
this.displayFailureMessage();
}
csharpierProcess = new CSharpierProcessSingleFile(this.logger, customPath, version);
}

return csharpierProcess;
if (csharpierProcess.getProcessFailedToStart()) {
this.displayFailureMessage();
}

return csharpierProcess;
} catch (ex: any) {
this.logger.error(ex.output.toString());
this.logger.debug(
Expand Down
119 changes: 119 additions & 0 deletions Src/CSharpier.VSCode/src/CSharpierProcessServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { Logger } from "./Logger";
import { FormatFileParameter, FormatFileResult, ICSharpierProcess2 } from "./ICSharpierProcess";
import fetch from "node-fetch";

export class CSharpierProcessServer implements ICSharpierProcess2 {
private csharpierPath: string;
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.csharpierPath = csharpierPath;
this.spawnProcess(csharpierPath, workingDirectory);
this.version = version;

this.logger.debug("Warm CSharpier with initial format");
// warm by formatting a file twice, the 3rd time is when it gets really fast
this.formatFile("public class ClassName { }", "/Temp/Test.cs").then(() => {
this.formatFile("public class ClassName { }", "/Temp/Test.cs");
});
}

getProcessFailedToStart(): boolean {
return this.processFailedToStart;
}

private spawnProcess(csharpierPath: string, workingDirectory: string) {
const csharpierProcess = spawn(csharpierPath, ["--server"], {
stdio: "pipe",
cwd: workingDirectory,
env: { ...process.env, DOTNET_NOLOGO: "1" },
});

csharpierProcess.on("error", data => {
this.logger.warn(
"Failed to spawn the needed csharpier process. Formatting cannot occur.",
data,
);
this.processFailedToStart = true;
});

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, 2000));
}

if (this.processFailedToStart || typeof this.process === "undefined") {
this.logger.warn("CSharpier process failed to start. Formatting cannot occur.");
return null;
}

try {
const url = "http://localhost:" + 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;
}
}
15 changes: 13 additions & 2 deletions Src/CSharpier.VSCode/src/CSharpierProcessSingleFile.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Logger } from "./Logger";
import { spawn } from "child_process";
import { ICSharpierProcess } from "./CSharpierProcess";
import { ICSharpierProcess } from "./ICSharpierProcess";
import * as path from "path";
import { getDotNetRoot } from "./DotNetProvider";
import * as process from "process";

export class CSharpierProcessSingleFile implements ICSharpierProcess {
private readonly csharpierPath: string;
private logger: Logger;
private version: string;

constructor(logger: Logger, csharpierPath: string) {
constructor(logger: Logger, csharpierPath: string, version: string) {
this.logger = logger;
this.csharpierPath = csharpierPath;
this.version = version;
}

getProcessFailedToStart(): boolean {
return false;
}

formatFile(content: string, filePath: string): Promise<string> {
Expand Down Expand Up @@ -40,4 +47,8 @@ export class CSharpierProcessSingleFile implements ICSharpierProcess {
}

dispose() {}

getVersion(): string {
return this.version;
}
}
2 changes: 1 addition & 1 deletion Src/CSharpier.VSCode/src/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { ExtensionContext, window, workspace } from "vscode";
import { CSharpierProcessProvider } from "./CSharpierProcessProvider";
import { FormattingService } from "./FormattingService";
import { Logger } from "./Logger";
import { NullCSharpierProcess } from "./CSharpierProcess";
import { findDotNet } from "./DotNetProvider";
import { options } from "./Options";
import { NullCSharpierProcess } from "./NullCSharpierProcess";

export async function activate(context: ExtensionContext) {
if (!workspace.isTrusted) {
Expand Down
Loading