diff --git a/src/formatter/htmlbeautifier.ts b/src/formatter/htmlbeautifier.ts
index cda7c21..37eddab 100644
--- a/src/formatter/htmlbeautifier.ts
+++ b/src/formatter/htmlbeautifier.ts
@@ -3,34 +3,39 @@ import * as cp from "child_process";
const isWsl = require("is-wsl");
export default class HtmlBeautifier {
+ private logChannel: vscode.LogOutputChannel;
+
+ constructor() {
+ this.logChannel = vscode.window.createOutputChannel("ERB Beautifier", {
+ log: true,
+ });
+ }
+
/**
- * Formats the given input using HTML Beautifier
- * @param {string} input - The input to be formatted
- * @returns {Promise} The formatted input
+ * Formats the input string using HTML Beautifier.
+ * @param input The input string to be formatted.
+ * @returns A promise that resolves to the formatted string.
*/
public async format(input: string): Promise {
try {
- const cmd = `${this.exe} ${this.cliOptions.join(
- " "
- )} with custom env ${JSON.stringify(this.customEnvVars)}`;
- console.log(`Formatting ERB with command: ${cmd}`);
- console.time(cmd);
-
+ const startTime = Date.now();
const result = await this.executeCommand(input);
-
- console.timeEnd(cmd);
+ const duration = Date.now() - startTime;
+ this.logChannel.info(
+ `Formatting completed successfully in ${duration}ms.`
+ );
return result;
} catch (error) {
- console.error(error);
- const errorMessage =
- error instanceof Error ? error.message : "Unknown error occurred";
- vscode.window.showErrorMessage(
- `Error occurred while formatting: ${errorMessage}`
- );
+ this.handleError(error, "Error occurred while formatting");
throw error;
}
}
+ /**
+ * Executes the formatting command with the provided input.
+ * @param input The input to format.
+ * @returns A promise that resolves to the formatted output.
+ */
private executeCommand(input: string): Promise {
return new Promise((resolve, reject) => {
// Handle spawn EINVAL error on Windows. See https://github.com/nodejs/node/issues/52554
@@ -44,53 +49,37 @@ export default class HtmlBeautifier {
...shellOptions,
});
- if (htmlbeautifier.stdin === null || htmlbeautifier.stdout === null) {
- const msg = "Couldn't initialize STDIN or STDOUT";
- console.warn(msg);
- vscode.window.showErrorMessage(msg);
- reject(new Error(msg));
- return;
- }
-
- let formattedResult = "";
- let errorMessage = "";
- let stdoutChunks: Buffer[] = [];
- let stderrChunks: Buffer[] = [];
+ const fullCommand = `${this.exe} ${this.cliOptions.join(" ")} (cwd: ${
+ vscode.workspace.rootPath || __dirname
+ }) with custom env: ${JSON.stringify(this.customEnvVars)}`;
+ this.logChannel.info(`Formatting ERB with command: ${fullCommand}`);
- htmlbeautifier.on("error", (err) => {
- console.warn(err);
- vscode.window.showErrorMessage(
- `Couldn't run ${this.exe} '${err.message}'`
+ if (!htmlbeautifier.stdin || !htmlbeautifier.stdout) {
+ return this.handleSpawnError(
+ reject,
+ "Couldn't initialize STDIN or STDOUT"
);
- reject(err);
- });
-
- htmlbeautifier.stdout.on("data", (chunk) => {
- stdoutChunks.push(chunk);
- });
+ }
- htmlbeautifier.stdout.on("end", () => {
- let result = Buffer.concat(stdoutChunks).toString();
- formattedResult = this.handleFinalNewline(input, result);
- });
+ const stdoutChunks: Buffer[] = [];
+ const stderrChunks: Buffer[] = [];
- htmlbeautifier.stderr.on("data", (chunk) => {
- stderrChunks.push(chunk);
- });
+ htmlbeautifier.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
+ htmlbeautifier.stderr.on("data", (chunk) => stderrChunks.push(chunk));
- htmlbeautifier.stderr.on("end", () => {
- errorMessage = Buffer.concat(stderrChunks).toString();
- });
+ htmlbeautifier.on("error", (err) =>
+ this.handleSpawnError(
+ reject,
+ `Couldn't run ${this.exe}: ${err.message}`,
+ err
+ )
+ );
htmlbeautifier.on("exit", (code) => {
- if (code) {
- vscode.window.showErrorMessage(
- `Failed with exit code: ${code}. '${errorMessage}'`
- );
- reject(new Error(`Command failed with exit code ${code}`));
- } else {
- resolve(formattedResult);
- }
+ const formattedResult = Buffer.concat(stdoutChunks).toString();
+ const finalResult = this.handleFinalNewline(input, formattedResult);
+ const errorMessage = Buffer.concat(stderrChunks).toString();
+ this.handleExit(code, finalResult, errorMessage, resolve, reject);
});
htmlbeautifier.stdin.write(input);
@@ -99,8 +88,64 @@ export default class HtmlBeautifier {
}
/**
- * Returns the executable path for HTML Beautifier
- * @returns {string} The executable path
+ * Handles errors during process spawning.
+ * @param reject The promise reject function.
+ * @param message The error message to log and show to the user.
+ * @param err Optional error object.
+ */
+ private handleSpawnError(
+ reject: (reason?: any) => void,
+ message: string,
+ err?: Error
+ ): void {
+ this.logChannel.warn(message);
+ vscode.window.showErrorMessage(message);
+ if (err) {
+ this.logChannel.warn(err.message);
+ }
+ reject(err || new Error(message));
+ }
+
+ /**
+ * Handles the process exit event and resolves or rejects the promise.
+ * @param code The process exit code.
+ * @param result The formatted result.
+ * @param errorMessage The error message, if any.
+ * @param resolve The promise resolve function.
+ * @param reject The promise reject function.
+ */
+ private handleExit(
+ code: number | null,
+ result: string,
+ errorMessage: string,
+ resolve: (value: string | PromiseLike) => void,
+ reject: (reason?: any) => void
+ ): void {
+ if (code && code !== 0) {
+ const error = `Failed with exit code: ${code}. ${errorMessage}`;
+ this.logChannel.error(error);
+ vscode.window.showErrorMessage(error);
+ reject(new Error(error));
+ } else {
+ resolve(result);
+ }
+ }
+
+ /**
+ * Handles errors by logging and displaying a message to the user.
+ * @param error The error object or message.
+ * @param userMessage The message to display to the user.
+ */
+ private handleError(error: any, userMessage: string): void {
+ const errorMessage =
+ error instanceof Error ? error.message : "Unknown error occurred";
+ this.logChannel.error(errorMessage);
+ vscode.window.showErrorMessage(`${userMessage}: ${errorMessage}`);
+ }
+
+ /**
+ * Gets the executable path for HTML Beautifier based on the configuration.
+ * @returns The path to the executable.
*/
private get exe(): string {
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
@@ -112,26 +157,26 @@ export default class HtmlBeautifier {
}
/**
- * Determines if the current platform is Windows (excluding WSL)
- * @returns {boolean} True if the platform is Windows, false otherwise
+ * Checks if the current platform is Windows (excluding WSL).
+ * @returns True if the platform is Windows; false otherwise.
*/
private isWindows(): boolean {
return process.platform === "win32" && !isWsl;
}
/**
- * Returns the command-line options for HTML Beautifier
- * @returns {string[]} The command-line options
+ * Retrieves the command-line options for HTML Beautifier from the configuration.
+ * @returns An array of command-line options.
*/
private get cliOptions(): string[] {
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
- const acc: string[] = [];
+ const options: string[] = [];
if (config.get("useBundler")) {
- acc.push("exec", "htmlbeautifier");
+ options.push("exec", "htmlbeautifier");
}
- return Object.keys(config).reduce(function (acc, key) {
+ return Object.keys(config).reduce((acc, key) => {
switch (key) {
case "indentBy":
acc.push("--indent-by", config[key]);
@@ -154,27 +199,23 @@ export default class HtmlBeautifier {
break;
}
return acc;
- }, acc);
+ }, options);
}
/**
- * Retrieves the custom environment variables from the configuration
- * @returns {Record} The custom environment variables
+ * Retrieves custom environment variables from the configuration.
+ * @returns A record of custom environment variables.
*/
private get customEnvVars(): Record {
const config = vscode.workspace.getConfiguration("vscode-erb-beautify");
- const customEnvVar = config.get("customEnvVar", {}) as Record<
- string,
- string
- >;
- return customEnvVar;
+ return config.get("customEnvVar", {}) as Record;
}
/**
- * Adjusts the final newline of the result string based on the VS Code configuration and the input string.
- * @param {string} input - The original input string.
- * @param {string} result - The result string to be processed.
- * @returns {string} The processed result string.
+ * Adjusts the final newline of the result string based on VS Code configuration.
+ * @param input The original input string.
+ * @param result The formatted result string.
+ * @returns The adjusted result string.
*/
private handleFinalNewline(input: string, result: string): string {
// Get the 'insertFinalNewline' setting from VS Code configuration
@@ -182,7 +223,7 @@ export default class HtmlBeautifier {
.getConfiguration()
.get("files.insertFinalNewline");
- // Check if the result string ends with a newline
+ // Determine if the result ends with a newline
const resultEndsWithNewline =
result.endsWith("\n") || result.endsWith("\r\n");
@@ -191,11 +232,8 @@ export default class HtmlBeautifier {
// Get the 'files.eol' setting from VS Code configuration
const eol = vscode.workspace.getConfiguration().get("files.eol");
- // Set the newline character(s) based on the 'files.eol' setting and the platform
- let newline = eol;
- if (eol === "auto") {
- newline = this.isWindows() ? "\r\n" : "\n";
- }
+ // Determine newline character(s) based on the 'files.eol' setting and the platform
+ let newline = eol === "auto" ? (this.isWindows() ? "\r\n" : "\n") : eol;
// Append the newline to the result
result += newline;
@@ -208,11 +246,8 @@ export default class HtmlBeautifier {
// If the input and result use different newline character(s)
if (inputNewline !== resultNewline) {
- // Remove the newline from the end of the result
- result = result.slice(0, -resultNewline.length);
-
- // Append the newline from the input to the result
- result += inputNewline;
+ // Remove the newline from the end of the result and append the input's newline
+ result = result.slice(0, -resultNewline.length) + inputNewline;
}
}
@@ -221,20 +256,16 @@ export default class HtmlBeautifier {
}
/**
- * Determines the type of newline used in the input string.
- * @param {string} input - The input string.
- * @returns {string} The newline character(s) used in the input string, or an empty string if the input does not end with a newline.
+ * Determines the newline character(s) used in the input string.
+ * @param input The input string.
+ * @returns The newline character(s) used, or an empty string if none.
*/
private getNewline(input: string): string {
- // If the input ends with a Windows-style newline, return '\r\n'
if (input.endsWith("\r\n")) {
- return "\r\n";
- }
- // If the input ends with a Unix-style newline, return '\n'
- else if (input.endsWith("\n")) {
- return "\n";
+ return "\r\n"; // Return Windows-style newline
+ } else if (input.endsWith("\n")) {
+ return "\n"; // Return Unix-style newline
}
- // If the input does not end with a newline, return an empty string
- return "";
+ return ""; // Return empty if no newline found
}
}