-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathformatters.ts
130 lines (100 loc) · 3.87 KB
/
formatters.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
import * as vscode from "vscode";
import { extensionConfiguration, getOutputChannel } from "./utils";
import { ToolCheckFunc, Tool, type FormattingProvider } from "./types";
import * as muon from "./tools/muon";
import * as meson from "./tools/meson";
type FormatterFunc = (tool: Tool, root: string, document: vscode.TextDocument) => Promise<vscode.TextEdit[]>;
type FormatterDefinition = {
format: FormatterFunc;
check: ToolCheckFunc;
priority: number;
};
//NOTE: the highest priority number means it is tested first, the lowest is tested last
const formatters: Record<FormattingProvider, FormatterDefinition> = {
muon: {
format: muon.format,
check: muon.check,
priority: 0,
},
meson: {
format: meson.format,
check: meson.check,
priority: 1,
},
};
type FormatterError = { provider: FormattingProvider; error: string };
type BestTool = {
provider: FormattingProvider;
tool: Tool;
};
type BestFormatterResult = BestTool | FormatterError[];
async function getBestAvailableFormatter(provider: FormattingProvider | "auto"): Promise<BestFormatterResult> {
if (provider !== "auto") {
const props = formatters[provider];
const checkResult = await props.check();
if (checkResult.isError()) {
return [{ provider, error: checkResult.error }];
}
return { provider, tool: checkResult.tool };
}
// sort the available providers by priority
const providerPriority: FormattingProvider[] = (Object.keys(formatters) as FormattingProvider[]).sort(
(provider1: FormattingProvider, provider2: FormattingProvider) => {
return formatters[provider2].priority - formatters[provider1].priority;
},
);
const errors: FormatterError[] = [];
for (const providerName of providerPriority) {
const props = formatters[providerName];
const checkResult = await props.check();
if (checkResult.isError()) {
errors.push({ provider: providerName, error: checkResult.error });
continue;
}
return { provider: providerName, tool: checkResult.tool };
}
return errors;
}
function isFormaterErrors(input: BestFormatterResult): input is FormatterError[] {
return Array.isArray(input);
}
async function reloadFormatters(sourceRoot: string, context: vscode.ExtensionContext): Promise<vscode.Disposable[]> {
let disposables: vscode.Disposable[] = [];
if (!extensionConfiguration("formatting").enabled) {
return disposables;
}
const providerName = extensionConfiguration("formatting").provider;
const bestFormatter = await getBestAvailableFormatter(providerName);
if (isFormaterErrors(bestFormatter)) {
getOutputChannel().appendLine(`Failed to find an available formatter: The user preference was '${providerName}'`);
for (const { provider, error } of bestFormatter) {
getOutputChannel().appendLine(`Failed to enable formatter ${provider}: ${error}`);
}
getOutputChannel().show(true);
return disposables;
}
const { tool, provider } = bestFormatter;
getOutputChannel().appendLine(
`The best formatter was determined to be ${provider}: The user preference was '${providerName}'`,
);
const props = formatters[provider];
const sub = vscode.languages.registerDocumentFormattingEditProvider("meson", {
async provideDocumentFormattingEdits(document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
return await props.format(tool, sourceRoot, document);
},
});
context.subscriptions.push(sub);
disposables.push(sub);
return disposables;
}
export async function activateFormatters(sourceRoot: string, context: vscode.ExtensionContext) {
let subscriptions: vscode.Disposable[] = await reloadFormatters(sourceRoot, context);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(async () => {
for (let handler of subscriptions) {
handler.dispose();
}
subscriptions = await reloadFormatters(sourceRoot, context);
}),
);
}