Skip to content

Commit 0dfccd8

Browse files
committed
feat: add new feature format for meson 1.9.0
add support for the new format cli argument "--source-file-path", as this helps in error messages
1 parent af5d9fc commit 0dfccd8

File tree

6 files changed

+92
-58
lines changed

6 files changed

+92
-58
lines changed

src/formatters.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,38 @@ import { ToolCheckFunc, Tool, type FormattingProvider } from "./types";
44
import * as muon from "./tools/muon";
55
import * as meson from "./tools/meson";
66

7-
type FormatterFunc = (tool: Tool, root: string, document: vscode.TextDocument) => Promise<vscode.TextEdit[]>;
8-
9-
type FormatterDefinition = {
10-
format: FormatterFunc;
11-
check: ToolCheckFunc;
7+
type FormatterFunc<Options> = (
8+
tool: Tool<Options>,
9+
root: string,
10+
document: vscode.TextDocument,
11+
) => Promise<vscode.TextEdit[]>;
12+
13+
type FormatterDefinition<Options> = {
14+
format: FormatterFunc<Options>;
15+
check: ToolCheckFunc<Options>;
1216
priority: number;
1317
};
1418

1519
//NOTE: the highest priority number means it is tested first, the lowest is tested last
16-
const formatters: Record<FormattingProvider, FormatterDefinition> = {
17-
muon: {
18-
format: muon.format,
19-
check: muon.check,
20-
priority: 0,
21-
},
22-
meson: {
23-
format: meson.format,
24-
check: meson.check,
25-
priority: 1,
26-
},
27-
};
20+
const formatters: Record<FormattingProvider, FormatterDefinition<undefined> | FormatterDefinition<meson.MesonOptions>> =
21+
{
22+
muon: {
23+
format: muon.format,
24+
check: muon.check,
25+
priority: 0,
26+
} as FormatterDefinition<undefined>,
27+
meson: {
28+
format: meson.format,
29+
check: meson.check,
30+
priority: 1,
31+
} as FormatterDefinition<meson.MesonOptions>,
32+
};
2833

2934
type FormatterError = { provider: FormattingProvider; error: string };
3035

3136
type BestTool = {
3237
provider: FormattingProvider;
33-
tool: Tool;
38+
tool: Tool<undefined> | Tool<meson.MesonOptions>;
3439
};
3540

3641
type BestFormatterResult = BestTool | FormatterError[];
@@ -44,7 +49,7 @@ async function getBestAvailableFormatter(provider: FormattingProvider | "auto"):
4449
return [{ provider, error: checkResult.error }];
4550
}
4651

47-
return { provider, tool: checkResult.tool };
52+
return { provider, tool: checkResult.data };
4853
}
4954

5055
// sort the available providers by priority
@@ -65,7 +70,7 @@ async function getBestAvailableFormatter(provider: FormattingProvider | "auto"):
6570
continue;
6671
}
6772

68-
return { provider: providerName, tool: checkResult.tool };
73+
return { provider: providerName, tool: checkResult.data };
6974
}
7075

