1- import * as os from 'node:os' ;
21import { EnvVariablesServer } from '@theia/core/lib/common/env-variables' ;
32import { MaybePromise } from '@theia/core/lib/common/types' ;
43import { FileUri } from '@theia/core/lib/node/file-uri' ;
@@ -15,7 +14,7 @@ export class ClangFormatter implements Formatter {
1514 private readonly configService : ConfigService ;
1615
1716 @inject ( EnvVariablesServer )
18- private readonly envVariableServer : EnvVariablesServer ;
17+ private readonly envVariablesServer : EnvVariablesServer ;
1918
2019 async format ( {
2120 content,
@@ -26,26 +25,19 @@ export class ClangFormatter implements Formatter {
2625 formatterConfigFolderUris : string [ ] ;
2726 options ?: FormatterOptions ;
2827 } ) : Promise < string > {
29- const [ execPath , style ] = await Promise . all ( [
30- this . execPath ( ) ,
31- this . style ( formatterConfigFolderUris , options ) ,
32- ] ) ;
28+ const execPath = this . execPath ( ) ;
29+ const args = await this . styleArgs ( formatterConfigFolderUris , options ) ;
3330 const formatted = await spawnCommand (
34- `" ${ execPath } "` ,
35- [ style ] ,
31+ execPath ,
32+ args ,
3633 console . error ,
3734 content
3835 ) ;
3936 return formatted ;
4037 }
4138
42- private _execPath : string | undefined ;
43- private async execPath ( ) : Promise < string > {
44- if ( this . _execPath ) {
45- return this . _execPath ;
46- }
47- this . _execPath = await getExecPath ( 'clang-format' ) ;
48- return this . _execPath ;
39+ private execPath ( ) : string {
40+ return getExecPath ( 'clang-format' ) ;
4941 }
5042
5143 /**
@@ -60,10 +52,10 @@ export class ClangFormatter implements Formatter {
6052 *
6153 * See: https://github.com/arduino/arduino-ide/issues/566
6254 */
63- private async style (
55+ private async styleArgs (
6456 formatterConfigFolderUris : string [ ] ,
6557 options ?: FormatterOptions
66- ) : Promise < string > {
58+ ) : Promise < string [ ] > {
6759 const clangFormatPaths = await Promise . all ( [
6860 ...formatterConfigFolderUris . map ( ( uri ) => this . clangConfigPath ( uri ) ) ,
6961 this . clangConfigPath ( this . configDirPath ( ) ) ,
@@ -72,11 +64,11 @@ export class ClangFormatter implements Formatter {
7264 const first = clangFormatPaths . filter ( Boolean ) . shift ( ) ;
7365 if ( first ) {
7466 console . debug (
75- `Using ${ ClangFormatFile } style configuration from '${ first } '.`
67+ `Using ${ clangFormatFilename } style configuration from '${ first } '.`
7668 ) ;
77- return ` -style= file:" ${ first } "` ;
69+ return [ ' -style' , ` file:${ first } ` ] ;
7870 }
79- return ` -style=" ${ style ( toClangOptions ( options ) ) } "` ;
71+ return [ ' -style' , style ( toClangOptions ( options ) ) ] ;
8072 }
8173
8274 private async dataDirPath ( ) : Promise < string | undefined > {
@@ -88,7 +80,7 @@ export class ClangFormatter implements Formatter {
8880 }
8981
9082 private async configDirPath ( ) : Promise < string > {
91- const configDirUri = await this . envVariableServer . getConfigDirUri ( ) ;
83+ const configDirUri = await this . envVariablesServer . getConfigDirUri ( ) ;
9284 return FileUri . fsPath ( configDirUri ) ;
9385 }
9486
@@ -100,7 +92,7 @@ export class ClangFormatter implements Formatter {
10092 return undefined ;
10193 }
10294 const folderPath = FileUri . fsPath ( uri ) ;
103- const clangFormatPath = join ( folderPath , ClangFormatFile ) ;
95+ const clangFormatPath = join ( folderPath , clangFormatFilename ) ;
10496 try {
10597 await fs . access ( clangFormatPath , constants . R_OK ) ;
10698 return clangFormatPath ;
@@ -115,7 +107,7 @@ interface ClangFormatOptions {
115107 readonly TabWidth : number ;
116108}
117109
118- const ClangFormatFile = '.clang-format' ;
110+ export const clangFormatFilename = '.clang-format' ;
119111
120112function toClangOptions (
121113 options ?: FormatterOptions | undefined
@@ -129,30 +121,14 @@ function toClangOptions(
129121 return { UseTab : 'Never' , TabWidth : 2 } ;
130122}
131123
132- export function style ( { TabWidth, UseTab } : ClangFormatOptions ) : string {
133- let styleArgument = JSON . stringify ( styleJson ( { TabWidth, UseTab } ) ) . replace (
134- / [ \\ " ] / g,
135- '\\$&'
136- ) ;
137- if ( os . platform ( ) === 'win32' ) {
138- // Windows command interpreter does not use backslash escapes. This causes the argument to have alternate quoted and
139- // unquoted sections.
140- // Special characters in the unquoted sections must be caret escaped.
141- const styleArgumentSplit = styleArgument . split ( '"' ) ;
142- for ( let i = 1 ; i < styleArgumentSplit . length ; i += 2 ) {
143- styleArgumentSplit [ i ] = styleArgumentSplit [ i ] . replace ( / [ < > ^ | ] / g, '^$&' ) ;
144- }
145-
146- styleArgument = styleArgumentSplit . join ( '"' ) ;
147- }
148-
149- return styleArgument ;
124+ function style ( { TabWidth, UseTab } : ClangFormatOptions ) : string {
125+ return stringifyConfiguration ( styleJson ( { TabWidth, UseTab } ) ) ;
150126}
151127
152128function styleJson ( {
153129 TabWidth,
154130 UseTab,
155- } : ClangFormatOptions ) : Record < string , unknown > {
131+ } : ClangFormatOptions ) : ClangConfiguration {
156132 // Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
157133 const defaultConfig = require ( '../../src/node/default-formatter-config.json' ) ;
158134 return {
@@ -161,3 +137,26 @@ function styleJson({
161137 UseTab,
162138 } ;
163139}
140+
141+ export type ClangStyleValue =
142+ | string
143+ | number
144+ | boolean
145+ | ClangStyleValue [ ]
146+ | { [ key : string ] : ClangStyleValue } ;
147+ export type ClangConfiguration = Record < string , ClangStyleValue > ;
148+
149+ export function stringifyConfiguration (
150+ value : ClangStyleValue | ClangConfiguration
151+ ) : string {
152+ if ( Array . isArray ( value ) ) {
153+ return `[${ value . map ( ( item ) => stringifyConfiguration ( item ) ) . join ( ', ' ) } ]` ;
154+ } else if ( typeof value === 'object' ) {
155+ return `{${ Object . entries ( value )
156+ . map ( ( [ key , v ] ) => `${ key } : ${ stringifyConfiguration ( v ) } ` )
157+ . join ( ', ' ) } }`;
158+ } else if ( typeof value === 'string' ) {
159+ return `'${ value } '` ;
160+ }
161+ return String ( value ) ;
162+ }
0 commit comments