From 3b762c8ce709323814f6749e14f1f254e73bae79 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 31 Jan 2025 20:45:04 +0100 Subject: [PATCH] feat: add more sophisticated formatter selection - allow the formatting provider to be set as auto, this means, select the best available one - add internal priority for formatting providers --- CHANGELOG.md | 2 ++ package.json | 9 +++--- src/formatters.ts | 75 +++++++++++++++++++++++++++++++++++++++++++---- src/types.ts | 2 +- 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8625a..03d7fd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## next - Add support for meson as formatter +- Use meson as default formatter +- Add auto select capability for formatting provider preference ## 1.26.0 diff --git a/package.json b/package.json index 1aa5e4b..91d4c9a 100644 --- a/package.json +++ b/package.json @@ -192,12 +192,13 @@ }, "mesonbuild.formatting.provider": { "type": "string", - "default": "muon", + "default": "auto", "enum": [ - "muon", - "meson" + "auto", + "meson", + "muon" ], - "description": "Select which formatting provider to use" + "description": "Select which formatting provider to use. If not set, the best available one is picked" }, "mesonbuild.formatting.muonConfig": { "type": "string", diff --git a/src/formatters.ts b/src/formatters.ts index a707600..f508c2e 100644 --- a/src/formatters.ts +++ b/src/formatters.ts @@ -9,19 +9,71 @@ type FormatterFunc = (tool: Tool, root: string, document: vscode.TextDocument) = type FormatterDefinition = { format: FormatterFunc; check: ToolCheckFunc; + priority: number; }; const formatters: Record = { muon: { format: muon.format, check: muon.check, + priority: 1, // higher means less priority }, meson: { format: meson.format, check: meson.check, + priority: 0, }, }; +type FormatterError = { provider: FormattingProvider; error: string }; + +type BestTool = { + provider: FormattingProvider; + tool: Tool; +}; + +type BestFormatterResult = BestTool | FormatterError[]; + +async function getBestAvailableFormatter(provider: FormattingProvider | "auto"): Promise { + 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[provider1].priority - formatters[provider2].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 { let disposables: vscode.Disposable[] = []; @@ -29,19 +81,30 @@ async function reloadFormatters(sourceRoot: string, context: vscode.ExtensionCon return disposables; } - const name = extensionConfiguration("formatting").provider; - const props = formatters[name]; + const providerName = extensionConfiguration("formatting").provider; + + const bestFormatter = await getBestAvailableFormatter(providerName); - const checkResult = await props.check(); - if (checkResult.isError()) { - getOutputChannel().appendLine(`Failed to enable formatter ${name}: ${checkResult.error}`); + 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 { - return await props.format(checkResult.tool, sourceRoot, document); + return await props.format(tool, sourceRoot, document); }, }); diff --git a/src/types.ts b/src/types.ts index 5a328d4..299dbe6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,7 +82,7 @@ export interface ExtensionConfiguration { }; formatting: { enabled: boolean; - provider: FormattingProvider; + provider: FormattingProvider | "auto"; muonConfig: string | null; mesonConfig: string | null; };