7176
return errors;
@@ -105,7 +110,7 @@ async function reloadFormatters(sourceRoot: string, context: vscode.ExtensionCon
105110

106111
const sub = vscode.languages.registerDocumentFormattingEditProvider("meson", {
107112
async provideDocumentFormattingEdits(document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
108-
return await props.format(tool, sourceRoot, document);
113+
return await (props.format as FormatterFunc<unknown>)(tool as Tool<unknown>, sourceRoot, document);
109114
},
110115
});
111116

src/linters.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import { extensionConfiguration, getOutputChannel } from "./utils";
33
import { ExtensionConfiguration, LinterConfiguration, ToolCheckFunc, Tool } from "./types";
44
import * as muon from "./tools/muon";
55

6-
type LinterFunc = (tool: Tool, sourceRoot: string, document: vscode.TextDocument) => Promise<vscode.Diagnostic[]>;
6+
type LinterFunc = (
7+
tool: Tool<undefined>,
8+
sourceRoot: string,
9+
document: vscode.TextDocument,
10+
) => Promise<vscode.Diagnostic[]>;
711

812
type LinterDefinition = {
913
lint: LinterFunc;
10-
check: ToolCheckFunc;
14+
check: ToolCheckFunc<undefined>;
1115
};
1216

1317
const linters: Record<keyof ExtensionConfiguration["linter"], LinterDefinition> = {
@@ -45,7 +49,7 @@ async function reloadLinters(
4549
continue;
4650
}
4751

48-
const linter = async (document: vscode.TextDocument) => await props.lint(checkResult.tool, sourceRoot, document);
52+
const linter = async (document: vscode.TextDocument) => await props.lint(checkResult.data, sourceRoot, document);
4953
enabledLinters.push(linter);
5054
}
5155

src/tools/meson.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import * as vscode from "vscode";
22
import { execFeed, extensionConfiguration, getOutputChannel, mesonProgram } from "../utils";
3-
import { Tool, ToolCheckResult } from "../types";
3+
import { Tool, CheckResult } from "../types";
44
import { getMesonVersion } from "../introspection";
55
import { Version } from "../version";
66

7-
export async function format(meson: Tool, root: string, document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
7+
export interface MesonOptions {
8+
supportsFileNameArgument: boolean;
9+
}
10+
11+
export type MesonTool = Tool<MesonOptions>;
12+
13+
export async function format(
14+
meson: MesonTool,
15+
root: string,
16+
document: vscode.TextDocument,
17+
): Promise<vscode.TextEdit[]> {
818
const originalDocumentText = document.getText();
919

1020
let args = ["format"];
@@ -15,6 +25,10 @@ export async function format(meson: Tool, root: string, document: vscode.TextDoc
1525
}
1626
args.push("-");
1727

28+
if (meson.options.supportsFileNameArgument) {
29+
args.push("--source-file-path", document.fileName);
30+
}
31+
1832
const { stdout, stderr, error } = await execFeed(meson.path, args, { cwd: root }, originalDocumentText);
1933
if (error) {
2034
//TODO: file a bug report, meson prints some errors on stdout :(
@@ -35,8 +49,9 @@ export async function format(meson: Tool, root: string, document: vscode.TextDoc
3549

3650
const formattingSupportedSinceVersion = new Version([1, 5, 0]);
3751
const formattingWithStdinSupportedSinceVersion = new Version([1, 7, 0]);
52+
const formattingWithFileNameArgumentSinceVersion = new Version([1, 9, 0]);
3853

39-
export async function check(): Promise<ToolCheckResult> {
54+
export async function check(): Promise<CheckResult<MesonTool>> {
4055
const meson_path = mesonProgram();
4156

4257
let mesonVersion;
@@ -45,23 +60,29 @@ export async function check(): Promise<ToolCheckResult> {
4560
} catch (e) {
4661
const error = e as Error;
4762
console.log(error);
48-
return ToolCheckResult.newError(error.message);
63+
return CheckResult.newError<MesonTool>(error.message);
4964
}
5065

5166
// meson format was introduced in 1.5.0
5267
// see https://mesonbuild.com/Commands.html#format
5368
if (mesonVersion.compareWithOther(formattingSupportedSinceVersion) < 0) {
54-
ToolCheckResult.newError(
69+
CheckResult.newError<MesonTool>(
5570
`Meson supports formatting only since version ${formattingSupportedSinceVersion}, but you have version ${mesonVersion}`,
5671
);
5772
}
5873

5974
// using "-" as stdin is only supported since 1.7.0 (see https://github.com/mesonbuild/meson/pull/13793)
6075
if (mesonVersion.compareWithOther(formattingWithStdinSupportedSinceVersion) < 0) {
61-
return ToolCheckResult.newError(
76+
return CheckResult.newError<MesonTool>(
6277
`Meson supports formatting from stdin only since version ${formattingWithStdinSupportedSinceVersion}, but you have version ${mesonVersion}`,
6378
);
6479
}
6580

66-
return ToolCheckResult.newTool({ path: meson_path, version: mesonVersion });
81+
const supportsFileNameArgument = mesonVersion.compareWithOther(formattingWithFileNameArgumentSinceVersion) >= 0;
82+
83+
const options: MesonOptions = {
84+
supportsFileNameArgument,
85+
};
86+
87+
return CheckResult.newData<MesonTool>({ path: meson_path, version: mesonVersion, options });
6788
}

src/tools/muon.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as vscode from "vscode";
22
import { ExecResult, exec, execFeed, extensionConfiguration, getOutputChannel } from "../utils";
3-
import { Tool, ToolCheckResult } from "../types";
3+
import { Tool, CheckResult } from "../types";
44
import { Version, type VersionArray } from "../version";
55

6-
export async function lint(muon: Tool, root: string, document: vscode.TextDocument): Promise<vscode.Diagnostic[]> {
6+
export type MuonTool = Tool<undefined>;
7+
8+
export async function lint(muon: MuonTool, root: string, document: vscode.TextDocument): Promise<vscode.Diagnostic[]> {
79
const { stdout, stderr } = await execFeed(
810
muon.path,
911
["analyze", "-l", "-O", document.uri.fsPath],
@@ -51,7 +53,7 @@ export async function lint(muon: Tool, root: string, document: vscode.TextDocume
5153
return diagnostics;
5254
}
5355

54-
export async function format(muon: Tool, root: string, document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
56+
export async function format(muon: MuonTool, root: string, document: vscode.TextDocument): Promise<vscode.TextEdit[]> {
5557
const originalDocumentText = document.getText();
5658

5759
let args = ["fmt"];
@@ -82,7 +84,7 @@ export async function format(muon: Tool, root: string, document: vscode.TextDocu
8284
return [new vscode.TextEdit(documentRange, stdout)];
8385
}
8486

85-
export async function check(): Promise<ToolCheckResult> {
87+
export async function check(): Promise<CheckResult<MuonTool>> {
8688
const muon_path = extensionConfiguration("muonPath");
8789
let stdout: string, stderr: string;
8890

@@ -91,12 +93,12 @@ export async function check(): Promise<ToolCheckResult> {
9193
} catch (e) {
9294
const { error, stdout, stderr } = e as ExecResult;
9395
console.log(error);
94-
return ToolCheckResult.newError(error!.message);
96+
return CheckResult.newError<MuonTool>(error!.message);
9597
}
9698

9799
const line1 = stdout.split("\n")[0].split(" ");
98100
if (line1.length !== 2) {
99-
return ToolCheckResult.newError(`Invalid version string: ${line1}`);
101+
return CheckResult.newError<MuonTool>(`Invalid version string: ${line1}`);
100102
}
101103

102104
const ver = line1[1]
@@ -110,5 +112,5 @@ export async function check(): Promise<ToolCheckResult> {
110112
return Number.parseInt(s);
111113
}) as VersionArray;
112114

113-
return ToolCheckResult.newTool({ path: muon_path, version: new Version(ver) });
115+
return CheckResult.newData<MuonTool>({ path: muon_path, version: new Version(ver) });
114116
}

src/types.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,37 @@ import type { Version } from "./version";
33

44
type Dict<T> = { [x: string]: T };
55

6-
export type Tool = { path: string; version: Version };
6+
export type Tool<Options> = { path: string; version: Version } & (Options extends undefined
7+
? {}
8+
: { options: Options });
79

8-
export type ToolCheckSuccessResult = {
9-
tool: Tool;
10+
export type CheckSuccessResult<Data> = {
11+
data: Data;
1012
error?: undefined;
1113
};
12-
export type ToolCheckErrorResult = {
13-
tool?: undefined;
14+
export type CheckErrorResult = {
15+
data?: undefined;
1416
error: string;
1517
};
1618

17-
type ResultImpl = ToolCheckSuccessResult | ToolCheckErrorResult;
19+
type ResultImpl<Data> = CheckSuccessResult<Data> | CheckErrorResult;
1820

19-
export class ToolCheckResult {
20-
private readonly result: ResultImpl;
21+
export class CheckResult<Data> {
22+
private readonly result: ResultImpl<Data>;
2123

22-
private constructor(result: ResultImpl) {
24+
private constructor(result: ResultImpl<Data>) {
2325
this.result = result;
2426
}
2527

26-
static newError(error: string) {
27-
return new ToolCheckResult({ error });
28+
static newError<D>(error: string): CheckResult<D> {
29+
return new CheckResult<D>({ error });
2830
}
2931

30-
static newTool(tool: Tool) {
31-
return new ToolCheckResult({ tool });
32+
static newData<D>(data: D): CheckResult<D> {
33+
return new CheckResult<D>({ data });
3234
}
3335

34-
private hasErrorImpl(result: ResultImpl): result is ToolCheckErrorResult {
36+
private hasErrorImpl(result: ResultImpl<Data>): result is CheckErrorResult {
3537
return !!result.error;
3638
}
3739

@@ -46,15 +48,15 @@ export class ToolCheckResult {
4648
return this.result.error;
4749
}
4850

49-
get tool(): Tool {
51+
get data(): Data {
5052
if (this.hasErrorImpl(this.result)) {
51-
throw new Error("Wrong invocation of getter for 'tool', check the state first");
53+
throw new Error("Wrong invocation of getter for 'data', check the state first");
5254
}
53-
return this.result.tool;
55+
return this.result.data;
5456
}
5557
}
5658

57-
export type ToolCheckFunc = () => Promise<ToolCheckResult>;
59+
export type ToolCheckFunc<D> = () => Promise<CheckResult<Tool<D>>>;
5860

5961
export type LinterConfiguration = {
6062
enabled: boolean;

src/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
Target,
1111
SettingsKey,
1212
ModifiableExtension,
13-
type ToolCheckResult,
14-
type ToolCheckErrorResult,
13+
type CheckResult,
14+
type CheckErrorResult,
1515
} from "./types";
1616
import { getMesonBuildOptions } from "./introspection";
1717
import { extensionPath, workspaceState } from "./extension";

0 commit comments

Comments
 (0)