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..a21c940 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 set to 'auto', the best available one is picked" }, "mesonbuild.formatting.muonConfig": { "type": "string", diff --git a/src/formatters.ts b/src/formatters.ts index a707600..1d5c63e 100644 --- a/src/formatters.ts +++ b/src/formatters.ts @@ -9,19 +9,72 @@ type FormatterFunc = (tool: Tool, root: string, document: vscode.TextDocument) = 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 = { 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 { + 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 { let disposables: vscode.Disposable[] = []; @@ -29,19 +82,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; };