From 7017699ae0b0ac446d2ba8b7d7ca3c1d5ba20862 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 30 Jul 2024 23:30:01 -0400 Subject: [PATCH 1/6] extension.ts: format Run formater on extension.ts Signed-off-by: Jesse Taube --- src/extension.ts | 52 +++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index c49688f..e783d59 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,15 +1,17 @@ import * as vscode from 'vscode'; import cp = require('child_process'); import path = require('path'); -import {MODES, - ALIAS} from './clangMode'; -import {getBinPath} from './clangPath'; +import { + MODES, + ALIAS +} from './clangMode'; +import { getBinPath } from './clangPath'; import sax = require('sax'); export let outputChannel = vscode.window.createOutputChannel('Clang-Format'); function getPlatformString() { - switch(process.platform) { + switch (process.platform) { case 'win32': return 'windows'; case 'linux': return 'linux'; case 'darwin': return 'osx'; @@ -52,7 +54,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma byte: 0, offset: 0 }; - let byteToOffset = function(editInfo: { length: number, offset: number }) { + let byteToOffset = function (editInfo: { length: number, offset: number }) { let offset = editInfo.offset; let length = editInfo.length; @@ -81,20 +83,20 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } switch (tag.name) { - case 'replacements': - return; - - case 'replacement': - currentEdit = { - length: parseInt(tag.attributes['length'].toString()), - offset: parseInt(tag.attributes['offset'].toString()), - text: '' - }; - byteToOffset(currentEdit); - break; - - default: - reject(`Unexpected tag ${tag.name}`); + case 'replacements': + return; + + case 'replacement': + currentEdit = { + length: parseInt(tag.attributes['length'].toString()), + offset: parseInt(tag.attributes['offset'].toString()), + text: '' + }; + byteToOffset(currentEdit); + break; + + default: + reject(`Unexpected tag ${tag.name}`); } }; @@ -191,14 +193,14 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma let parsedPath = path.parse(document.fileName); let fileNoExtension = path.join(parsedPath.dir, parsedPath.name); return assumedFilename - .replace(/\${file}/g, document.fileName) - .replace(/\${fileNoExtension}/g, fileNoExtension) - .replace(/\${fileBasename}/g, parsedPath.base) - .replace(/\${fileBasenameNoExtension}/g, parsedPath.name) - .replace(/\${fileExtname}/g, parsedPath.ext); + .replace(/\${file}/g, document.fileName) + .replace(/\${fileNoExtension}/g, fileNoExtension) + .replace(/\${fileBasename}/g, parsedPath.base) + .replace(/\${fileBasenameNoExtension}/g, parsedPath.name) + .replace(/\${fileExtname}/g, parsedPath.ext); } - private getWorkspaceFolder(): string|undefined { + private getWorkspaceFolder(): string | undefined { const editor = vscode.window.activeTextEditor; if (!editor) { vscode.window.showErrorMessage("Unable to get the location of clang-format executable - no active workspace selected"); From 66d4d4b0fedd5eb15692deb957b8139925ce1211 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 16 Jul 2024 23:14:47 -0400 Subject: [PATCH 2/6] extension.ts: Add variable support for .clang-format path Add the ability to use ${workspaceRoot} ${workspaceFolder} ${cwd} ${env.VAR} in the .clang-format path Signed-off-by: Jesse Taube --- src/extension.ts | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index e783d59..88036b0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -159,30 +159,53 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma private getStyle(document: vscode.TextDocument) { let ret = vscode.workspace.getConfiguration('clang-format').get(`language.${this.getLanguage(document)}.style`); - if (ret.trim()) { - return ret.trim(); + + if (ret && ret.trim()) { + ret = this.replaceStyleVariables(ret.trim(), document); + if (ret && ret.trim()) { + return ret.trim(); + } } ret = vscode.workspace.getConfiguration('clang-format').get('style'); if (ret && ret.trim()) { - return ret.trim(); - } else { - return this.defaultConfigure.style; + ret = this.replaceStyleVariables(ret.trim(), document); + if (ret && ret.trim()) { + return ret.trim(); + } } + + return this.replaceStyleVariables(this.defaultConfigure.style, document); } private getFallbackStyle(document: vscode.TextDocument) { let strConf = vscode.workspace.getConfiguration('clang-format').get(`language.${this.getLanguage(document)}.fallbackStyle`); - if (strConf.trim()) { - return strConf; + + if (strConf && strConf.trim()) { + strConf = this.replaceStyleVariables(strConf.trim(), document); + if (strConf && strConf.trim()) { + return strConf.trim(); + } } strConf = vscode.workspace.getConfiguration('clang-format').get('fallbackStyle'); - if (strConf.trim()) { - return strConf; + if (strConf && strConf.trim()) { + strConf = this.replaceStyleVariables(strConf.trim(), document); + if (strConf && strConf.trim()) { + return strConf.trim(); + } } - return this.defaultConfigure.style; + return this.replaceStyleVariables(this.defaultConfigure.style, document); + } + + private replaceStyleVariables(str: string, document: vscode.TextDocument): string { + return str.replace(/\${workspaceRoot}/g, vscode.workspace.rootPath) + .replace(/\${workspaceFolder}/g, this.getWorkspaceFolder()) + .replace(/\${cwd}/g, process.cwd()) + .replace(/\${env\.([^}]+)}/g, (sub: string, envName: string) => { + return process.env[envName]; + }); } private getAssumedFilename(document: vscode.TextDocument) { From 003fe8d905e58893d3367967fcf48a432e214499 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 30 Jul 2024 23:44:13 -0400 Subject: [PATCH 3/6] extension.ts: Code cleanup Fix most of the variables should be const. Remove unused variables. Move many declarations to the top of the function. Signed-off-by: Jesse Taube --- src/extension.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 88036b0..0e2558a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -132,11 +132,11 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma /// Get execute name in clang-format.executable, if not found, use default value /// If configure has changed, it will get the new value private getExecutablePath() { - let platform = getPlatformString(); - let config = vscode.workspace.getConfiguration('clang-format'); + const platform = getPlatformString(); + const config = vscode.workspace.getConfiguration('clang-format'); - let platformExecPath = config.get('executable.' + platform); - let defaultExecPath = config.get('executable'); + const platformExecPath = config.get('executable.' + platform); + const defaultExecPath = config.get('executable'); let execPath = platformExecPath || defaultExecPath; if (!execPath) { @@ -209,12 +209,14 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } private getAssumedFilename(document: vscode.TextDocument) { - let assumedFilename = vscode.workspace.getConfiguration('clang-format').get('assumeFilename'); + const assumedFilename = vscode.workspace.getConfiguration('clang-format').get('assumeFilename'); + const parsedPath = path.parse(document.fileName); + const fileNoExtension = path.join(parsedPath.dir, parsedPath.name); + if (assumedFilename === '') { return document.fileName; } - let parsedPath = path.parse(document.fileName); - let fileNoExtension = path.join(parsedPath.dir, parsedPath.name); + return assumedFilename .replace(/\${file}/g, document.fileName) .replace(/\${fileNoExtension}/g, fileNoExtension) @@ -247,10 +249,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable { return new Promise((resolve, reject) => { - let filename = document.fileName; - - let formatCommandBinPath = getBinPath(this.getExecutablePath()); - let codeContent = document.getText(); + const formatCommandBinPath = getBinPath(this.getExecutablePath()); + const codeContent = document.getText(); let formatArgs = [ '-output-replacements-xml', @@ -322,11 +322,9 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } } -let diagnosticCollection: vscode.DiagnosticCollection; - export function activate(ctx: vscode.ExtensionContext): void { - let formatter = new ClangDocumentFormattingEditProvider(); + const formatter = new ClangDocumentFormattingEditProvider(); let availableLanguages = {}; MODES.forEach((mode) => { From b52829b8ef2550626fdef1bb9616e09f114edb74 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 30 Jul 2024 23:46:58 -0400 Subject: [PATCH 4/6] extension.ts: Add config variable to reduce repetition `vscode.workspace.getConfiguration('clang-format')` is called in almost every function often multiple times. This commit adds `const config = ...` at the top of each function to reduce repetition. Signed-off-by: Jesse Taube --- src/extension.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0e2558a..722a8b8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -158,7 +158,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } private getStyle(document: vscode.TextDocument) { - let ret = vscode.workspace.getConfiguration('clang-format').get(`language.${this.getLanguage(document)}.style`); + const config = vscode.workspace.getConfiguration('clang-format'); + let ret = config.get(`language.${this.getLanguage(document)}.style`); if (ret && ret.trim()) { ret = this.replaceStyleVariables(ret.trim(), document); @@ -167,7 +168,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } } - ret = vscode.workspace.getConfiguration('clang-format').get('style'); + ret = config.get('style'); if (ret && ret.trim()) { ret = this.replaceStyleVariables(ret.trim(), document); if (ret && ret.trim()) { @@ -179,7 +180,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } private getFallbackStyle(document: vscode.TextDocument) { - let strConf = vscode.workspace.getConfiguration('clang-format').get(`language.${this.getLanguage(document)}.fallbackStyle`); + const config = vscode.workspace.getConfiguration('clang-format'); + let strConf = config.get(`language.${this.getLanguage(document)}.fallbackStyle`); if (strConf && strConf.trim()) { strConf = this.replaceStyleVariables(strConf.trim(), document); @@ -188,7 +190,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } } - strConf = vscode.workspace.getConfiguration('clang-format').get('fallbackStyle'); + strConf = config.get('fallbackStyle'); if (strConf && strConf.trim()) { strConf = this.replaceStyleVariables(strConf.trim(), document); if (strConf && strConf.trim()) { @@ -209,7 +211,8 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } private getAssumedFilename(document: vscode.TextDocument) { - const assumedFilename = vscode.workspace.getConfiguration('clang-format').get('assumeFilename'); + const config = vscode.workspace.getConfiguration('clang-format'); + const assumedFilename = config.get('assumeFilename'); const parsedPath = path.parse(document.fileName); const fileNoExtension = path.join(parsedPath.dir, parsedPath.name); From d8cdfd011583136f0ceb2f093c4f676c4622562a Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 30 Jul 2024 23:54:46 -0400 Subject: [PATCH 5/6] extension.ts: Fix error handling If vscode has no workspace the extension will fail. Check if the styles are valid before using them. Signed-off-by: Jesse Taube --- src/extension.ts | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 722a8b8..6fb00ba 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; import cp = require('child_process'); import path = require('path'); +import fs = require('fs'); import { MODES, ALIAS @@ -20,6 +21,14 @@ function getPlatformString() { return 'unknown'; } +function checkFileExists(file: string): boolean { + try { + return fs.statSync(file).isFile(); + } catch (err) { + return false; + } +} + export class ClangDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider, vscode.DocumentRangeFormattingEditProvider { private defaultConfigure = { executable: 'clang-format', @@ -144,9 +153,12 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } // replace placeholders, if present + if (execPath.includes('${workspaceFolder})')) { + execPath = execPath.replace('${workspaceFolder}', this.getWorkspaceFolder()); + } + return execPath .replace(/\${workspaceRoot}/g, vscode.workspace.rootPath) - .replace(/\${workspaceFolder}/g, this.getWorkspaceFolder()) .replace(/\${cwd}/g, process.cwd()) .replace(/\${env\.([^}]+)}/g, (sub: string, envName: string) => { return process.env[envName]; @@ -202,8 +214,11 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma } private replaceStyleVariables(str: string, document: vscode.TextDocument): string { + if (str.includes('${workspaceFolder})')) { + str = str.replace('${workspaceFolder}', this.getWorkspaceFolder()); + } + return str.replace(/\${workspaceRoot}/g, vscode.workspace.rootPath) - .replace(/\${workspaceFolder}/g, this.getWorkspaceFolder()) .replace(/\${cwd}/g, process.cwd()) .replace(/\${env\.([^}]+)}/g, (sub: string, envName: string) => { return process.env[envName]; @@ -216,7 +231,7 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma const parsedPath = path.parse(document.fileName); const fileNoExtension = path.join(parsedPath.dir, parsedPath.name); - if (assumedFilename === '') { + if (assumedFilename === '' || assumedFilename === null) { return document.fileName; } @@ -252,14 +267,28 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable { return new Promise((resolve, reject) => { + const style = this.getStyle(document); + const fallbackStyle = this.getFallbackStyle(document); + const assumedFilename = this.getAssumedFilename(document); const formatCommandBinPath = getBinPath(this.getExecutablePath()); const codeContent = document.getText(); + if (style.substring(0, 5) == "file:" && checkFileExists(style.substring(5)) === false) { + vscode.window.showErrorMessage('The \'' + style + '\' style file is not available. Please check your clang-format.style user setting and ensure it is installed.'); + return resolve(null); + } + + if (fallbackStyle.substring(0, 5) == "file:" && + checkFileExists(fallbackStyle.substring(5)) === false) { + vscode.window.showErrorMessage('The \'' + fallbackStyle + '\' fallback style file is not available. Please check your clang-format.fallbackStyle user setting and ensure it is installed.'); + return resolve(null); + } + let formatArgs = [ '-output-replacements-xml', - `-style=${this.getStyle(document)}`, - `-fallback-style=${this.getFallbackStyle(document)}`, - `-assume-filename=${this.getAssumedFilename(document)}` + `-style=${style}`, + `-fallback-style=${fallbackStyle}`, + `-assume-filename=${assumedFilename}` ]; if (range) { From 35841c394ca25da894453ee3ba1f10598b97a9b8 Mon Sep 17 00:00:00 2001 From: Jesse Taube Date: Tue, 30 Jul 2024 23:55:39 -0400 Subject: [PATCH 6/6] extension.ts: Add additionalArguments setting Some users may want to pass additional arguments to clang-format. This commit adds a setting to allow users to do so. Signed-off-by: Jesse Taube --- package.json | 5 +++++ src/extension.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/package.json b/package.json index 5ce9058..4ba848e 100644 --- a/package.json +++ b/package.json @@ -309,6 +309,11 @@ "type": "string", "default": "", "description": "When reading from stdin, clang-format assumes this filename to look for a style config file (with -style=file) and to determine the language." + }, + "clang-format.additionalArguments": { + "type": "string", + "default": "", + "description": "Additional arguments to pass to clang-format" } } } diff --git a/src/extension.ts b/src/extension.ts index 6fb00ba..42c9c8b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -267,11 +267,13 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma private doFormatDocument(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable { return new Promise((resolve, reject) => { + const config = vscode.workspace.getConfiguration('clang-format'); const style = this.getStyle(document); const fallbackStyle = this.getFallbackStyle(document); const assumedFilename = this.getAssumedFilename(document); const formatCommandBinPath = getBinPath(this.getExecutablePath()); const codeContent = document.getText(); + const additionalArgs = config.get('additionalArguments'); if (style.substring(0, 5) == "file:" && checkFileExists(style.substring(5)) === false) { vscode.window.showErrorMessage('The \'' + style + '\' style file is not available. Please check your clang-format.style user setting and ensure it is installed.'); @@ -291,6 +293,10 @@ export class ClangDocumentFormattingEditProvider implements vscode.DocumentForma `-assume-filename=${assumedFilename}` ]; + if (additionalArgs != '') { + formatArgs.push(additionalArgs); + } + if (range) { let offset = document.offsetAt(range.start); let length = document.offsetAt(range.end) - offset;