-
-
Notifications
You must be signed in to change notification settings - Fork 435
/
Copy pathclang-formatter.ts
140 lines (127 loc) · 4.04 KB
/
clang-formatter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { MaybePromise } from '@theia/core/lib/common/types';
import { FileUri } from '@theia/core/lib/common/file-uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import { constants, promises as fs } from 'node:fs';
import { join } from 'node:path';
import { ConfigService } from '../common/protocol';
import { Formatter, FormatterOptions } from '../common/protocol/formatter';
import { spawnCommand } from './exec-util';
import { clangFormatPath } from './resources';
import defaultClangFormat from './default-formatter-config.json';
@injectable()
export class ClangFormatter implements Formatter {
@inject(ConfigService)
private readonly configService: ConfigService;
@inject(EnvVariablesServer)
private readonly envVariablesServer: EnvVariablesServer;
async format({
content,
formatterConfigFolderUris,
options,
}: {
content: string;
formatterConfigFolderUris: string[];
options?: FormatterOptions;
}): Promise<string> {
const execPath = this.execPath();
const args = await this.styleArgs(formatterConfigFolderUris, options);
const formatted = await spawnCommand(
execPath,
args,
console.error,
content
);
return formatted;
}
private execPath(): string {
return clangFormatPath;
}
/**
* Calculates the `-style` flag for the formatter. Uses a `.clang-format` file if exists.
* Otherwise, falls back to the default config.
*
* Style precedence:
* 1. in the sketch folder,
* 1. `~/.arduinoIDE/.clang-format`,
* 1. `directories#data/.clang-format`, and
* 1. default style flag as a string.
*
* See: https://github.com/arduino/arduino-ide/issues/566
*/
private async styleArgs(
formatterConfigFolderUris: string[],
options?: FormatterOptions
): Promise<string[]> {
const clangFormatPaths = await Promise.all([
...formatterConfigFolderUris.map((uri) => this.clangConfigPath(uri)),
this.clangConfigPath(this.configDirPath()),
this.clangConfigPath(this.dataDirPath()),
]);
const first = clangFormatPaths.filter(Boolean).shift();
if (first) {
console.debug(
`Using ${clangFormatFilename} style configuration from '${first}'.`
);
return ['-style', `file:${first}`];
}
return ['-style', style(toClangOptions(options))];
}
private async dataDirPath(): Promise<string | undefined> {
const { config } = await this.configService.getConfiguration();
if (!config?.dataDirUri) {
return undefined;
}
return FileUri.fsPath(config.dataDirUri);
}
private async configDirPath(): Promise<string> {
const configDirUri = await this.envVariablesServer.getConfigDirUri();
return FileUri.fsPath(configDirUri);
}
private async clangConfigPath(
folderUri: MaybePromise<string | undefined>
): Promise<string | undefined> {
const uri = await folderUri;
if (!uri) {
return undefined;
}
const folderPath = FileUri.fsPath(uri);
const clangFormatPath = join(folderPath, clangFormatFilename);
try {
await fs.access(clangFormatPath, constants.R_OK);
return clangFormatPath;
} catch {
return undefined;
}
}
}
interface ClangFormatOptions {
readonly UseTab: 'Never' | 'ForIndentation';
readonly TabWidth: number;
}
export const clangFormatFilename = '.clang-format';
function toClangOptions(
options?: FormatterOptions | undefined
): ClangFormatOptions {
if (!!options) {
return {
UseTab: options.insertSpaces ? 'Never' : 'ForIndentation',
TabWidth: options.tabSize,
};
}
return { UseTab: 'Never', TabWidth: 2 };
}
function style({ TabWidth, UseTab }: ClangFormatOptions): string {
return JSON.stringify(styleJson({ TabWidth, UseTab }));
}
function styleJson({
TabWidth,
UseTab,
}: ClangFormatOptions): Record<string, unknown> {
return {
// Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
...defaultClangFormat,
TabWidth,
UseTab,
};
}