From cfafcbf24446674dfa286641f40c90e07f9aac7b Mon Sep 17 00:00:00 2001 From: Witalis Domitrz Date: Sat, 30 Aug 2025 14:01:33 +0100 Subject: [PATCH 1/5] support multiple languages --- src/extension.ts | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index be8df42..501ffd5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,7 +7,7 @@ import { runCommand } from './run-command' interface Formatter { command: string - language: string + language: string | string[] } export function activate(context: ExtensionContext) { @@ -42,27 +42,31 @@ export function activate(context: ExtensionContext) { break } - let disposable = languages.registerDocumentFormattingEditProvider(formatter.language, { - async provideDocumentFormattingEdits(document: TextDocument): Promise { - const rawText = document.getText() - const filename = document.uri.fsPath - const workspaceDir = getCwd(filename) - const formattedText = await runCommand(rawText, formatter.command, filename, workspaceDir) + const languages = Array.isArray(formatter.language) ? formatter.language : [formatter.language] - const lastLineNumber = document.lineCount - 1 - const lastLineChar = document.lineAt(lastLineNumber).text.length + for (let language of languages) { + let disposable = languages.registerDocumentFormattingEditProvider(language, { + async provideDocumentFormattingEdits(document: TextDocument): Promise { + const rawText = document.getText() + const filename = document.uri.fsPath + const workspaceDir = getCwd(filename) + const formattedText = await runCommand(rawText, formatter.command, filename, workspaceDir) - const startPos = new Position(0, 0) - const endPos = new Position(lastLineNumber, lastLineChar) - const replaceRange = new Range(startPos, endPos) + const lastLineNumber = document.lineCount - 1 + const lastLineChar = document.lineAt(lastLineNumber).text.length - return [TextEdit.replace(replaceRange, formattedText)] - }, - }) + const startPos = new Position(0, 0) + const endPos = new Position(lastLineNumber, lastLineChar) + const replaceRange = new Range(startPos, endPos) - listeningLanguages.push(formatter.language) + return [TextEdit.replace(replaceRange, formattedText)] + }, + }) - context.subscriptions.push(disposable) + listeningLanguages.push(language) + + context.subscriptions.push(disposable) + } } log(`Formatting activated for the following languages: [${listeningLanguages.toLocaleString()}]`) From 90031748ee55cf3f8af54dc4d74afe7eb3264288 Mon Sep 17 00:00:00 2001 From: Witalis Domitrz Date: Sat, 30 Aug 2025 14:11:34 +0100 Subject: [PATCH 2/5] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 14bf94e..b6258fc 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,14 @@ Populate the `custom-format.formatters` setting. // Create our custom formatters "custom-format.formatters": [ { - // Whatever language id you need to format - "language": "javascript", + // Whatever language id you need to format - or a list of languages + "language": ["javascript", "typescript"], // The command that will be run to format files with the language id specified above "command": "node format.js $FILE" // $FILE is replaced with the path of the file to be formatted }, { - "language": "typescript", - "command": "node format-ts.js" + "language": "html", + "command": "node format-html.js" } ], From e5b6f5f00ff876284c959ca630cd7c99b2ba6025 Mon Sep 17 00:00:00 2001 From: Witalis Domitrz Date: Sat, 30 Aug 2025 14:09:57 +0100 Subject: [PATCH 3/5] support filetype --- README.md | 5 ++++- src/extension.ts | 3 ++- src/run-command.ts | 15 ++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b6258fc..6862d50 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,10 @@ If the command exits with a non-zero code, the formatting of the file will be sk Commands are executed in a subshell, not spawned directly. -Commands are provided with the environment variable, `FILE`, which is the absolute path to the file that is being formatted. +Commands are provided with the environment variables: + +* `FILE`, which is the absolute path to the file that is being formatted. +* `FILETYPE`, which is the file type (language) as seen by the editor. Although it is possible to read from and write to `$FILE` directly, it is better to read from `stdin` and write to `stdout` because it mutates the editor state, not the filesystem state, which could differ. diff --git a/src/extension.ts b/src/extension.ts index 501ffd5..6005bab 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -49,8 +49,9 @@ export function activate(context: ExtensionContext) { async provideDocumentFormattingEdits(document: TextDocument): Promise { const rawText = document.getText() const filename = document.uri.fsPath + const filetype = document.languageId const workspaceDir = getCwd(filename) - const formattedText = await runCommand(rawText, formatter.command, filename, workspaceDir) + const formattedText = await runCommand(rawText, formatter.command, filename, filetype, workspaceDir) const lastLineNumber = document.lineCount - 1 const lastLineChar = document.lineAt(lastLineNumber).text.length diff --git a/src/run-command.ts b/src/run-command.ts index 73888ca..6702f30 100644 --- a/src/run-command.ts +++ b/src/run-command.ts @@ -12,8 +12,17 @@ function stripColor(input: string): string { return input.replace(regex, '') } -export async function runCommand(text: string, command: string, filename: string, workspacePath: string): Promise { - const args = command.replace(/\$FILE/g, filename).split(' ') +export async function runCommand( + text: string, + command: string, + filename: string, + filetype: string, + workspacePath: string +): Promise { + const args = command + .replace(/\$FILETYPE/g, filetype) + .replace(/\$FILE/g, filename) + .split(' ') const file = args.shift() if (!file) return text @@ -31,7 +40,7 @@ export async function runCommand(text: string, command: string, filename: string const child = childProcess.exec( command, - { cwd: workspacePath, env: { ...process.env, FILE: filename }, shell: process.env.SHELL }, + { cwd: workspacePath, env: { ...process.env, FILE: filename, FILETYPE: filetype }, shell: process.env.SHELL }, (error, stdout, stderr) => { if (error) { if (!didReject) reject(error) From 68b907f888541d5149b6623fe89851023f5ee5af Mon Sep 17 00:00:00 2001 From: Witalis Domitrz Date: Sun, 28 Sep 2025 23:46:32 -0400 Subject: [PATCH 4/5] Remove old, unused code --- src/run-command.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/run-command.ts b/src/run-command.ts index 6702f30..95f8c5e 100644 --- a/src/run-command.ts +++ b/src/run-command.ts @@ -19,14 +19,6 @@ export async function runCommand( filetype: string, workspacePath: string ): Promise { - const args = command - .replace(/\$FILETYPE/g, filetype) - .replace(/\$FILE/g, filename) - .split(' ') - const file = args.shift() - - if (!file) return text - const errorOut = (error: string) => { logError(`Received an error while formatting ${filename}:\n> ${command}\nError:\n> ${error.replace(/\n/g, '\n> ')}`) } From 30b8da1ffb1676b6f7162361ed7ede908b045fb0 Mon Sep 17 00:00:00 2001 From: Witalis Domitrz Date: Sun, 28 Sep 2025 23:47:40 -0400 Subject: [PATCH 5/5] Nicer text in README --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 6862d50..7850658 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,7 @@ If there is no content written to `stdout`, error or no error, the formatting of If the command exits with a non-zero code, the formatting of the file will be skipped. -Commands are executed in a subshell, not spawned directly. - -Commands are provided with the environment variables: +Commands are executed in a subshell, not spawned directly, and are provided with the following environment variables: * `FILE`, which is the absolute path to the file that is being formatted. * `FILETYPE`, which is the file type (language) as seen by the editor.